Question

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 );
Was it helpful?

Solution

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.

OTHER TIPS

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.

Going to give this a whirl... thx for the post...

// new struct and generic return for items to 
struct  _itemStruct
{
    unsigned int id; // 0 by default, so all lists should start at 1, 0 means unassigned
    wchar_t *Name;
};

// for DLL lib precede void with the following... 
// EXPORT_API 
void getItems(std::vector<_itemStruct *> *items)
{
    // set item list values here


    //unsigned char *bytePtr = (unsigned char*)&items; // manual pointer return

    return;
};

/* // In theory c# code will be...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct _itemStruct
{
    public unsigned int Id;
    public string Name;
}

[DllImport ("ListOfItems")] // for ListOfItems.DLL
public static extern void getItems(
[MarshalAs(UnmanagedType.Struct)] ref List<_itemStruct> items);
// */
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top