A Practical Tutorial on C# – C++ Interop using (mostly) Standard C++

Sometimes you will find yourself wanting to call a C++ dll from C# or have part of your project written in C++ and the other part in C#. This tutorial is about how to call a native dll from C# in a straightforward manner and how to handle callbacks from C++ (please click on the pics to expand them).

While I was working the ‘medi-board’ project for University College London it was decided fairly early on that we needed some kind of interoperability between C# and C++. Specifically, we needed to achieve the following things:

  1. The native object needed to be used like any C# object (with a clean interface)
  2. We needed bi-directional data exchange between C++ and C#
  3. We needed a C# delegate function to be invoked as a callback function from C++

While there are some really good tutorials out there, I feel there is general lack of information on how to accomplish this in a simple, straightforward manner. I hope this tutorial can help clarify some things.

The first decision to make is whether to accomplish the majority of the interop functionality from the C# of the C++ side. We opted for the former because:

  • We wanted to use standard C++ and not rely on visual C++ as the native code had to be cross-platform.
  • We knew the C# code would never be used in any other interop and could thus be specific whereas specialising on the C++ side would eventually lead to having multiple specialisations in the code

First, it has to be noted that because Microsoft’s CLR provides such a rich set of functionality, there are many ways to accomplish interoperability between C# and C++. Anyone interested should have a look here Microsoft’s Tutorials on this.

Our starting point is a solution with the following two projects:

  •  ‘Interop’: A C# Console Application
  • ‘Library’: An empty Visual C++ project (It’s not going to visual C++ only but that’s what the template is called)

Which should look something like this in your visual studio (I’m using VS 2012 here):

1

Before we actually do some coding, let’s examine our general strategy. The idea is that there is a static interface both on the managed and the native side. The native wrapper exposes the object/functions we want to use in such a way that C# can understand them. The managed wrapper ‘imports’ that exposed C++ interface in a static way. Because we want to be able to use our C++ object like any C# object, we encapsulate the managed wrapper further in a standard C# class. It is important to note that with this approach C# has ownership of the C++ instance and hence is responsible for deleting it appropriately.

2

On the C++ side, we add a simple class called ‘NativeObject’ to test our code:

3

The implementation of this object is trivial for now:

4

Next, we need to create the native wrapper that exposes functionality so it will be visible in the DLL’s interface.

5

While most of this should look very familiar, note how we had to wrap the constructor and destructor of the object in some extra functions and that whenever we wish to invoke a member function we need to also specify the instance.

Also we had to explicitly specify linkage by using ‘extern “C”’. Those not interested in the details can safely skip this section. The C++ standard (I’m using version N3242) states in clause 7.5 that:

“All function types, function names with external linkage, and variable names with external linkage have a language linkage […]. The default language linkage of all function types, function names, and variable names is C++ language linkage. Two function types with different language linkages are distinct types even if they are otherwise identical.

2 Linkage (3.5) between C++ and non-C++ code fragments can be achieved using a linkage-specification:

linkage-specification:

extern string-literal { declaration-seqopt}

extern string-literal declaration

The string-literal indicates the required language linkage. This International Standard specifies the semantics for the string-literals “C” and “C++”. “

This is basically saying that for every function for which we do not explicitly specify another linkage, the compiler assumes that we meant “C++”, but that we have the option change this default. The reason why this matters for our interop is that specifying C-linkage means that the compiler is not allowed to do some stuff that other languages do not understand, most importantly ‘name-mangling’. If you’re overloading a function or have the same name declared in different namespaces, the compiler will eventually have to use both of them in the same parse tree and namespaces do not exist in that.

So in order to differentiate between those different names, additional information such as the namespace that name was declared in is encoded into the function name used internally.

As we will see below in order for the interop to work, C# has to know the exact name of the native function – so we cannot allow name mangling. (See IBM’s notes on this)

Using the ‘dumpbin’ utility (See Microsoft dumpbin’s help page) under windows, we can have a look at this in action. Open your ‘VS2012 x64 Native Tools Command Prompt’ and navigate to the directory .dll is in.

Then use the command “dumpbin /EXPORTS Library.dll” which will give you the dll exports. In a version with the ‘extern “C”’ deleted, the output looks like:

6

Note how the names are mangled. If I add the ‘extern “C”’ linkage specification again, the exports looks like:

7

This is exactly what we want and presents C# an interface it can understand.

The fact that we pass a pointer to every function of our wrapper highlights again that C# has ownership of our object handle. The implementation of these functions is therefore straightforward:

8

And that’s it – We’ve written all the code we need on the C++ side. In order to create the managed wrapper, we have to use .Net’s ‘platform invoke’ or p-invoke functionality (If you want to know more about it, read this article).

All we have to do to use it is to import ‘System.Runtime.InteropServices’ and provide functions that match exactly those defined in our native wrapper. Note how we can represent the NativeObject handles with the type ‘IntPtr’ here.

9

This is actually all we have to do to get the interop to work. However, our goal was to encapsulate this rather ugly static interface in a nice C# object. To do so, we just add another class called ManagedObject that has a private IntPtr and calls the static functions of ‘Managed_Wrapper’:

10

The only thing that is absolutely crucial here is that delete() is called before this object goes out of scope to prevent memory leaks. This is because garbage collection will only delete the IntPtr as it has no knowledge of that native instance it points to (and C# has no control over the native heap anyway).

In order to use this new class, consider the following example:

11

This will print 11 followed by 22 as expected.

In order to make this compile we will have to adjust the following settings:

C++:

  • Change the configuration type to .dll:

12

  • Change the Platform to x64 in the Configuration Manager:

13

C#:

  • Set platform target in the Project settings to x64

14

  • Make sure the set the C# project as the startup project that ‘Build’ is enabled in the configuration manager

If you attempt to run the code you will most likely receive this error:

15

This is because by default, C++ and C# projects have a different output directory for the files they create. To avoid having to copy stuff by hand it’s useful to use a post-build-event here, i.e. some simple operation that takes place after your code is compiled. In our case, we want to use it to copy the .dll to the correct directory, namely that one that our .exe file from the C# project is in.

To set this up navigate to the following section of the C++ project’s properties and edit the ‘command line’:

16

17

The command

xcopy /d /y “$(SolutionDir)x64\Debug\*.*” “$(SolutionDir)Interop\bin\Debug\”

Tells VS to copy all files from the first directory to the second if they are a ‘newer’ version of the ones currently in that directory.

Note that if you run into problems with this, please consider the following common sources of errors:

  • Your projects/folder hierarchy is different to mine
  • Configuration of 32/64bit does not match. It is vital to get this right because the pointers we are passing around have to come from the same address space
  • Spaces in Folder names (Yes, the system is fairly archaic)
  • If you’ve changed from debug to release, this command might fail if the ‘Release’ folder does not already exist, i.e. it won’t create folders for you

If you the program, it produces the following output as expected:

18

Finally, let’s consider a few special cases (I will use some code examples from the ‘medi-board’ project here:

Passing Arrays/Strings

I will show passing Strings as char arrays so this technique is applicable to all arrays. Again, there are ‘cleaner’ ways to do this when you’re writing visual C++ but we want to keep our C++ code as close to standard C++ as possible.

On the native side we simply have:

19

And in C#

20

Note how we pass the length separately so we know how long the array is on the C++ side (please note that this has the same security implications as C arrays). As an aside, a C# String as the ‘ToCharArray’ function that makes it really easy to use this method.

Callback functions

Callback functions are somewhat tricky to get right. Our approach was to register a function ptr with our native libraries that could then be used as a callback:

21

With ‘__event_callback’ being defined as:

22

We need to use __stdcall to make sure we know who cleans the stack after the function call. This is Windows specific feature (please read more about it here if you’re interested).

On the C# side, this gives us:

23

We always pass a delegate here to make a programmer able to use the C# function in a generic way. If this code seems strange, please make sure you have looked carefully at delegate functions first as this is the hardest part of understanding this bit of code. Everything else is merely using some pre-defined features of the CLR.

Hope this tutorials helps and let me know what you think! Many thx for reading 🙂