by Wojciech Sura

Exporting classes from C++ to C#

Sometimes it happens, that a very useful piece of code is written in C++ and we would like to use it in C#. C++/CLI is one option, but sometimes even this solution can’t be used. In such case the only option left is to export required functionality through a DLL file.

Unfortunately, there’s a problem: DLLs were invented, when C was one of leading languages and they’re designed in such way, that even C programs may use them as well. That means: no object-oriented programming! (by the way, that’s one of the reasons of the creation of .NET)

The core of my ProCalc program is written in C++, but the user interface is written in C#, so I had to solve exactly this kind of problem. Fortunately, there’s a quite simple solution, which allows transferring objects through the DLL’s interface.

Let’s do a case study. The actual C++ class looks like the following:
[cpp]class Engine
{
// (…)
private:
ProCalcCore::Core * core;

public:
Engine();
~Engine();

double GetVariableFloatValue(const char * name);
// (…)
};[/cpp]
In order to pass this class through DLL’s interface, I flattened its constructor and destructor in the following way:
[cpp]__declspec(dllexport) BOOL __stdcall Engine_Create(ProCalcEngine::Engine * & instance)
{
instance = new ProCalcEngine::Engine();

return TRUE;
}

__declspec(dllexport) BOOL __stdcall Engine_Destroy(ProCalcEngine::Engine * instance)
{
if (instance == NULL)
return FALSE;

delete instance;

return TRUE;
}[/cpp]
So if you want to instantiate that specific class, you’ll have to call Engine_Create function, passing a reference to a pointer through the parameter – and the function will fill that parameter with a pointer to class instance there. The actual value of that pointer is irrelevant – you only have to keep it to reference that specific instance while calling other DLL functions. Methods are implemented in a very similar way:
[cpp]__declspec(dllexport) BOOL __stdcall Engine_GetVariableFloatValue(ProCalcEngine::Engine * instance,
char * name,
double & value)
{
if (instance == NULL)
return FALSE;

try
{
value = instance->GetVariableFloatValue(name);

return TRUE;
}
catch (…)
{
return FALSE;
}
}[/cpp]
Now let’s wrap all these functions in a C# class, such that we can interface with this native class in object-oriented manner in C#.

First of all, we have to import all necessary functions from the DLL to C#.
[csharp]public class Engine : IDisposable
{
protected class Native
{
[DllImport("ProCalc.Engine.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Engine_Create(ref IntPtr newInstance);

[DllImport("ProCalc.Engine.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Engine_Destroy(IntPtr instance);

[DllImport("ProCalc.Engine.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Engine_GetVariableFloatValue(IntPtr instance,
[MarshalAs(UnmanagedType.LPStr)] string name,
out double value);
// (…)
}

// (…)
[/csharp]
Now we can prepare this class to work with DLL internally, but provide a regular C# interface outside (such that user of this class won’t even know, that it is not 100% managed):
[csharp] private IntPtr instance = IntPtr.Zero;

public Engine()
{
if (!Native.Engine_Create(ref instance))
throw new InvalidOperationException("Internal error: cannot create instance of ProCalc engine!");
}

public void Dispose()
{
if (!Native.Engine_Destroy(instance))
throw new InvalidOperationException("Internal error: cannot destroy instance of ProCalc engine!");

instance = IntPtr.Zero;
}

public double GetVariableFloatValue(string name)
{
double result;
if (!Native.Engine_GetVariableFloatValue(instance, name, out result))
throw new InvalidOperationException("Internal error: Cannot get variable value!");

return result;
}

// (…)
}[/csharp]
Bonus chatter: I’m generally not a big fond of using the underscore character in names of identifiers. However, it seemed nice to name functions exported from the DLL, for instance, Engine_GetVariableFloatValue, because they nicely resemble the C++ syntax: Engine::GetVariableFloatValue.

This method simplifies keeping order in all functions exported by the DLL.