Calling a C++ DLL from C#

2010-01-18

If I’m going to borrow some C++ code for a folder-picker dialog box, then first I have to figure out how to write a C++ DLL that is useable from C#. Microsoft has a nice tutorial about creating and using DLLs, but it’s in pure C++. “No problem,” I thought. “I know how to use P/Invoke.” Well, it turns out that it’s not so easy.

To keep things simple, I figured I’d write an app to do math, as in that MS tutorial. So I used Visual Studio to create a C# console application, and I wrote this code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;


namespace MixedDll
{
    class Program
    {
        [DllImport("MathDll.dll")]
        static extern int Add(int a, int b);

        static void Main(string[] args)
        {
            Console.WriteLine(Add(21, 21));
            Console.ReadLine();
        }
    }
}

As you can see, I’m planning to write a dll called MathDll.dll, which will export one function: int Add(int a, int b). This requires adding a second project to our solution. In Visual Studio, each “solution” can have multiple “projects,” where each project builds some artifact like a dll or exe. As far as I can tell, you may only use one language within a given project.

Following the MS tutorial, I chose to create an empty C++ project rather than following any of the templates. Then in the settings (under Configuration Properties:General), I changed Configuration Type from .exe to .dll. I also added support for the Common Language Runtime (/clr). Then I added two files. First was MathDll.h:

extern "C" {
    __declspec(dllexport) int Add(int a, int b);
}

The second file was MathDll.cpp:

#include "MathDll.h"

extern int Add(int a, int b) {
    return a + b;
}

Finally, I added a reference from the C# project to the C++ project. In the Solution Explorer on the right, I right-clicked on References under MixedDll. Then I went to the Projects tab and selected MathDll. Oops! Visual Studio wouldn’t let me add the reference! Everything built, but I couldn’t get the IDE to supply access to the dll at runtime. Well, I figured I could do that myself. So after building everything, I manually copied MathDll.dll to MixedDll/bin/Debug. That’s where MixedDll.exe gets built, so I figured it should be able to pick up the dll.

Unfortunately, my little hack didn’t work. When I ran the program, I got a BadImageFormatException. It turns out this is what happens when you mix 32-bit and 64-bit binaries. As far as I can tell, a 32-bit exe can only use 32-bit dlls, and a 64-bit exe can only use 64-bit dlls. My C++ project was building as 32-bit, but my C# app was building as “Any CPU” (under Properties:Build:Platform Target), which I guess results in a 64-bit product.

The right solution would have been to build a 64-bit dll, but for some reason Visual Studio wasn’t giving me that option. So I switched the C# app to 32-bit and tried again. This time I was able to add the reference (no more kludgy manual copy)! When I rebuilt everything and ran the program, I saw my output: 42!

blog comments powered by Disqus Prev: Hello world! Next: Perseus Installation Directions