Creating a C++ native bridge DLL calling into C# classes and methods in a managed assembly using xInterop .NET Bridge
Before we start talking about the features of xInterop .NET Bridge can offer, we would like to explain the terminology of such a Bridge. There are different words which may be used to describe such a technology for accessing and calling the C# method from a native C++ application. C++ wrapper, C++ binding, C++ Bridge, you name it. Our bridge technology provides 2 way communication between C++ native world and the .NET managed world. A “Bridge” may better represent our technology and product, it creates wrapper or binding in both directions from C/C++ to .NET and from .NET to C/C++.
When we started design xInterop .NET Bridge, one of the important requirements is the process of creating the C++ Bridge must be automatic and very use-friendly to users/developers. For any existing .NET assembly, since the information of all classes is already embedded in the assembly as the metadata.
xInterop .NET Bridge is able to retrieve all the class information including methods, properties, fields, etc., analyze the metadata and then create a C++ DLL bridging to the .NET assembly by exporting all the corresponding C++ classes to the .NET classes in the managed world.
Once built, A C++ Bridge DLL automatically loads .NET run-time and the corresponding .NET assembly which it Bridges to. There is no method required to make that happen, it is all handled by xInterop .NET Bridge.
Let’s take an example of how we can create a C++ Bridge DLL allowing developers to call C# assembly from native C++.
Step 1, create a C# .NET library project called CalculatorDemo
The assembly contains a single class named Calculator whose implementation is shown below.
Build the C# project, we will have a CalculatorDemo.dll, a C# .NET assembly. Now let’s feed the .NET assembly to xInterop .NET Bridge.
Step 2, Create xInterop C++ Bridge to C# assembly
Right click on the menu of “Create New C++ Bridge Projectâ€¦” and bring up the following window for further configuration.
- .NET assembly
It is the path to the file of .NET assembly which shall be Bridged to the C++ native DLL.
- Platform Toolset
It is the compiler and also targeting the C++ runtime, depending on the Visual Studio installed on the local machine, the following versions of Visual Studio are supported.
Visual Studio 2013
Visual Studio 2012
Visual Studio 2010
It will be possible to support Visual Studio 2008 and Visual Studio 2005, Since we are building new C++ DLL, developers may prefer to using newer version of Visual Studio compiler. So both of Visual Studio 2008 and 2005 are currently not supported.
- CPU Architecture
Either x86 or x64 version of the C++ Bridge DLL can be created and built. When AnyCPU is used, both of DLLs shall be created.
Unicode or ANSI, this defines the character encoding used in the C++ Bridge DLL, it is the same setting of a Visual Studio C++ project.
- Use Smart Pointer
When using C++ smart pointers, any pointer of an instance will be wrapped in a C++ smart pointer, for capability with SWIG, shared_ptr is used for smart pointer. This is to make sure that any memory allocated when calling the .NET assembly get freed when they are going out of scope.
- Undefine method name
A name of C# method, property, field may has conflict with existing C++ macro defined, “Undefine method name” can make sure the existing C++ macro undefine before defining the method using the same name.
- Export interface
Any .NET interface can be Bridged to a C++ native DLL by creating a C++ Bridge class to wrap all the methods of any .NET interface. If such interfaces are not desired, they are not necessarily needed to be Bridged and exported since creating additional C++ Bridge class increases the size of the resulting C++ Bridge DLL.
- Show build information
When the option is checked and enabled, there will be information printed out in the output window when the building process is going on.
- Load .NET assembly automatically
The .NET assembly can be loaded by calling xiInitializeBridgeAssembly, it can also be loaded automatically when any of the C++ Bridge class is accessed. When this option is enabled, the .NET assembly must reside in the same directory when the C++ Bridge DLL is located.
- Create testing application
For the convenience of setting up the C++ application along with the C# Bridge assembly, xInterop .NET Bridge creates a C++ console application(a visual studio solution) for testing when this option is enabled.
- Number of iteration
There are lots of C# .NET classes referenced in a single .NET assembly directly or indirectly. Creating C++ Bridge classes for all the referenced .NET class is not necessary and it creates a C++ Bridge DLL with huge size. The number of iteration limits the loop which searches for .NET classes referenced by classes, methods, properties and fields. The default is 0 which means directly referenced by the .NET assembly.
- Depth of base types
The .NET assembly contains classes defined in itself, it may also be derived from .NET classes defined in other assembly, such as the runtime, the depth of base types is to instruct how xInterop should create C++ Bridge class for those base classes. The default is zero, which means no C++ Bridge classes should be created for any of the base class. 1 means only the direct base class shall have a C++ Bridge class.
- Output directory
This is the root directory where the C++ Bridge DLL project and the testing application project shall be created.
Once the C++ Bridge generating process is done. You should find very pleasant messages printed in the output window as shown below
Basically speaking, xInterop .NET Bridge creates a C# Bridge assembly project which builds a C# Bridge managed assembly. It also creates C++ Bridge native DLL with a header file so that the DLL can be referenced from your native C++ application. The C++ Bridge DLL access the original .NET assembly through the intermediate C# Bridge assembly. The C# Bridge assembly is a C# wrapper for the C++ Bridge DLL with additional features added.
The solution explorer contains the C# .NET Bridge assembly project which is the result of wrapping the C++ Bridge native DLL as shown in the screenshot below.
If you have selected “Create Testing Application”, you should be able simply open the directory where is the solution file is located by double-clicking the link.
Step 3, writing the C++ console testing application
It you open the TestApp.sln solution, there will be 2 projects shown up in the solution folder.
The TestApp is already set as the Startup application, it does not directly reference the C# Bridge assembly project because it can not. It does depend on the C# Bridge assembly project, and it will copy all the necessary DLL and assembly into it is $(Configuration) folder(Debug/Release) so that it can load the DLL/assembly at runtime.
If you open the TestApp.cpp, you will find all the header file and lib file are already included or linked programmatically.
Okay, both the C++ native DLL and the C# Bridge assembly are ready for use, we can now implement the C++ console application by simply adding a few lines.
And let’s look at how the C++ Calculator is defined in the header file of CalculatorDemoBridge.h.
As you can see, the C++ Bridge class of Calculator mimic the exact interface of the corresponding C# Calculator. The C++ Calculator is derived from NObject C++ class which represents the .NET System.Object class.
We have the declaration of the C++ NObject shown below.
You may have noticed that NObject implements all the methods of System.Object class except GetType method which will be implemented in a future version. The method of GetType may be useful when using .NET reflection from the native C++ application.
This is a very simple demonstration showing what xInterop .NET Bridge can offer. It only shows a little bit of the features of xInterop .NET Bridge. The little sample application is able to demonstrate how you can use xInterop .NET Bridge create the C++ Bridge DLL and the C# Bridge assembly.
In the future posts, we will describe more how this works and how you can create C++ Bridge native DLL for calling more complicated .NET assembly.