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 AccessViolationExceptions 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