10

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 );
mkolarek
  • 527
  • 1
  • 7
  • 16

3 Answers3

7

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.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I've tried to use ref, but I still haven't managed to get the right value out... Could you possibly have a look at my edited question? – mkolarek Aug 30 '13 at 12:43
  • @kolarek: Like I said in my answer, when you use the `ref` or `out` keyword C# will actually pass a pointer. So use `void detectMarkers( /*...*/ markerStruct* MarkerInfo)` on the C++ side, and then `MarkerInfo->id = 3;`. Also, get rid of the `In` attribute in the p/invoke signature, that means to not get data back from C++, which obviously is the opposite of what you want. – Ben Voigt Aug 30 '13 at 14:36
  • Thank you very much, I've got it up and running! – mkolarek Aug 31 '13 at 08:54
  • @kolarek: Glad to hear that. If this answer solved your problem, you click the hollow checkmark to its left to let future visitors know it was the right solution. Also, very nice to see you added the working code to your question. – Ben Voigt Aug 31 '13 at 18:29
3

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.

Pruyque
  • 372
  • 1
  • 5
0

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);
// */
Joseph Poirier
  • 386
  • 2
  • 17