FAQ: C# calling C++ code with callbacks with unmanaged array arguments

This question or a similar variation pops up in the forums once in a while. The core problem is that while null terminated char arrays can be marshaled to a System.String fairly easily, with other unmanaged arrays the marshaller cannot know for sure what size of array to create. The solution is to marshal the argument as an IntPtr and then in the calling code, the user can manually create a managed array and copy the data into it. This assumes that the unmanaged API has a size parameter (which it invariably does) that indicates the length of the unmanaged array. Here’s some example code that shows how this is done:

C++ code

typedef long (CALLBACK * READDATA)(unsigned char * data, int length);

extern "C" __declspec(dllexport)  void __stdcall SomeFunc(READDATA rd)
{
 unsigned char data[5] = {'a', 'b', 'c', 'd', 'e'};
 rd(data, 5);
}

And here’s the calling code:

C# code

delegate int ReadDataDelegate(IntPtr data, int len);

[DllImport("SimpleLib.dll")]
static extern void SomeFunc(ReadDataDelegate d);

private static void CallNativeWithCallback()
{
    ReadDataDelegate newCB = new ReadDataDelegate((data, len) =>
    {
        byte[] array = new byte[len];
        Marshal.Copy(data, array, 0, len);
        foreach (byte b in array)
        {
            Console.WriteLine((char)b);
        }
        Console.WriteLine();
        return 0;
    });
    SomeFunc(newCB);
}

4 thoughts on “FAQ: C# calling C++ code with callbacks with unmanaged array arguments

Leave a comment