I have this C++ code:
extern "C" __declspec(dllexport) VOID AllocateFoo(MY_DATA_STRUCTURE** foo)
{
*foo = new MY_DATA_STRUCTURE;
//do stuff to foo
}
Then in C# I call the function thus:
[DllImport("MyDll.dll")]
static extern void AllocateFoo(out IntPtr pMyDataStruct);
...
MyDataStructure GetMyDataStructure()
{
IntPtr pData;
ManagedAllocateFooDelegate(out pData);
MyDataStructure foo = (MyDataStructure)Marshal.PtrToStructure(pData, typeof(MyDataStructure));
return foo;
}
Where MyDataStructure is a struct (not class) which corresponds to MY_DATA_STRUCTURE and members are marshalled appropriately.
So questions: do I need to store pData and then release it again in unmanaged code when MyDataStructure is GC'd? MSDN says for Marshal.PtrToStructure(IntPtr, Type): "Marshals data from an unmanaged block of memory to a newly allocated managed object of the specified type." In that sentence does "Marshall" mean "copy"? In which case I'd need to preserve (IntPtr pData) and then pass it to unmanaged code (in the MyDataStructure destructor) so I can do a C++ "delete"?
I've searched but I can't locate a sufficiently explicit answer for this.
The .NET runtimes provide a few extension points for you to customize your structure's layout and how fields are marshalled. Customizing structure layout is supported for all scenarios, but customizing field marshalling is only supported for scenarios where runtime marshalling is enabled.
If runtime marshalling is disabled, then any field marshalling must be done manually. .NET provides the System.Runtime.InteropServices.StructLayoutAttribute attribute and the System.Runtime.InteropServices.LayoutKind enumeration to allow you to customize how fields are placed in memory. The following guidance will help you avoid common issues.
When using a WinRT-based API, you may need to marshal a string as an HSTRING. Using the UnmanagedType.HString value, you can marshal a string as a HSTRING. HSTRING marshalling is only supported on runtimes with built-in WinRT support. WinRT support was removed in .NET 5, so HSTRING marshalling is not supported in .NET 5 or newer.
By default, .NET marshals arrays as a pointer to a contiguous list of the elements: If you're interfacing with COM APIs, you may have to marshal arrays as SAFEARRAY* objects. You can use the System.Runtime.InteropServices.MarshalAsAttribute and the UnmanagedType.SafeArray value to tell the runtime to marshal an array as a SAFEARRAY*:
As Erik said, the Marshal does mean copy, but I don't think he answered the main point of your question.
Do you need to hold onto the pData native pointer until the MyDataStructure is GCed? No.
Once marshaled, your MyDataStructure instance, foo, contains a copy of the structure pointed to by pData. You need not hold onto pData any longer. To avoid a memory leak, you must pass that pData into another unmanaged function that will delete it, and that can be done right after the marshaling, regardless of how long you hold on to the MyDataStructure instance.
Yes, in this case, Marshall means copy; thus, you need to deallocate your memory in unmanaged code. All the call to PtrToStructure does is read a number of bytes indicated by the size of the destination structure 'MyDataStructure' from the memory location pointed to by pData.
The details of course depend on exactly what 'MyDataStructure' looks like (do you use any FieldOffset or StructLayout attributes in MyDataStructure) - but the end result is that the return from PtrToStructure is a copy of the data.
As GBegen points out in his answer, I didn't answer the main point of your question. Yes, you will need to delete the unmanaged copy of your structure in unmanaged code, but no, you don't need to hold onto pData - you can delete the unmanaged copy as soon as the call to PtrToStructure completes.
PS: I've edited my post to contain this information so as to consolidate the answers into one post - if anyone upvotes this answer, please upvote GBegen's answer as well for his contribution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With