This post is here to help you start consuming C++ code from C# using C++ CLI.

C++ CLI is a comprehensive technology for managed-to-native interoperability. It’s not quite the Bat Mobile but it is a cool gadget to have in your utility belt.

The application we are going to build will consist of three parts:

  1. Internal implementation written in C++
  2. User-interface based on WPF and C#
  3. Intermediate layer written in C++ CLI

Let’s get started and lay out the basic project structure:

The basic layer is our C++ implementation:

// MeaningOfLife.Cpp/Logic.h
#pragma once

namespace MeaningOfLife
{
    namespace Cpp
    {
        // This is our native implementation
        // It's marked with __declspec(dllexport) 
        // to be visible from outside the DLL boundaries
        class __declspec(dllexport) Logic
        {
        public:
            int Get() const; // That's where our code goes
        };
    }
}

// MeaningOfLife.Cpp/Logic.cpp
#include "Logic.h"

int MeaningOfLife::Cpp::Logic::Get() const
{
    return 42; // Really, what else did you expect?
}

Next, is our wrapper project. In order to create a C++ CLI project, all you need to do is to make an empty C++ project and enable Common Language Runtime Support:

Here is our wrapper class:

// MeaningOfLife.Cpp.CLI/Logic.h
#pragma once

namespace MeaningOfLife
{
    namespace Cpp
    {
        // First a Forward Declaration to Cpp::Logic class:
        class Logic; // This allows us to mention it in this header file
        // without actually including the native version of Logic.h

        namespace CLI
        {
            // Next is the managed wrapper of Logic:
            public ref class Logic
            {
            public:
                // Managed wrappers are generally less concerned 
                // with copy constructors and operators, since .NET will
                // not call them most of the time.
                // The methods that do actually matter are:
                // The constructor, the "destructor" and the finalizer
                Logic();
                ~Logic();
                !Logic();

                int Get();

                void Destroy(); // Helper function
            private:
                // Pointer to our implementation
                Cpp::Logic* _impl;
            };
        }
    }    
}

// MeaningOfLife.Cpp.CLI/Logic.cpp
#include "Logic.h"
#include "..\MeaningOfLife.Cpp\Logic.h"

using namespace std;

MeaningOfLife::Cpp::CLI::Logic::Logic()
    : _impl(new Cpp::Logic()) 
    // Allocate some memory for the native implementation
{
}

int MeaningOfLife::Cpp::CLI::Logic::Get()
{
    return _impl->Get(); // Call native Get
}

void MeaningOfLife::Cpp::CLI::Logic::Destroy()
{
    if (_impl != nullptr)
    {
        delete _impl;
        _impl = nullptr;
    }
}

MeaningOfLife::Cpp::CLI::Logic::~Logic()
{
    // C++ CLI compiler will automaticly make all ref classes implement IDisposable.
    // The default implementation will invoke this method + call GC.SuspendFinalize.
    Destroy(); // Clean-up any native resources 
}

MeaningOfLife::Cpp::CLI::Logic::!Logic()
{
    // This is the finalizer
    // It's essentially a fail-safe, and will get called
    // in case Logic was not used inside a using block.
    Destroy(); // Clean-up any native resources 
}

And on top of it all is the UI:

<Window x:Class="MeaningOfLife.WPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MeaningOfLife.WPF"
        mc:Ignorable="d"
        SizeToContent="WidthAndHeight"
        WindowStyle="ToolWindow"
        WindowStartupLocation="CenterScreen"
        Title="Example">
    <Grid>
        <Button Content="Click me!" Padding="20" Click="Button_Click" />
    </Grid>
</Window>
// MainWindow.cs
..
private void Button_Click(object sender, RoutedEventArgs e)
{
    using (var wrapper = new Logic()) 
    // Using block is here to make sure we release native memory right away
    {
        MessageBox.Show("The answer is " + wrapper.Get());
    }
}

The result:

There is a couple of small things you should check:

  • Make sure you are compiling WPF application not as Any CPU. You can go either x86 or x64 as long as you remain consistent with your other projects. The problem with Any CPU is that you are essentially saying “I don’t care” and you might end up with the wrong framework platform at run-time.
  • In case there are bugs in your native code (and there are always bugs in any code), you need a way to debug into it:

Now we are ready to hit the button:

What causes the problem is the relative location of our output files. Default output destination for native projects is ../Debug:

In general, C++ solutions declare one shared output directory at the solution level. C# projects, on the other hand, place all their outputs in folders under project folder:

Note that MeaningOfLife.Cpp.CLI.dll is both in managed and native output directories. C++ CLI is placing the library together with its native counterpart, but our WPF application is aware of its managed dependencies and is managing to copy them along.

This is a common problem that can be solved by manually dropping the missing file together with the rest. This can, however, be a real pain. For example, if your managed library is hosted on the IIS, the server will copy your files to some obscure directory before loading them. And just like before, all native dependencies will be left behind. The problem is commonly “solved” by modifying PATH environment variable. I suggest an alternative.

Let’s inspect MeaningOfLife.Cpp.CLI.dll using the Dependency Walker (depends.exe):

You can see that MeaningOfLife.Cpp.dll is the only missing dependency without an hourglass next to it and it’s the only one generating an error.

What is keeping the other unresolved dependencies from crashing the program is the fact that they are Delay-Loaded. This means they will not be looked-up until their functionality is actually needed.

How do you communicate your intent to load certain dependency late? Using a linker flag on MeaningOfLife.Cpp.CLI:

Just add the file-name you want to delay and re-compile. (You would generally use Visual Studio Macros to make sure you get the names right)

Now when we refresh the dependency walker no errors are being reported:

You must be asking “How does delay-loading help me, exactly?”. At the moment all it’s doing is making the program crash one millisecond later. But here is the catch: since dependencies are no longer enforced at load time, we can load the library manually from the location of our choosing. Here is how:

// First, let's add a function to our wrapper class:
static void InitializeLibrary(System::String^ path);

// MeaningOfLife.Cpp.CLI/Logic.cpp:
#include <string>
#include <Windows.h>

using namespace std;

..

void MeaningOfLife::Cpp::CLI::Logic::InitializeLibrary(System::String^ path)
{
    string nativePath = ManagedStringToStdString(path);
    LoadLibrary(nativePath.c_str()); // Actually load the delayed 
                                     // library from specific location
}

Next, we’ll add some code to locate and load the library:

private void Button_Click(object sender, RoutedEventArgs e)
{
    var fileOpenDialog = new OpenFileDialog
    {
        CheckFileExists = true,
        Filter = "Native Library|MeaningOfLife.Cpp.dll",
        InitialDirectory = Environment.CurrentDirectory
    };

    var result = fileOpenDialog.ShowDialog(this);
    if (result.HasValue && result.Value)
    {
        Logic.InitializeLibrary(fileOpenDialog.FileName);

        using (var wrapper = new Logic())
        {
            MessageBox.Show("The answer is " + wrapper.Get());
        }
    }
}

This is of course a contrived example, the actual logic can range from using a hard-coded constant, to reading from config, to querying a database.

Source code is avialable here