Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing struct from unmanaged C++ to C#

Tags:

c++

c#

unmanaged

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 );
like image 637
mkolarek Avatar asked Aug 29 '13 19:08

mkolarek


2 Answers

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.

like image 152
Ben Voigt Avatar answered Oct 21 '22 06:10

Ben Voigt


The MarkerInfo variable is local and goes out of scope when the function returns. Don't return pointers to local variables, the objects they point to won't exist anymore.

like image 3
Pruyque Avatar answered Oct 21 '22 06:10

Pruyque