c# / c++ /cli wrappers

Started by
7 comments, last by Shaarigan 8 years, 3 months ago

hello, i created a c#/c++cli/c++ application.
in c++ i wrote a little opengl engine and in c# i implemented the ui interface.
in cli i created the wrappers around c++ functions an i have imported it in c# in a managed dll.
Now, what is the system for send/receive data as collections from and to c#?
I can use a simple MVC or it can be slow?
Thanks.

Advertisement
Not entirely sure what you're asking here. "MVC" (or Model View Controller, I assume) has nothing to do with data marshalling that I am aware of.

If you want C++ to access data in .NET or vice versa you are going to have to "marshal" it - basically copying memory from one to the other and back, and it's just as expensive as it sounds. Fortunately C++/CLI comes with several utility functions and classes to help you do it, but it's still expensive. There are several examples over on MSDN that lay out exactly how to marshal various types of data.

Ideally you minimize the number of times you switch languages, do as much as you can do in one language or the other, and then switch when you have to.

Fortunately, marshalling is usually fast enough for UI programming, where a C# UI can "live update" a C++ renderer every time the user makes a change, because users are incredibly slow compared to the computer.

But you wouldn't want to do something like sort a list of items in C# where you have to call into C++ for every single comparison - it would be far better to copy the data to one language or the other and do the sort there so you're only marshalling twice for the whole sort rather than once per comparison.

Note that you can also use unsafe and fixed to use pointers in C#, including pointers to arrays. By doing that, you can bypass the whole marshalling step. The limitation is that you can only use pointers to pure value types (as in may not have any reference type members). Using unsafe and pointers in C# is generally not recommended, but it's probably not worse than using C++/CLI.

Note that you can also use unsafe and fixed to use pointers in C#, including pointers to arrays. By doing that, you can bypass the whole marshalling step. The limitation is that you can only use pointers to pure value types (as in may not have any reference type members). Using unsafe and pointers in C# is generally not recommended, but it's probably not worse than using C++/CLI.

Fixed pointers and unsafe methods are for interacting with P/Invoke, and P/Invoke and C++/CLI are complementary technologies, not competing technologies. To my knowledge, P/Invoke does not work with C++ libraries so C++/CLI is the only way to go. (Considering the absolute mess that is C++ name mangling, making an automated tool such as P/Invoke support linking to C++ library would be a total PITA to maintain anyway.)

To wrap a C++ library, you typically use C++/CLI and leave your C# code free of the unsafe stuff like pointers. If you have any unsafe stuff to do, you do it in your C++/CLI wrapper where pointers are first-class citizens and you can access both the managed heap (via references) and the unmanaged heap, and you do the marshaling yourself.

I don't find marshaling to be slow. I am using two (previously 3) C++/CLI wrapper libraries for my game editor in C# and it works incredibly well, although I am not using it to keep graphics animated at 60 FPS in the level editor. Marshaling obviously has a cost since there is processing to be done for any managed input on top of the forwarded method call, but I designed the API of my C++/CLI wrapper classes with no real care for mashaling efficiency and it still works well. I think you shouldn't suffer too much from slowdowns unless you are passing very long lists of managed objects that have to be copied. Strings can also be an issue because of encoding, but even then that should not be a huge issue unless your string contains the Lord of the Rings trilogy or something.

As in our c++ engine were using c# as an frontend for a various set of tools communicating with the engine and back. For doing that I was doing heavy research an the c++/cli capabilities but we decide that this wasnt even meant to be used in that way by microsoft itself because c++/cli was originaly designed to add some c++ functionality in the .net framework without the need of using the plain c p/invoke but not for realtime interaction between them.

We then decided to make some compiler switches in our engine for adding some additional functions wherever needed decorated by an dllexport/visibility abbtribute

statement the engine namespace named e.g. net_CreateTexture, pointing to the function for texture creation. This enables the capability of p/invoking the function from the c# side by knowing the export name from c++ (that may be obtained by using dumpbin to the compiled c++ library) in the way


[DllImport("Engine.dll", EntryPoint = "?net_CreateTexture@TextureGenerator@FireEngine@@YAFF@Z", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateTexture(ref IntPtr target);

The behavior from this line of code is passing an IntPtr type from .net into the creation function on c++ side that will be filled and stored bointing to the texture object in native memory so youre able to call additional functions from .net passing back that pointer to c++ then performing the real opperation on it by be very fast in performance and you dont waste time by writing code twice (resource management, memory and so on). The function overhead is simply passing a pointer like type that was made and optimized to avoid the need of beeing marshalled by microsoft.

We found that be the best solution for our propose otherwise it would be a complex marshalling overhead needed on each side in .net and c++/cli for getting the native data into .net memory model and back to c++

As in our c++ engine were using c# as an frontend for a various set of tools communicating with the engine and back. For doing that I was doing heavy research an the c++/cli capabilities but we decide that this wasnt even meant to be used in that way by microsoft itself because c++/cli was originaly designed to add some c++ functionality in the .net framework without the need of using the plain c p/invoke but not for realtime interaction between them.

We then decided to make some compiler switches in our engine for adding some additional functions wherever needed decorated by an dllexport/visibility abbtribute
statement the engine namespace named e.g. net_CreateTexture, pointing to the function for texture creation. This enables the capability of p/invoking the function from the c# side by knowing the export name from c++ (that may be obtained by using dumpbin to the compiled c++ library) in the way






[DllImport("Engine.dll", EntryPoint = "?net_CreateTexture@TextureGenerator@FireEngine@@YAFF@Z", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateTexture(ref IntPtr target);
The behavior from this line of code is passing an IntPtr type from .net into the creation function on c++ side that will be filled and stored bointing to the texture object in native memory so youre able to call additional functions from .net passing back that pointer to c++ then performing the real opperation on it by be very fast in performance and you dont waste time by writing code twice (resource management, memory and so on). The function overhead is simply passing a pointer like type that was made and optimized to avoid the need of beeing marshalled by microsoft.

We found that be the best solution for our propose otherwise it would be a complex marshalling overhead needed on each side in .net and c++/cli for getting the native data into .net memory model and back to c++


You can do the same thing in C++/CLI, just with less work. And you don't have to manually fix up all your p/invoke code when you update your compiler because the name mangler changed - a problem that can be avoided if you use C as the interface layer instead of C++.

The real optimization here is using a integer as a handle to a C++ object and passing that around instead of trying to pass the C++ object across the barrier. (An integer copy is faster than most other types) Which is something you can do in both C++/CLI and p/invoke.

The obvious problem with the above solution is that .NET is treating the handle as a random integer, and so does not refcount it or have any way of controlling the lifetime of the C++ object, so you are going to have to be very careful with your lifetimes to make sure that it goes away when you want it to, but not before, and the your C# code can clean up the integer.

One way to avoid the above issue is to write a C# wrapper around the handle using the IDisposable pattern that basically tells the C++ code to addref/release every time a wrapper object is created/destroyed/copied/moved.
One way to avoid the above issue is to write a C# wrapper around the handle using the IDisposable pattern that basically tells the C++ code to addref/release every time a wrapper object is created/destroyed/copied/moved.

IDisposable is really not a good fit for wrapping reference counting behavior (we found this out the hard way building SlimDX). sad.png
The contract for IDisposable is such that you must Dispose() things you have "created" (that is, allocated with new or been given control over the lifetime of by the documentation of the API), but not things you are just currently holding a reference to. This makes it a poor fit for implemented AddRef/Release automatically, since you have to subvert the contract, which can cause subtle bugs down the road depending on the complexity of the actual objects themselves.

One way to avoid the above issue is to write a C# wrapper around the handle using the IDisposable pattern that basically tells the C++ code to addref/release every time a wrapper object is created/destroyed/copied/moved.


IDisposable is really not a good fit for wrapping reference counting behavior (we found this out the hard way building SlimDX). sad.png

The contract for IDisposable is such that you must Dispose() things you have "created" (that is, allocated with new or been given control over the lifetime of by the documentation of the API), but not things you are just currently holding a reference to. This makes it a poor fit for implemented AddRef/Release automatically, since you have to subvert the contract, which can cause subtle bugs down the road depending on the complexity of the actual objects themselves.


It's fine (and pretty much the best option you have in .NET) for wrapping external resources not managed by .NET (be that memory, files, OS handles, whatever). You don't make the IDisposable class itself do any reference counting (that's what the GC is for), but you can wrap a C++ ref-counted smart pointer inside the class, or call internal AddRef/Release with a handle.

C# does not have RAII or stack-based deallocation of objects. So using IDisposable with the "using" keyword is the closest you can get.

You can do the same thing in C++/CLI, just with less work. And you don't have to manually fix up all your p/invoke code when you update your compiler because the name mangler changed - a problem that can be avoided if you use C as the interface layer instead of C++.


We dont fix up any code because an auto generater tool does this for us depending on the dumpbin file generated out of our libraries.

The obvious problem with the above solution is that .NET is treating the handle as a random integer, and so does not refcount it or have any way of controlling the lifetime of the C++ object, so you are going to have to be very careful with your lifetimes to make sure that it goes away when you want it to, but not before, and the your C# code can clean up the integer.

One way to avoid the above issue is to write a C# wrapper around the handle using the IDisposable pattern that basically tells the C++ code to addref/release every time a wrapper object is created/destroyed/copied/moved.


Thats true but even such big players as Unity3D have to fight with this situation. Personaly we have wrapped the p/invoke code into an own assembly file that acts as bridge wrapper for the IDE Tool.

This topic is closed to new replies.

Advertisement