C ++ Managed Shells for Legacy C ++ Libraries

We are looking at creating a .Net-called shell for some legacy C ++ libraries using managed C ++.

Everything looks pretty easy. Is there something we need to keep track of?

+6
c ++ managed-c ++
source share
5 answers

I found that in general it is quite easy to wrap some existing C ++ libraries in C ++ / CLI and encounter a relatively small number of errors. I remember:

  • Mixing unmanaged C ++ code and C ++ / CLI code in the same executable / DLL is a very bad idea. I ran into problems with competing memory managers during shutdown this way (mostly the .NET runtime and the normal C ++ runtime when they stepped on each other, when it came to clearing the memory at shutdown, which led to non-deterministic behavior in relation to which someone has freed). Instead of linking the old legacy C ++ library to the C ++ / CLI library, I created a DLL containing the legacy C ++ and linked it to the C ++ / CLI DLL, which solved the problem once and for all.
  • If your code uses enumerations, you must wrap them in the appropriate C ++ / CLI rename classes, otherwise other .NET languages ​​will not be able to see and use them.
  • C ++ / CLI objects can only contain pointers to unmanaged C ++ objects. Unfortunately, in some cases, this means that you have to create thin shells to process certain objects. My β€œfavorite” was that I had to either wrap boost :: shared_ptrs in this way (and thereby add another layer of indirection), or put them in shared_ptrs with zero deletes after crossing the .NET / native border. Nothing is very nice when you have to deal with APIs that use this type of construct. RAII does not cross this boundary, so be careful, you will have to spend some time setting it up to match the .NET method.
  • C ++ / CLI does not do multiple inheritance, so if your legacy library uses this, you may have to model it using interfaces, etc.
  • The internal marshalling code seems to be able to handle most POD conversions, but you will have a search / borrow code that converts std :: strings, etc. This code exists, a few minutes on Google should raise it (sorry, at the moment there are no links to them).
+6
source share

This is pretty straight forward and works well. It is much simpler than PInvoke.

The big thing you need to pay attention to is the lack of unmanaged members in your managed headers, including private members, method signatures, etc. It is possible that private members, which are pointers to managed types, are declarations for your classes.

Also keep an eye on the lifetime of the object. It is easy to introduce memory leaks, since many .NET programmers are not used to clean up after themselves. Make sure that all wrapper classes that you create are one-time if they contain pointers, and make sure that you delete them in managed code. The IDisposable syntax in managed C ++ is also weird, but it is in the docs.

Also, remember that every time you cross a managed / unmanaged border, you get a small hit, so try planning your interface accordingly. If something is called repeatedly in loops, it's probably best to move that loop across the border so that you only cross once. Don't worry too much about it, though, if you talk millions of calls.

This article goes the other way, but has some useful points.

Use our ManWrap library to get the best from .NET in Native C ++ Code

see also

Managed Code in Visual Studio 2005 and
Removing managed objects, wrapping around a library, and more

+2
source share

Just some of the problems we encountered:

  • Memory / Resource Lifecycle Management (GC / IDisposable vs. Destructors). I think this is well known, and Rob post has a few things about it, so I will not go into details here ...
  • String coding / decoding. If your own code is a UNICODE assembly, you don’t have to worry about that much, but if not, be careful with the encodings when converting between native strings and .Net Strings.
  • C ++ does not execute [Conditional ("Debugging")], which means Debug.Assert, Debug.Trace, etc. will also be called in release builds. Use traditional C ++ macros instead.
  • 64-bit support: .Net by default generates 32 or 64-bit code depending on the platform, but your own code will probably only be 32-bit ...
+1
source share

I’ll just add to what everyone already said

pin_ptr wch = PtrToStringChars (string); (where the string is System :: String)

will become your friend.

You cannot directly include an unmanaged class in a managed class, but you can put a pointer to an unmanaged class and add it to your constructor and delete it in your destructor.

I did not have the problems Timo Geusch mentioned when mixing C ++ and C ++ / CLI code in one DLL. My DLL uses both without problems and without problems.

C ++ / CLI is not complicated (if you know C ++ and .NET) and works fine.

+1
source share

As others say: 98% of the time when it works, its debugged and fast.

What I have encountered so far:

  • Do not compile all your old C ++ code as managed. There have been some articles suggesting that this will be useful, but usually it only happens more slowly.
  • Remember to catch unmanaged exceptions and restore them as managed.
  • If you use MFC, note that you cannot use .lib runtime, so you will also deploy MFC runtime.
  • OpenMP (thread library) will not work in C ++ / CLI.
  • We had some build problems in VS2005 when we made the C ++ / CLI dll dependent on the C # dll from our own code.

It even worked so well that I started writing C ++ / CLI code to run unit tests in C ++ code. NUnit / Resharper will happily find and run the unit test in the C ++ / CLI DLL, which can directly call your own code at ANY LEVEL, even check your template container classes.

+1
source share

All Articles