Question

I have the following signature in my C++ code

extern "C" __declspec(dllexport) void*__cdecl 
widgetCreate(char* data, size_t length){
    return new Widget(data);
}

And the following in my C# code:

[DllImport(Path, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr widgetCreate(byte[] data, int length);

I want the c++ code to keep its own copy of the data, so I do a memcpy():

extern "C" __declspec(dllexport) void*__cdecl 
widgetCreate(char* data, size_t length){
    auto copy = new char[length];
    memcpy(copy, data, length);
    return new Widget(copy);
}

Is there a way to make the marshaler do that copy for me? Something like the following?

[DllImport(Path, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr widgetCreate([CopyThisThing] byte[] data);
Was it helpful?

Solution

No. That copy has to be released again, sooner or later. The pinvoke marshaller cannot know which allocator the native code uses. So it cannot reliably pick a heap to store the copy. The only [MarshalAs] you could write to force a copy to be made is one that uses UnmanagedType.SafeArray. A specific array type that was created to make interop safe, that is normally only accepted by code that was explicitly written to support interop. Like COM code, you don't typically pinvoke that.

There are cases where it must make a copy, that happens when the managed data is not blittable and needs to be converted from the managed layout to the native layout, as directed by the [StructLayout]. That's however entirely transparent and the marshaller will always delete the copy after the call is completed.

You can make it work by creating the copy explicitly in your C# code. You declare the argument as IntPtr and allocate memory with Marshal.AllocHGlobal() or Marshal.AllocCoTaskMem(). And marshal the data yourself, typically by calling Marshal.StructureToPtr() The odds that this works out are not great unless you know for a fact that the native code uses the appropriate way to release the copy again. In other words, LocalFree() or CoTaskMemFree(). If you use VS2012 or higher for the native code then the odds significantly increase, its CRT now uses the default process heap instead of creating its own heap.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top