简体   繁体   English

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

[英]PInvoke: Allocate memory in C++ and free it in C#

We are using PInvoke to interop between C# and C++. 我们使用PInvoke在C#和C ++之间进行交互。

I have an interop struct as follows, with an identical layout C++ struct on the other side. 我有一个如下的互操作结构,另一边有相同的布局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;
        }
    }
}

Now I would like to add a second ctor 现在我想添加第二个ctor

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

and then write a method in C++ that allows C++ to fill in the data. 然后用C ++编写一个允许C ++填充数据的方法。 This would allow us to use the same structure for input as well as output data... 这将允许我们使用相同的结构进行输入和输出数据......

However, as far as I understand it, AllocHGlobal is available in C# and C++/Cli, but not pure C++. 但是,据我所知, AllocHGlobal可以在C#和C ++ / Cli中使用,但不是纯C ++。

So my question is: How can I allocate memory in C++ such that I can safely free it on the C# side with a call to Marshal.FreeHGlobal(...) ? 所以我的问题是:如何在C ++中分配内存,以便我可以通过调用Marshal.FreeHGlobal(...)安全地在C#端释放它?

From the documentation : 文档

AllocHGlobal is one of two memory allocation methods in the Marshal class. AllocHGlobal是Marshal类中的两种内存分配方法之一。 (Marshal.AllocCoTaskMem is the other.) This method exposes the Win32 LocalAlloc function from Kernel32.dll. (Marshal.AllocCoTaskMem是另一个。)此方法从Kernel32.dll公开Win32 LocalAlloc函数。

When AllocHGlobal calls LocalAlloc, it passes a LMEM_FIXED flag, which causes the allocated memory to be locked in place. 当AllocHGlobal调用LocalAlloc时,它会传递一个LMEM_FIXED标志,这会导致分配的内存被锁定到位。 Also, the allocated memory is not zero-filled. 此外,分配的内存不是零填充。

So, you can call LocalAlloc from your unmanaged code to allocate memory, and Marshal.FreeHGlobal from your managed code to deallocate it. 因此,您可以从非托管代码调用LocalAlloc来分配内存,并从托管代码调用Marshal.FreeHGlobal来释放它。 Likewise, LocalFree can be be used in unmanaged code to deallocate memory allocated with Marshal.AllocHGlobal . 同样, LocalFree可以在非托管代码中使用,以释放分配给Marshal.AllocHGlobal内存。

As the documentation also intimates, you could do the same with CoTaskMemAlloc/CoTaskMemFree and Marshal.AllocCoTaskMem/FreeCoTaskMem . 由于文档也暗示,您可以使用CoTaskMemAlloc/CoTaskMemFreeMarshal.AllocCoTaskMem/FreeCoTaskMem执行相同的Marshal.AllocCoTaskMem/FreeCoTaskMem

Having said that, you are setting yourself up for a fall doing it this way. 话虽如此,你这样做是为了让自己陷入困境。 It is far cleaner to keep the allocation and deallocation in the same modules. 将分配和释放保存在相同的模块中要清晰得多。 Mixing an matching in this way is very likely to lead to great confusion over who is responsible for deallocating the memory. 以这种方式混合匹配很可能会导致对谁负责释放内存产生很大的困惑。

This traditionally always ended up poorly, the Microsoft CRT created its own heap with HeapCreate() to service malloc/new calls in a C or C++ program. 传统上,这总是很糟糕,Microsoft CRT使用HeapCreate()创建了自己的堆,以便在C或C ++程序中为malloc / new调用提供服务。 Can't deallocate such memory in C#, you don't have the heap handle. 无法在C#中释放这样的内存,你没有堆句柄。

That has changed however, starting with the CRT included with VS2012 (msvcr120.dll and up). 然而,这已经改变,从VS2012附带的CRT开始(msvcr120.dll及更高版本)。 It now uses the default process heap, the one returned by GetProcessHeap(). 它现在使用默认进程堆,即GetProcessHeap()返回的进程堆。 Also the one used by Marshal.Alloc/FreeHGlobal(). 也是Marshal.Alloc / FreeHGlobal()使用的那个。 So you now have a shot at it, provided the native code doesn't use the debug allocator (crtdbg.h). 因此,如果本机代码不使用调试分配器(crtdbg.h),那么您现在可以使用它。 Be careful throwing away that debug option. 小心扔掉那个调试选项。

The pinvoke marshaller was not changed, nor can it. pinvoke marshaller没有改变,也没有改变。 If it has to release memory, like an array or string returned as a function return value, then it will call CoTaskMemFree(). 如果它必须释放内存,就像作为函数返回值返回的数组或字符串一样,那么它将调用CoTaskMemFree()。 It is not clear from your question which could apply. 你的问题不清楚哪些适用。 In case of doubt and if you have the choice in your native code then you can't go wrong with CoTaskMemAlloc(), paired to Marshal.FreeCoTaskMem() in your C# code. 如果有疑问并且您可以在您的本机代码中进行选择,那么您可以使用CoTaskMemAlloc()与C#代码中的Marshal.FreeCoTaskMem()配对。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM