PInvoke:在 C++ 中分配内存并在 C# 中释放它
PInvoke: Allocate memory in C++ and free it in 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# 端安全地释放它?
AllocHGlobal is one of two memory allocation methods in the Marshal
class. (Marshal.AllocCoTaskMem is the other.) This method exposes the
Win32 LocalAlloc function from Kernel32.dll.
When AllocHGlobal calls LocalAlloc, it passes a LMEM_FIXED flag, which
causes the allocated memory to be locked in place. Also, the allocated
memory is not zero-filled.
因此,您可以从非托管代码调用 LocalAlloc
来分配内存,并从托管代码调用 Marshal.FreeHGlobal
来释放内存。同样,LocalFree
可用于非托管代码以释放使用 Marshal.AllocHGlobal
.
分配的内存
正如文档还暗示的那样,您可以对 CoTaskMemAlloc/CoTaskMemFree
和 Marshal.AllocCoTaskMem/FreeCoTaskMem
执行相同的操作。
话虽如此,您这样做是在为跌倒做准备。将分配和释放保留在同一个模块中要干净得多。以这种方式混合匹配很可能导致对谁负责释放内存的极大混淆。
这传统上总是以糟糕的方式结束,Microsoft CRT 使用 HeapCreate() 创建了自己的堆来为 C 或 C++ 程序中的 malloc/new 调用提供服务。无法在 C# 中释放此类内存,您没有堆句柄。
然而,从 VS2012(msvcr120.dll 及更高版本)附带的 CRT 开始,情况已经发生了变化。它现在使用默认进程堆,即由 GetProcessHeap() 编辑的 return。也是 Marshal.Alloc/FreeHGlobal() 使用的那个。所以你现在可以试试了,前提是本机代码不使用调试分配器 (crtdbg.h)。小心丢弃该调试选项。
pinvoke 编组器未更改,也不能更改。如果它必须释放内存,例如数组或字符串 returned 作为函数 return 值,那么它将调用 CoTaskMemFree()。从您的问题中不清楚哪个可以适用。如有疑问,并且如果您在本机代码中有选择,那么 CoTaskMemAlloc() 不会出错,它与 C# 代码中的 Marshal.FreeCoTaskMem() 配对。
我们正在使用 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# 端安全地释放它?
AllocHGlobal is one of two memory allocation methods in the Marshal class. (Marshal.AllocCoTaskMem is the other.) This method exposes the Win32 LocalAlloc function from Kernel32.dll.
When AllocHGlobal calls LocalAlloc, it passes a LMEM_FIXED flag, which causes the allocated memory to be locked in place. Also, the allocated memory is not zero-filled.
因此,您可以从非托管代码调用 LocalAlloc
来分配内存,并从托管代码调用 Marshal.FreeHGlobal
来释放内存。同样,LocalFree
可用于非托管代码以释放使用 Marshal.AllocHGlobal
.
正如文档还暗示的那样,您可以对 CoTaskMemAlloc/CoTaskMemFree
和 Marshal.AllocCoTaskMem/FreeCoTaskMem
执行相同的操作。
话虽如此,您这样做是在为跌倒做准备。将分配和释放保留在同一个模块中要干净得多。以这种方式混合匹配很可能导致对谁负责释放内存的极大混淆。
这传统上总是以糟糕的方式结束,Microsoft CRT 使用 HeapCreate() 创建了自己的堆来为 C 或 C++ 程序中的 malloc/new 调用提供服务。无法在 C# 中释放此类内存,您没有堆句柄。
然而,从 VS2012(msvcr120.dll 及更高版本)附带的 CRT 开始,情况已经发生了变化。它现在使用默认进程堆,即由 GetProcessHeap() 编辑的 return。也是 Marshal.Alloc/FreeHGlobal() 使用的那个。所以你现在可以试试了,前提是本机代码不使用调试分配器 (crtdbg.h)。小心丢弃该调试选项。
pinvoke 编组器未更改,也不能更改。如果它必须释放内存,例如数组或字符串 returned 作为函数 return 值,那么它将调用 CoTaskMemFree()。从您的问题中不清楚哪个可以适用。如有疑问,并且如果您在本机代码中有选择,那么 CoTaskMemAlloc() 不会出错,它与 C# 代码中的 Marshal.FreeCoTaskMem() 配对。