Passing struct from unmanaged C++ to C#

mkolarek picture mkolarek · Aug 29, 2013 · Viewed 8.9k times · Source

Note: The final working solution is after the edit!

I hope someone can help me with a problem I've been trying to solve for the last few days.

I am trying to pass a struct from a unmanaged C++ DLL to a C# script. This is what I have so far:

C++

EXPORT_API uchar *detectMarkers(...) {
    struct markerStruct {
            int id;
    } MarkerInfo;

    uchar *bytePtr = (uchar*) &MarkerInfo;

    ...

    MarkerInfo.id = 3;
    return bytePtr;
}

C#

[DllImport ("UnmanagedDll")] 
    public static extern byte[] detectMarkers(...);

...

[StructLayout(LayoutKind.Explicit, Size = 16, Pack = 1)]
public struct markerStruct
{
    [MarshalAs(UnmanagedType.U4)]
    [FieldOffset(0)]
    public int Id;
}

...

markerStruct ByteArrayToNewStuff(byte[] bytes){
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    markerStruct stuff = (markerStruct)Marshal.PtrToStructure(
        handle.AddrOfPinnedObject(), typeof(markerStruct));
    handle.Free();
    return stuff;
}

...

print(ByteArrayToNewStuff (detectMarkers(d, W, H, d.Length) ).Id);

The problem is that this works, but the value printed is completely off (sometimes it prints around 400, sometimes max int value).

I'm guessing that there's something wrong with how I marshalled the struct in C#. Any ideas?

Edit:

This is the working solution using ref:

C++

struct markerStruct {
    int id;
};

...

EXPORT_API void detectMarkers( ... , markerStruct *MarkerInfo) {
    MarkerInfo->id = 3;
    return;
}

C#

[DllImport ("ArucoUnity")] 
    public static extern void detectMarkers( ... ,
        [MarshalAs(UnmanagedType.Struct)] ref MarkerStruct markerStruct);

...

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct MarkerStruct
{
    public int Id;
}

...

detectMarkers (d, W, H, d.Length, ref markerInfo);      
print( markerInfo.Id );

Answer

Ben Voigt picture Ben Voigt · Aug 29, 2013

You're returning a pointer to a local variable which has already been destroyed before .NET can read it. That's a bad idea in pure C++ and a bad idea with p/invoke.

Instead, have C# pass a pointer to a structure (just use the ref keyword) and the C++ code just fill it in.