Safe Array Sample Code on GitHub

I uploaded some sample code on GitHub, that shows how to create safe arrays in C++ and consume them in a C# application.

This project contains a C++ DLL (with a C interface) that exports two functions to produce safe arrays containing bytes and strings.

Then, there’s a WinForms C# application that consumes these safe arrays using proper PInvoke declarations, and shows their content on screen.

 

Marshal STL string vectors Using Safe Arrays

Suppose you have a vector<string> in some cross-platform C++ code, and you want to marshal it across module or language boundaries on the Windows platform: Using a safe array is a valid option.

So, how can you achieve this goal?

Well, as it’s common in programming, you have to combine together some building blocks, and you get the solution to your problem.

A safe array can contain many different types, but the “natural” type for a Unicode string is BSTR. A BSTR is basically a length-prefixed Unicode string encoded using UTF-16.

ATL offers a convenient helper class to simplify safe array programming in C++: CComSafeArray. The MSDN Magazine article “Simplify Safe Array Programming in C++ with CComSafeArray” discusses with concrete sample code how to use this class. In particular, the paragraph “Producing a Safe Array of Strings” is the section of interest here.

So, this was the first building block. Now, let’s discuss the second.

You have a vector<string> as input. An important question to ask is what kind of encoding is used for the strings stored in the vector. It’s very common to store Unicode strings in std::string using the UTF-8 encoding. So, there’s an encoding impedance here: The input strings stored in the std::vector use UTF-8; but the output strings that will be stored as BSTR in the safe array use UTF-16. Ok, not a big problem: You just have to convert from UTF-8 to UTF-16. This is the other building block to solve the initial problem, and it’s discussed in the MSDN Magazine article “Unicode Encoding Conversions with STL Strings and Win32 APIs”.

So, to wrap up: You can go from a vector<string> to a safe array of BSTR strings following this path:

  1. Create a CComSafeArray<BSTR> of the same size of the input std::vector
  2. For each string in the input vector<string>, convert the UTF-8-encoded string to the corresponding UTF-16 wstring
  3. Create a CComBSTR from the previous wstring
  4. Invoke CComSafeArray::SetAt() to copy the CComBSTR into the safe array

The steps #1, #3, and #4 are discussed in the CComSafeArray MSDN article; the step #2 is discussed in the Unicode encoding conversion MSDN article.

A Subtle Bug with PInvoke and Safe Arrays Storing Variant Bytes

When exchanging array data between different module boundaries using safe arrays, I tend to prefer (and suggest) safe arrays of direct types, like BYTEs, or BSTR strings, instead of safe array storing variants (that in turn contain BYTEs, or BSTRs, etc.).

However, there are some scripting clients that only understand safe arrays storing variants. So, if you want to support such clients, you have to pack the original array data items into variants, and build a safe array of variants.

If you have a COM interface method or C-interface function that produces a safe array of variants that contain BSTR strings, and you want to consume this array in C# code,  the following PInvoke seems to work fine:

[DllImport("NativeDll.dll", PreserveSig = false)]
pubic static extern void BuildVariantStringArray(
  [Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]
  out string[] result);

So, if you have a safe array of variants that contain BYTEs, you may deduce that such a PInvoke declaration would work fine as well:

[DllImport("NativeDll.dll", PreserveSig = false)]
pubic static extern void BuildVariantByteArray(
  [Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]
  out byte[] result);

I’ve just changed “string[]” to “byte[]” in the declaration of the “result” out parameter.

Unfortunately, this doesn’t work. What you get as a result in the output byte array is garbage.

The fix in this case of safe array of variant bytes is to use an object[] array in C#, which directly maps the original safe array of variants (as variants are marshaled to objects in C#):

[DllImport("NativeDll.dll", PreserveSig = false)]
pubic static extern void BuildVariantByteArray(
  [Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]
  out object[] result);

And then manually convert from the returned object[] array to a byte[] array, for example using the C# Array.CopyTo method; e.g.:

// Get a safe array of variants (that contain bytes).
object[] data;
BuildVariantByteArray(out data);

// "Render" (copy) the previous object array 
// to a new byte array.
byte[] byteData = new byte[data.Length];
data.CopyTo(byteData, 0);

// Use byteData...

A variant is marshaled using object in C#. So a safe array of variants is marshaled using an object array in C#. In the case of safe arrays of variant bytes, the returned bytes are boxed in objects. Using Array.CopyTo, these bytes get unboxed and stuffed into a byte array.

The additional CopyTo step doesn’t seem necessary in the safe array of string variants, probably because strings are objects in C#.

Still, I think this aspect of the .NET/C# marshaler should be fixed, and if a PInvoke declaration clearly states byte[] on the C# side, the marshaler should automatically unbox the bytes from the safe array of variants.

 

MSDN Magazine Article: Simplify Safe Array Programming in C++

The March 2017 issue of MSDN Magazine contains a feature article of mine on simplifying safe array programming in C++ with the help of the ATL’s CComSafeArray class template.

There is also an accompanying web-only side bar introducing the SAFEARRAY C data structure and some of the basic operations available for it via Win32 API calls, although for C++ code I encourage the use of a convenient higher-level C++ object-oriented wrapper like ATL::CComSafeArray.

Safe arrays are useful for example when you have a COM component and you want to exchange array data between the component and its clients (that can be potentially written in languages even different than C++, e.g. C#, or scripting languages).

I wish I could have had such a resource available when I did some safe array programming in C++.

Some of the insights and experience I developed in that regard are distilled in the aforementioned article.

I hope it may be helpful to someone.

Check it out here!

 

Simplifying SAFEARRAY programming with CComSafeArray

EDIT 2017-03-01: Please read my MSDN Magazine article:

Simplify Safe Array Programming in C++ with CComSafeArray

for a detailed discussion.

 

Original blog post follows:

There are some questions on the MSDN forums about passing arrays between C++ and C#. This argument is rich and there are several options. There was a recent thread on this topic on the Visual C++ MFC and ATL MSDN Forum. As requested by the OP, a simple C++ code using raw C arrays with corresponding C# P/Invoke signature was showed.

Another option consists of using SAFEARRAY’s. While programming SAFEARRAY’s using raw C functions (e.g. SafeArrayCreate, SafeArrayLock, etc.) can lead to some boilerplate code, ATL offers a convenient CComSafeArray helper class that makes the life of SAFEARRAY programmers much easier.

CComSafeArray is a C++ template class; suppose that a SAFEARRAY of LONG’s is requested: it can be simply created with code like this:

       CComSafeArray<LONG> saData(count);

 

Thanks to CComSafeArray, SAFEARRAY items can be accessed using classic operator[], like this:

        for (int i = 0; i < count; i++)

            saData[i] = (i+1)*10;

 

And to transfer the created SAFEARRAY to the caller (assuming there is a SAFEARRAY ** ppsaData output parameter), a simple code like this works fine:

 

      *ppsaData = saData.Detach();

 

It is worth noting that CComSafeArray can throw C++ exceptions (instances of CAtlException class) on error. So, if the method in which the CComSafeArray is used returns an HRESULT (as common to COM methods), it is safe to guard the code using CComSafeArray in a try/catch block, to convert CAtlException instances back to HRESULT error codes, that can cross COM method boundaries, e.g. :

    try

    {

        // Create a safe array of LONG’s

        CComSafeArray<LONG> saData(count);

 

        // Fill with some data…

        for (int i = 0; i < count; i++)

            saData[i] = (i+1)*10;

       

        // Copy to output parameter

        *ppsaData = saData.Detach();

    }

    catch (const CAtlException & e)

    {

        // Trap errors signaled as C++ ATL exceptions

        // and return the corresponding HRESULT

 

        return e// implicit cast to HRESULT

    }

 

    // All right

    return S_OK;

 

A sample solution illustrating these concepts is attached to this blog post.