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
!