I am doing some manual marshaling for interop from C#/.NET to unmanaged DLLs.
Consider the following struct:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct LockInfo
{
ushort lockVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string lockName;
}
I marshal this to unmanaged memory:
var lockInfo = new LockInfo();
var lockInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lockInfo));
Marshal.StructureToPtr(lockInfo, lockInfoPtr, false);
Once I'm done with it, do I need to call Marshal.DestroyStructure
on lockInfoPtr
?
I am aware of the need to call Marshal.FreeHGlobal
, but prior to that, is Marshal.DestroyStructure
actually required in this case?
I have found it very difficult to understand Microsoft's documentation around this. Google searching hasn't helped, possibly because I just don't quite understand marshalling properly yet (but I am learning).
Similar questions...
I have reviewed the similar question "Marshal.DestroyStructure vs Marshal.FreeHGlobal in .Net" but this question does not address the issue of the content a struct should contain that would require the use of DestroyStructure
. My limited understanding is that DestroyStructure
does not always need to be called, only when the structure contains certain kinds of fields. In my case I am unsure if a string, being marshalled as ByValTStr, requires the use of DestroyStructure
.
Marshaling is complex stuff and a full answer could fill a whole chapter in a book, which of course is not appropriate here. So, in a very succinct nutshell:
Normally, when calling native functions from managed code, .NET marshals strings and arrays to native BSTR strings and SafeArray arrays.
To accomplish this, the marshaler calls SysAllocString
and SafeArrayCreate
respectively.
At some point, when these native-side strings and arrays are no longer needed, the marshaler will call SysFreeString
and SafeArrayDestroy
respectively to free memory.
If you take over .NET's automatic marshaling, and call methods like Marshal.StructureToPtr
to manually marshal a structure, you become responsible for freeing/destroying those native-side BSTRs and SafeArrays. That's exactly what Marshal.DestroyStructure
is for.
However...
By prepending the [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
attribute to your string field, you instructed the marshaler to not marshal the string to a BSTR string, but rather to allocate a fixed-length character array within the native-side structure itself.
That being the case, there is no need to call Marshal.DestroyStructure
because there is no BSTR string to free. Of course you will still need to call Marshal.FreeHGlobal
, I see you are aware of that.
Credit to @SimonMourier for his comment that made it all click.