PInvoke:用C ++分配内存并在C#中释放它

我们正在使用PInvoke在C#和C ++之间进行交互。

我有一个如下的互操作结构,另一边有相同的布局C ++结构。

[StructLayout(LayoutKind.Sequential)] public struct MeshDataStruct : IDisposable { public MeshDataStruct(double[] vertices, int[] triangles , int[] surfaces) { _vertex_count = vertices.Length / 3; _vertices = Marshal.AllocHGlobal(_vertex_count*3*sizeof (double)); Marshal.Copy(vertices, 0, _vertices, _vertex_count); } // .. extract data methods to double[] etc. private IntPtr _vertices; private int _vertex_count; public void Dispose() { if (_vertices != IntPtr.Zero) { Marshal.FreeHGlobal(_vertices); _vertices = IntPtr.Zero; } } } 

现在我想添加第二个ctor

  public MeshDataStruct(bool filled_in_by_native_codee) { _vertex_count = 0; _vertices = IntPtr.Zero; } 

然后用C ++编写一个允许C ++填充数据的方法。 这将允许我们使用相同的结构进行输入和输出数据……

但是,据我所知, AllocHGlobal可以在C#和C ++ / Cli中使用,但不是纯C ++。

所以我的问题是:如何在C ++中分配内存,以便我可以通过调用Marshal.FreeHGlobal(...)安全地在C#端释放它?

传统上,这总是很糟糕,Microsoft CRT使用HeapCreate()创建了自己的堆,以便在C或C ++程序中为malloc / new调用提供服务。 无法在C#中释放这样的内存,你没有堆句柄。

然而,这已经改变了,从VS2012附带的CRT开始(msvcr120.dll及更高版本)。 它现在使用默认进程堆,即GetProcessHeap()返回的进程堆。 也是Marshal.Alloc / FreeHGlobal()使用的那个。 因此,如果本机代码不使用调试分配器(crtdbg.h),那么您现在可以使用它。 小心扔掉那个调试选项。

pinvoke marshaller没有改变,也没有改变。 如果必须释放内存,就像作为函数返回值返回的数组或字符串一样,那么它将调用CoTaskMemFree()。 你的问题不清楚哪些适用。 如果有疑问并且您可以在您的本机代码中进行选择,那么您可以使用CoTaskMemAlloc()与C#代码中的Marshal.FreeCoTaskMem()配对。

从文档 :

AllocHGlobal是Marshal类中的两种内存分配方法之一。 (Marshal.AllocCoTaskMem是另一个。)此方法从Kernel32.dll公开Win32 LocalAlloc函数。

当AllocHGlobal调用LocalAlloc时,它会传递一个LMEM_FIXED标志,这会导致分配的内存被锁定到位。 此外,分配的内存不是零填充。

因此,您可以从非托管代码调用LocalAlloc来分配内存,并从托管代码调用Marshal.FreeHGlobal来释放它。 同样, LocalFree可以在非托管代码中使用,以释放分配给Marshal.AllocHGlobal内存。

由于文档也暗示,您可以使用CoTaskMemAlloc/CoTaskMemFreeMarshal.AllocCoTaskMem/FreeCoTaskMem执行相同的Marshal.AllocCoTaskMem/FreeCoTaskMem

话虽如此,你正在以这种方式做好准备。 在同一模块中保持分配和释放更加清晰。 以这种方式混合匹配很可能会导致对负责释放内存的人产生很大的困惑。