Is there any way to pass C# object references (class types, not structs) into and out of C++ via native interop?
Here's my use case:
I'm trying to write a game engine in C# but want to use a native (C++) physics library. The physics library maintains its own internal state of all the physical bodies and lets the user associate a small amount of data (a pointer) with each body. After a physics simulation tick, the library supplies a list of all physical bodies that moved. I want to iterate over this list and relay all the changes to the corresponding C# objects.
The question is: what's the best way to associate objects in C# with the corresponding physical bodies in C++?
I can think of a couple ways:
Is there any mechanism like option 3 I don't know of? C++/CLI isn't an option since I want to support platforms without it.
Pass by reference. Even though C always uses 'pass by value', it is possible simulate passing by reference by using dereferenced pointers as arguments in the function definition, and passing in the 'address of' operator & on the variables when calling the function.
C always uses 'pass by value' to pass arguments to functions (another term is 'call by value', which means the same thing), which means the code within a function cannot alter the arguments used to call the function, even if the values are changed inside the function.
If the function needs to modify a dynamically allocated (i.e. heap-allocated) string buffer from the caller, you must pass in a pointer to a pointer. In C, function arguments are passed by value.
Array can be passed to function in C using pointers and because they are passed by reference changes made on an array will also be reflected on the original array outside function scope. Arrays can be returned from functions in C using a pointer pointing to the base address of the array or by creating user-defined data type using struct.
I would suggest using the tool that was specifically designed for situations like that, i.e. System.Runtime.InteropServices.GCHandle
.
Usage
In:
GCHandle
for your CLR object with GCHandleType.Normal
or GCHandleType.Weak
.GCHandle
to an IntPtr
via GCHandle.ToIntPtr
.IntPtr
to the C++ side as the userdata pointer that you mentioned.Out:
IntPtr
back from the C++ side.IntPtr
back to a GCHandle
via GCHandle.FromIntPtr
Target
Property of the GCHandle
to get to the original CLR object.When the CLR object is no longer being referenced from the C++ side (either because the C++ object was destroyed or because you reset the userdata pointer to e.g. IntPtr.Zero
), release the GCHandle
by calling GCHandle.Free
.
What type of GCHandle
you should allocate depends on whether you want the GCHandle
to keep the object alive (GCHandleType.Normal
) or not (GCHandleType.Weak
).
Marshal.GetFunctionPointerForDelegate
to get an address to pass to the unmanaged side. The only thing to note is that you have to make sure that the callback is using __stdcall
.Also, remember that AccessViolationException
s are generally not catchable in .NET.
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