Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass C# object references to and from C++

Tags:

c++

c#

pinvoke

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:

  1. 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.
  2. 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.
  3. 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.

like image 756
jbatez Avatar asked Aug 19 '15 20:08

jbatez


People also ask

How do you pass by reference in C programming?

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.

What does pass by value mean in C++?

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.

How do you pass arguments to a function in C?

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.

How do you pass an array to a function in C?

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.


2 Answers

I would suggest using the tool that was specifically designed for situations like that, i.e. System.Runtime.InteropServices.GCHandle.

Usage

In:

  1. Allocate a GCHandle for your CLR object with GCHandleType.Normal or GCHandleType.Weak.
  2. Convert the GCHandle to an IntPtr via GCHandle.ToIntPtr.
  3. Pass that IntPtr to the C++ side as the userdata pointer that you mentioned.

Out:

  1. Get IntPtr back from the C++ side.
  2. Convert the IntPtr back to a GCHandle via GCHandle.FromIntPtr
  3. Use the 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).

like image 50
Paul Groke Avatar answered Sep 28 '22 13:09

Paul Groke


  1. 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.
  2. 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.
  3. It's probably possible to access class members and functions directly, but without a C++/CLI wrapper you're playing with fire.
  4. 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.
  5. Make the object COM visible.

Also, remember that AccessViolationExceptions are generally not catchable in .NET.

like image 36
theB Avatar answered Sep 28 '22 15:09

theB