从非托管C++传递结构到C#

10

注意:最终有效的解决方案在编辑后面!

我希望有人能够帮助我解决我已经尝试了几天的问题。

我正在尝试从一个非托管的C++ DLL传递一个结构体到一个C#脚本。目前为止,我已经完成以下内容:

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);

问题在于这个代码可以运行,但打印的值完全偏离预期(有时打印大约为400,有时打印最大整数值)。

我猜测可能是我在C#中序列化结构体的方式有误。有什么想法吗?

编辑:

这是使用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 );
3个回答

7
你正在返回指向已在.NET读取之前被销毁的本地变量的指针。这是在纯C++中和p / invoke中都不好的做法。 相反,让C#传递一个指向结构体的指针(只需使用“ref”关键字),然后C ++代码就可以填充它。

我尝试使用ref,但仍然无法得到正确的值...您能否看一下我编辑后的问题? - mkolarek
@kolarek:就像我在我的答案中所说的,当你使用refout关键字时,C#实际上会传递一个指针。因此,在C++端使用void detectMarkers( /*...*/ markerStruct* MarkerInfo),然后MarkerInfo->id = 3;。另外,在p/invoke签名中去掉In属性,这意味着不从C++获取数据,这显然与你想要的相反。 - Ben Voigt
非常感谢,我已经把它启动起来了! - mkolarek
@kolarek:很高兴听到这个好消息。如果这个答案解决了你的问题,点击其左边的空心复选标记让未来的访问者知道这是正确的解决方案。另外,很高兴看到你把可工作的代码添加到了你的问题中。 - Ben Voigt

3
< p > MarkerInfo 变量是本地变量,当函数返回时就会超出其作用域。不要返回指向本地变量的指针,它们所指向的对象将不再存在。


谢谢!你有什么建议,我应该做什么? - mkolarek

0

准备试一下这个...谢谢你的帖子...

// 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);
// */

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接