13

我有这个 C++ 代码:

extern "C" __declspec(dllexport) VOID AllocateFoo(MY_DATA_STRUCTURE** foo)
{
    *foo = new MY_DATA_STRUCTURE;

    //do stuff to foo
}

然后在 C# 中我这样调用函数:

[DllImport("MyDll.dll")]
static extern void AllocateFoo(out IntPtr pMyDataStruct);

...

MyDataStructure GetMyDataStructure()
{
    IntPtr pData;
    ManagedAllocateFooDelegate(out pData);

    MyDataStructure foo = (MyDataStructure)Marshal.PtrToStructure(pData, typeof(MyDataStructure));
    return foo;
}

其中 MyDataStructure 是一个结构(不是类),它对应于 MY_DATA_STRUCTURE 并且成员被适当地编组。

所以问题:当 MyDataStructure 被 GC 时,我是否需要存储 pData 然后在非托管代码中再次释放它?MSDN 对 Marshal.PtrToStructure(IntPtr, Type) 说:“将数据从非托管内存块编组到新分配的指定类型的托管对象。” 在那句话中,“Marshall”是指“复制”吗?在这种情况下,我需要保留 (IntPtr pData),然后将其传递给非托管代码(在 MyDataStructure 析构函数中),以便我可以执行 C++“删除”?

我已经搜索过,但找不到足够明确的答案。

4

2 回答 2

12

正如埃里克所说,元帅确实意味着复制,但我认为他没有回答您问题的主要观点。

您是否需要保留 pData 本机指针,直到 MyDataStructure 被 GCed?不。

编组后,您的 MyDataStructure 实例 foo 包含 pData 指向的结构的副本。您不再需要保留 pData。为了避免内存泄漏,您必须将该 pData 传递给另一个将删除它的非托管函数,并且可以在封送处理之后立即完成,无论您持有 MyDataStructure 实例多长时间。

于 2009-01-30T21:19:00.203 回答
8

是的,在这种情况下,Marshall 表示复制;因此,您需要在非托管代码中释放内存。对 PtrToStructure 的所有调用都是从 pData 指向的内存位置读取由目标结构“MyDataStructure”的大小指示的字节数。

当然,细节完全取决于“MyDataStructure”的样子(您是否在 MyDataStructure 中使用任何 FieldOffset 或 StructLayout 属性) - 但最终结果是 PtrToStructure 的返回是数据的副本。

正如GBegen在他的回答中指出的那样,我没有回答你问题的主要观点。是的,您需要在非托管代码中删除结构的非托管副本,但不,您不需要保留 pData - 一旦对 PtrToStructure 的调用完成,您就可以删除非托管副本。

PS:我已经编辑了我的帖子以包含此信息,以便将答案合并到一个帖子中 - 如果有人支持这个答案,请支持 GBegen 的回答以及他的贡献。

于 2009-01-30T20:53:00.177 回答