问题
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:
- Give each physical body an ID of some kind, associate the ID with the physical body in C++, and maintain a map of ID's to corresponding objects in C#. This seems unnecessarily indirect to me, even with an optimized mapping mechanism.
- Take advantage of the ability to marshal C# delegates (which can implicitly reference C# object instances) into C++ function pointers. This might be the best way to do it; I don't know how C# implements delegates and what type of overhead this would entail.
- Find some other way to refer to C# objects in C++ and associate that reference with each native physical body.
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.
回答1:
I would suggest using the tool that was specifically designed for situations like that, i.e. System.Runtime.InteropServices.GCHandle
.
Usage
In:
- Allocate a
GCHandle
for your CLR object withGCHandleType.Normal
orGCHandleType.Weak
. - Convert the
GCHandle
to anIntPtr
viaGCHandle.ToIntPtr
. - Pass that
IntPtr
to the C++ side as the userdata pointer that you mentioned.
Out:
- Get
IntPtr
back from the C++ side. - Convert the
IntPtr
back to aGCHandle
viaGCHandle.FromIntPtr
- Use the
Target
Property of theGCHandle
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
).
回答2:
- Mapping a unique identifier would allow you to isolate the managed from the unmanaged code. Its probably better if the unmanaged side isn't doing anything with your managed objects since you really can't control much of the object's lifecycle.
- Delegates as function pointers are possible. All you need to do is define your delegate type and create an instance. Then you can use 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
. - It's probably possible to access class members and functions directly, but without a C++/CLI wrapper you're playing with fire.
- Allocate a chunk of memory on the C# side and pass that to C++. In the classes on the managed side use properties to access this memory with appropriate offsets.
- Make the object COM visible.
Also, remember that AccessViolationException
s are generally not catchable in .NET.
来源:https://stackoverflow.com/questions/32105089/how-to-pass-c-sharp-object-references-to-and-from-c