I have an application that marshals data between C# and C++ using PInvoke. The application is a native C++ app that starts the CLR with the C# part internally.
At some point I have to marshal data from C++ to C# and I'm using Marshal.PtrToStructure
for this. The C++ part basically looks like this:
struct X {};
auto xPtr = new X; // somewhere in the application
callCSharp(xPtr); // somewhere else
and the C# part like this:
public void callCSharp(IntPtr xPtr)
{
var x = Marshal.PtrToStructure<X>(xPtr);
}
This code works on my Windows 10 machine but I'm not sure if I could run into trouble with this code. Lifetime management is all done in C++ so I don't have to allocate or free anything on C# side. However I'm not sure if the CLR can always access the native heap. xPtr
is allocated using new
and is therefore located on the native heap of the C++ application. Marshal.PtrToStructure<X>(xPtr)
then tries to read memory at that location but I don't know if this can cause problems.
I've read this which indicates that both the C++ application and the CLR use the same heap (GetProcessHeap
that is) so this seems to support my findings for Windows 10, but it might be different for other OS versions.
Is my sample code above sane? Are there any pitfalls here? Does this code work in Windows 7, 8 and 10?
Yes that's perfectly reasonable and safe as long as the C/C++ code doesn't free the memory. Note that it isn't always necessary (or desirable) to use Marshal
here; depending on what <X>
is, you can do this in other ways too, including:
unsafe
(cast a void*
to a X*
)Unsafe.AsRef<X>(...)
(which casts a void*
to a ref X
)new Span<X>(...)
(which creates a span of some number of X
from a void*
; a span is like a vector, but talking to arbitrary memory)All of these are zero-copy approaches, meaning your C# code is then talking directly to exactly the same memory space, not a local snapshot; but if you dereference the pointer (managed or unmanged) into a non-reference local, then it will make a copy.