Managed-Unmanaged Interoperability in the 2010s: Part 3, C++/CLI and Other Options

January 30, 2012

tags:
no comments

What if P/Invoke is not enough? Hold on, why should it not be enough? What needs could we possibly have but calling global C-style functions exported from DLLs?

Well, suppose you want to use a C++ class exported from a DLL. Or maybe the C++ class is not yet exported and you are looking for a way to make it available to managed code. The problem with a C++ class as opposed to a global function is that … it is not a global function.

For example, a typical C++ class with a constructor and a couple of instance methods would be compiled to roughly the following when you look at it as a bunch of C-style calls:

//C++-style class declaration
class Klass {
private:
    int _n;
public:
    Klass(int n) : _n(n) {}
    void Work(int m) …
    virtual bool Sleep() …
};
//C++-style class usage
Klass k = new Klass(42);
k->Work(13);
bool b = k->Sleep();

//C-style class declaration
struct Klass {
    void* vfptr[1];
    int _n;
};
bool __Klass_Sleep() …
void __Klass_Ctor(Klass* pThis, int n) {
    pThis->vfptr[0] = &__Klass_Sleep;
    pThis->_n = n;
}
void __Klass_Work(Klass* pThis, int m) …
//C-style class usage
Klass* k = (Klass*)malloc(sizeof(Klass));
__Klass_Ctor(k, 42);
__Klass_Work(k, 13);
bool b = ((bool(*)(Klass*))k->vfptr[0])();

What am I saying here? I’m saying that using a C++ class from P/Invoke signatures would entail mimicking the C-style class usage below – i.e. declaring a P/Invoke signature for the constructor, for the instance methods, and calling virtual methods through the virtual function table. This is clearly intractable.

C++/CLI offers a reasonable approach that bridges .NET and C++ interoperability in both directions, and allows both sides to be the initiator (unlike P/Invoke). It has been covered well elsewhere, and I have written a post on automating the reverse direction four years ago. Still, a brief recap is in order.

You create a new C++/CLI assembly that bridges the managed and unmanaged worlds. It can be a part of an existing C++ DLL (which will then contain parts compiled with /CLR), or a brand new one. In your new assembly, you can create three categories of types:

  1. Managed types – “ref class” or “value class”, which are fully privileged citizens of the managed world. These types can be exported from your assembly and form the façade to other managed code (e.g. C#).
  2. C++ classes compiled to IL – can be accessed directly by managed types within the same assembly, or by (3) below.
  3. C++ classes compiled to machine code – can be accessed directly by managed types within the same assembly, by (2) above, or by any native C++ code within the same DLL. They can also be exported from the DLL as C++ classes.

Now there are two interop directions:

  • Managed code calls into a managed type in the C++/CLI assembly, which calls into (2) or (3) or any other unmanaged API, including Win32.
  • Unmanaged code calls into (2) which calls into (1) or any managed type, including the whole .NET Framework.

For more information about C++/CLI, you can check out my post I mentioned above, and there’s plenty of good docs on MSDN and a book (which, frankly, I haven’t read).


Is there anything else? Any other options for interop in the 2010s? Well, as the matter of fact, there are quite a few:

COM Interop – managed code can very easily call COM objects and managed classes can act as COM objects very easily. This allows bidirectional interoperability, but means you have to learn COM (most probably ATL) to expose COM objects from the unmanaged world. Verdict: Ugh.

C++/CX – the language extensions for C++ (/ZW) that make consuming and creating Windows 8 components very easy from C++. Not surprisingly, C++/CX is based on COM, but makes it much easier to create COM objects, which can be subsequently consumed by managed code. Verdict: Perhaps a feasible alternative when Visual Studio 11 ships.

CXXI Mono’s new technology which extends gcc and creates an interoperability façade easily accessible from managed code. This works not only for global functions, like P/Invoke, but for complex C++ code as well. Verdict: This might just be the future.


I am posting short updates and links on Twitter as well as on this blog. You can follow me: @goldshtn

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*