我们正在使用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#端释放内存?
答案 0 :(得分:3)
AllocHGlobal是Marshal中两种内存分配方法之一 类。 (Marshal.AllocCoTaskMem是另一个。)这个方法暴露了 来自Kernel32.dll的Win32 LocalAlloc函数。
当AllocHGlobal调用LocalAlloc时,它会传递一个LMEM_FIXED标志 导致分配的内存被锁定到位。另外,分配 记忆不是零填充。
因此,您可以从非托管代码中调用LocalAlloc
来分配内存,并从托管代码调用Marshal.FreeHGlobal
来释放内存。同样,LocalFree
可以在非托管代码中使用,以释放分配有Marshal.AllocHGlobal
的内存。
由于文档也暗示,您可以对CoTaskMemAlloc/CoTaskMemFree
和Marshal.AllocCoTaskMem/FreeCoTaskMem
执行相同的操作。
话虽如此,你正在为自己做好准备。将分配和释放保存在相同的模块中要清晰得多。以这种方式混合匹配很可能会导致对谁负责释放内存产生很大的困惑。
答案 1 :(得分:2)
传统上,这总是很糟糕,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()配对。