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

Wil*_*ert 6 c# c++ pinvoke interop marshalling

我们正在使用 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我想添加第二个 ctor

    public MeshDataStruct(bool filled_in_by_native_codee)
    {
        _vertex_count = 0;
        _vertices = IntPtr.Zero;
    }
Run Code Online (Sandbox Code Playgroud)

然后用C++写一个方法,让C++可以填充数据。这将允许我们对输入和输出数据使用相同的结构......

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

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

Dav*_*nan 5

文档

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

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

因此,您可以LocalAlloc从非托管代码调用以分配内存,并Marshal.FreeHGlobal从托管代码调用以释放内存。同样,LocalFree可用于非托管代码以释放分配的内存Marshal.AllocHGlobal

正如文档中所暗示的那样,您可以使用CoTaskMemAlloc/CoTaskMemFreeand做同样的事情Marshal.AllocCoTaskMem/FreeCoTaskMem

话虽如此,您这样做是在为跌倒做准备。将分配和释放保持在同一个模块中要干净得多。以这种方式混合匹配很可能会导致对谁负责释放内存的极大困惑。


Han*_*ant 5

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

但是,从 VS2012 附带的 CRT(msvcr120.dll 及更高版本)开始,情况发生了变化。它现在使用默认进程堆,即 GetProcessHeap() 返回的进程堆。也是 Marshal.Alloc/FreeHGlobal() 使用的那个。因此,您现在可以尝试一下,前提是本机代码不使用调试分配器 (crtdbg.h)。小心扔掉那个调试选项。

pinvoke 编组器没有改变,也不能改变。如果它必须释放内存,比如作为函数返回值返回的数组或字符串,那么它将调用 CoTaskMemFree()。从您的问题中不清楚哪些适用。如有疑问,并且如果您可以在本机代码中进行选择,那么在 C# 代码中与 Marshal.FreeCoTaskMem() 配对的 Co​​TaskMemAlloc() 不会出错。