Introduction to the xInterop .NET bridge


With the .NET open source movement, there will be more and more .NET libraries available to the managed world, there will be also a trend of using .NET libraries from all other languages, not every one is willing to re-invent the wheels in their favorite languages.

The best language to bridge to the .NET world is C/C++. There are existing technologies which can be used to call into C# libraries.

1. C++/CLI

C++/CLI is a bridging language which can be used to integrate .NET libraries by creating C++/CLI bridging/wrapper classes being exposed to the native world. The effort of creating such C++/CLI bridging/wrapper class is still considerable if there are many .NET classes and methods, the amount of time spent on marshaling different data types depends on size of the library. Ultimately, using C++/CLI is a manual process and requires big effort and it is not error prone, in fact, one may make many errors if not being careful enough.

(1) Lots of work, it is less than re-writing the library, but still lots of work for manually trying to figure out how to marshal the data types.

2. Unmanaged Exports

By adding MSIL .export directive to the original .NET function(must be static) then reassemble into a mixed-mode DLL, Unmanaged Exports can export static functions. You can download the nuget package from here. The features provided by Unmanaged Exports is very limited, for one, you can only export static functions with limited data primitive type and string, secondly, you will have to write the corresponding C/C++ portion in order to call the exported static methods from the .NET assembly, developers must load the library dynamically by calling LoadLibrary because you won’t have a .lib file you can link to, you must also define the type of a function type which corresponding to each C# static method signature.

(1) Any .NET class can not be exposed to the native world directly.

(2) Developers are responsible for creating C/C++ function type and loading individual functions.

(3) .NET assembly must be pre-processed and made into mixed mode DLL.

3. COM

Developers can also create a .NET COM component to expose COM interface for the C/C++ code to access the .NET assembly via COM. All the traditional COM limitation applies here.

(1) NET COM component must be registered.

(2) Generic and Generic type instance can not be used in COM interface.

(3) Static methods of any class will have to be re-wrapped in a new class as instance methods.

(4) Existing .NET class can not be just exposed via interface.

4. Reverse PInvoke

Reverse PInvoke merely means it is the reversed platform invoke from native application to .NET managed assembly, it basically sets up the callback function from .NET by using delegate so that the native DLL can retrieve the function pointer and call back into the .NET assembly. There are a few links listed below, you may want to read it for further information.

(1) The features provided by Revers PInvoke is also very limited, only the data types supported by PInvoke may be supported by Reverse PInvoke. Array type is not supported, there is no way for the .NET to find out the size of an pointer passed from the native world without extra work.

(2) Only .NET static methods can be called by native application.

(3) .NET classes can not be exposed to native application via Reverse PInvoke.

(4) The type of function type corresponding to the .NET static methods must be defined by developers.

5. xInterop .NET bridge

What is xInterop .NET bridge.

It is an add-on feature to the existing xInterop NGen++ (which we will officially rename to xInterop .NET). xInterop .NET bridge automatically creates C++ DLL based on any given C# .NET assembly and export header files and lib file for the C++ DLL so that any native application can reference and use the DLL for further development. It supports the following features.

(1) Classes. It will bridge any classes and create corresponding C++ classes for accessing the .NET classes. All public methods, both static and instance methods can be called from the native world.

(2) Structs. It will bridge any classes and create corresponding C++ classes for accessing the .NET struct. All public methods, both static and instance methods can be called from the native world.

(3) Enums. It will create corresponding C++ enum for each C# .NET enum.

(4) Generic Instance Types. All public methods, both static and instance methods can be called from the native world.

(5) Event and Event Handlers.

(6) Properties

(7) Fields

(8) Inheritance architecture.

(9) Constants

(10) Interfaces

(11) Delegates

(12) All C++ classes are derived from NObject class so that the instance of any class can be referenced by using NObject.

much more.

We will detail each of the features in the next posts. Please continue reading.

How is nested class in native C++ handled by xInterop NGen++ ?

How is inner class in C++ wrapped in C# by xInterop NGen++?

In C++, an inner class or nested class is a class declared entirely within the body of another class, nesting class. Inner classes are basically a form of scope. C# supports inner class or nested class as well, so mapping a nested C++ class in a nesting C++ class to a nested C# wrapper class in a nesting C# wrapper class is doable in theory. But how?

The following native C++ code is a very simple example of declaring a nested class inside another class, a nesting C++ class. In the following example, the nested native C++ class is called NestedObject which is declared and implemented inside of the nesting C++ native class named SimpleObject.

xInterop NGen++ (A C# wrapper generator for the native C++ DLL) fully supports nested class, nested struct/structure, nested enum, even nested anonymous struct/structure and nested anonymous enum via pure Explicit P/Invoke. Calling into a native nested C++ class from C# .NET and accessing its method and fields has no difference of calling a regular native C++ class.

We always look into the issues which other code generators such as like SWIG (Simplified Wrapper and Interface Generator) and CXXI are facing while working on implementing and improving xInterop NGen++. CXXI is not very active as of today. SWIG does not really support nested classes or its support is limited from reading its document, but it does provide some workarounds with defining the nested class inside the nesting class where it is forward declared, which means the actual declaration and implementation should be defined outside of the class where the nested class is forward declared in order for SWIG to process it. On SWIG’s to-do-list of 2013 Google Summer of Code, the difficulty is marked as medium to hard. I am not an expert of SWIG implementation or using SWIG to create C# wrapper semi-automatically, but I think its difficulty may have something to do with the fact that SWIG’s underlying mechanism of wrapping C++ is another layer of C style DLL, which means SWIG must wrap the native C++ DLL in a C style DLL which then gets wrapped in C# by using Explicit P/Invoke.

When wrapping the native un-managed native C++ class residing in a native C++ DLL, xInterop NGen++ takes a different approach than SWIG. xInterop NGen++ does not try to wrap the original C++ native un-managed DLL in another C style DLL except that it needs to generate a supplement C++ DLL when any instantiated template class is not exported in the original C++ DLL in order to export the instantiated template class, xInterop NGen++ uses the native C++ DLL as it is. The C# .NET code generated by xInterop NGen++ operates on the pointer of C++ object/instance exactly like a traditional C++ application linking with that native C++ DLL based on the information of the memory layout and the method signatures of a C++ object, including both exported methods and virtual methods. The resulting C# wrapper class has exact same interface of the corresponding native C++ class does, which enables developers to easily convert a C++ application to a C# application when using the native C++ DLL via the generated C# wrapper library.

To xInterop NGen++, any native unmanaged C++ object is basically just a pointer pointing to the beginning of a specific block of memory in the unmanaged native world. The layout, virtual function table and the exported methods of the C++ object are all known to xInterop NGen++. An instance of a nested class is just another native pointer. So it does not matter if the class of the object is nested or not. Basically speaking, being nested or not is just a name scope of the class definition in the original C++ source code.

The way which xInterop NGen++ chose to handle the native nested C++ class is same as the way of handling other regular native C++ classes, a corresponding C# .NET wrapper class gets generated for each native nested C++ class. As for the name scope or namespace, the C# .NET wrapper class of the native nested C++ class will be put inside of the C# .NET wrapper class of the C++ nesting class where the native C++ nested class resides so that the original C++ class structure can be maintained.

I have listed the meta data and class definition of the C# .NET wrapper class of the native C++ nesting class along with nested class below.

If you look into the preceding code carefully, you would find that the structure of the interface of the C# nested inner class has no much difference than a regular top-level C# wrapper class, they are both derived from the same base C# class, the only difference is that the nested inner class is declared and implemented in another C# wrapper class like it does in C++. It is a difference on the name scope in C++ native world, it is also just the same difference on the name scope in the C# managed world, so they are perfectly matched in both worlds.

Translate »