Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PInvoke: Allocate memory in C++ and free it in C#

We are using PInvoke to interop between C# and C++.

I have an interop struct as follows, with an identical layout C++ struct on the other side.

[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

    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. 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++.

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(...)?

like image 718
Wilbert Avatar asked Nov 02 '15 16:11

Wilbert


People also ask

How do you allocate and deallocate memory?

In C++ when we want to allocate memory from the free-store (or we may call it heap) we use the new operator. int *ptr = new int; and to deallocate we use the delete operator.

Does malloc allocate memory from free?

In C, the library function malloc is used to allocate a block of memory on the heap. The program accesses this block of memory via a pointer that malloc returns. When the memory is no longer needed, the pointer is passed to free which deallocates the memory so that it can be used for other purposes.

What function should be used to free the memory allocated by calloc () in C?

Function used to free the memory allocated by calloc() - free();

How is memory allocated to variables in C?

3.2. 1 Memory Allocation in C ProgramsThe space is allocated once, when your program is started (part of the exec operation), and is never freed. Automatic allocation happens when you declare an automatic variable, such as a function argument or a local variable.


2 Answers

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. Can't deallocate such memory in C#, you don't have the heap handle.

That has changed however, starting with the CRT included with VS2012 (msvcr120.dll and up). It now uses the default process heap, the one returned by GetProcessHeap(). Also the one used by Marshal.Alloc/FreeHGlobal(). So you now have a shot at it, provided the native code doesn't use the debug allocator (crtdbg.h). Be careful throwing away that debug option.

The pinvoke marshaller was not changed, nor can it. If it has to release memory, like an array or string returned as a function return value, then it will call 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.

like image 74
Hans Passant Avatar answered Sep 30 '22 20:09

Hans Passant


From the documentation:

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.

So, you can call LocalAlloc from your unmanaged code to allocate memory, and Marshal.FreeHGlobal from your managed code to deallocate it. Likewise, LocalFree can be be used in unmanaged code to deallocate memory allocated with Marshal.AllocHGlobal.

As the documentation also intimates, you could do the same with CoTaskMemAlloc/CoTaskMemFree and 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.

like image 21
David Heffernan Avatar answered Sep 30 '22 21:09

David Heffernan