COM
The idea is to make a class or several classes available thru COM. Then the compiled dll or the TLB is used to generate and Interop Assembly and call the desired functions.
With this solution the current C++ code base line can be kept or might require just subtle changes.
Calling a function thru com is involved in a lot of marshalling and can add an additional layer that is not really needed in the architecture of the solution.
Creating a Managed Wrapper with Managed C++
The idea with this scenario is to provide a class in Managed C++ that will be available in C#. This class is just a thin proxy that redirects calls to the Managed object.
Let’s see the following example:
If we have a couple of unmanaged classes like:
class Shape {
public:
Shape() {
nshapes++;
}
virtual ~Shape() {
nshapes--;
};
double x, y;
void move(double dx, double dy);
virtual double area(void) = 0;
virtual double perimeter(void) = 0;
static int nshapes;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) { };
virtual double area(void);
virtual double perimeter(void);
};
The first thing we can try, to expose our classes to .NET it to set the setting for managed compilation:
If your project compiles then you are just very close, and what you need is to add some managed classes to your C++ project to expose your native classes:
Let’s see the Shape class:
//We can use another namespace, to avoid name collition.
//In this way we can replicate the structure of our C++ classes.
namespace exposedToNET
{
//Shape is an abstract class so the better thing
// to do is to generate an interface
public interface class Shape : IDisposable
{
public:
//public variables must be exposed as properties
property double x
{
double get();
void set(double value);
}
property double y
{
double get();
void set(double value);
}
//method do not expose any problems
void move(double dx, double dy);
double area();
double perimeter();
//public static variables must
static property int nshapes;
};
//Static methods or variables of abstract class are added here
public ref class Shape_Methods
{
//public static variables must be exposed as static properties
public:
static property int nshapes
{
int get()
{
return ::Shape::nshapes;
}
void set(int value)
{
::Shape::nshapes = value;
}
}
};
}
And for the Circle class we will have something like this:
namespace exposedToNET
{
public ref class Circle : Shape
{
private:
::Circle* c;
public:
Circle(double radius)
{
c = new ::Circle(radius);
}
~Circle()
{
delete c;
}
//public variables must be exposed as properties
property double x
{
virtual double get()
{
return c->x;
}
virtual void set(double value)
{
c->x = value;
}
}
property double y
{
virtual double get()
{
return c->y;
}
virtual void set(double value)
{
c->y = value;
}
}
//method do not expose any problems
virtual void move(double dx, double dy)
{
return c->move(dx,dy);
}
virtual double area()
{
return c->area();
}
virtual double perimeter()
{
return c->perimeter();
}
//public static variables must be exposed as static properties
static property int nshapes
{
int get()
{
return ::Shape::nshapes;
}
void set(int value)
{
::Shape::nshapes = value;
}
}
};
}
DOWNLOAD EXAMPLE CODE
SWIG
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages.
This is a great tool used for several languages like Python, Perl, Ruby, Scheme, and even in different platforms.
The exposure mechanism used in this scheme is platform invoke, the issues here are similar to those of COM because there is some marshaling going on. This scheme might be more efficient than the COM one but I haven’t really test it to be completely sure that it is better.
I have reviewed the SWIG code and it might also be possible to modify its code to generate wrappers using managed C++, but this is an interesting exercise that I have to leave for my readers. Sorry I just don’t have enough time.
But how is SWIG used?
In SWIG what you do is that you add a .i file to your project. This file provides directives for some code generation that specify exactly what you want to expose and how.
This can very helpful if you just want to expose some methods.
If you are lazy like me you can just add something like:
/* File : example.i */
%module example
%{
#include "example.h" ß you put here includes with the definitions for your classes
%}
/* Let's just grab the original header file here */
%include "example.h" ß add alse the include here
And SWIG will add a file like example_wrap.cxx that you have to compile with the rest of your C++ code.
It will also generate a set of C# classes that you use in your C# application, so it seams to your program that all the code is just C#.
SWIG is a great tool and has been testing in a lot of platforms.