Create C++ DLL for .NET

August 13, 2010 12:00

I recently faced a situation while working on a current VB project, in which I really felt like by utilizing lower level code, I might be able to improve the efficiency of my program. I mean, bigger companies can produce their own DLL's in C++ and other languages for me to use, why couldn't I? I immediately began looking online for little tutorials on how to create such a DLL to be read by VB.net.

And I looked...and I looked...and found nothing concrete. I mean nothing. Sure there were little helpful forum posts and whatnot, but nothing out there that said "This is how you do it". After fighting with my computer and Visual Studio for a little bit, I was successfully able to create a DLL that VB.net can read well. The interesting thing about the whole process is that it's not difficult at all. A single class is all we'll need to create. The problem is that there are a few severe problems that can arise, that, if you don't know how to deal with them, it really is impossible to continue. I'll try my best to explain any problems that may arise at each step, and how to fix them.

So let's get to it, shall we?

Let's start by opening up Visual Studio, and creating a new Visual C++ Project. We're going to want a Win32 Project. This type of project is going to allow us to turn our code into a DLL file. At the Application Settings window, the Application type is going to be 'DLL' and under Additional options, select Empty Project. Next, we'll want to add our first, and only, .cpp file. Go to Project > Add Class and select a C++ class. Being the creative people we are, let's name it 'TheDLL'. You'll now be looking at some auto-generated code. We won't be using that so feel free to delete it.

Now, let's talk about how we're going to want to implement this DLL. For the sake of example, we're going to write three functions in our DLL. One that returns the number 5, one that returns the String "five", and a final one that takes one integer as input and returns the square root. That way, we'll touch base on a little of everything, and you'll be able to roughly see how everything works in C++ DLLs.

So let's begin with some actual code. Let's start with a simple function that returns '5' as an integer, along the lines of

int returnFiveAsInteger() {
	return 5;
}
But written in a way that will be written into a DLL, and be able to be read by VB.net. To do so, we preface the function with a fun little set of keywords "extern "C" __declspec(dllexport)". This leaves the function looking like this:
extern "C" __declspec(dllexport) int returnFiveAsInteger() {
	return 5;
}
The beginning section "extern "C" tells us that the following function will be written in C code. But wait a minute. C code? I thought this was C++? It is, but if we were to write extern "C++" we would encounter some fun little problems, known as name mangling. When our DLL is compiled, the functions' names need to be maintained so that our external program will be able to call them normally. If we were to use extern "C++", we encounter this fun little bug of name mangling. Instead of the normal name of the function, the function would be renamed, and without going in a little deeper into what happens behind the scenes, there really is no way of knowing what the name of the function has been saved as. So, let's stick to telling the compiler we're only going to be using C code in these external DLL functions.

Now, let's put in a function that will return "five" as a string.
extern "C" __declspec(dllexport)  char* returnFiveAsString() {
	return "five";
}
Now for a function that takes in an integer as a paramter, and returns the square root. For kicks and giggles, we're going to be using an extra method call to get it done.
extern "C" __declspec(dllexport) double getRoot(int numberPassed) {
	return getSquareRoot(numberPassed);
}
double getSquareRoot(int numberPassed) {
	return sqrt((double(numberPassed)));
}
You'll notice that the getRoot function does not have the extern '"C" __declspec(dllexport)' line before it. As a result, the external code that is reading this DLL will NOT have access to this method, nor will it even know it exists. As well, there is no reason to worry about name mangling, and we can freely put our C++ code in here.

At this point, we're just about done putting together our DLL. The finished file will look something like this.
#include <cmath>

double getSquareRoot(int);

using namespace std;

extern "C" __declspec(dllexport) int returnFiveAsInteger() {
	return 5;
}
extern "C" __declspec(dllexport)  char* returnFiveAsString() {
	return "five";
}
extern "C" __declspec(dllexport) double getRoot(int numberPassed) {
	return getSquareRoot(numberPassed);
}
double getSquareRoot(int numberPassed) {
	return sqrt((double(numberPassed)));
}

Notice how the only function being declared at the beginning is the one not being imported. This will always be the case. If every single one of our methods were meant to be seen by the external program, we wouldn't have a single method declaration up there. Now, we're ready to create our DLL. Go to Build > Build TheDLL (or whatever it was you decided to name your project). There we are. You now have a DLL made from this small C++ file.

Reading Your DLL from VB.net

Create a new project in Visual Studio, specifically a VB Windows Forms Application. Go into the code view of the program. First thing we'll want to do is remember to stick in some imports.
Imports System
Imports System.Runtime.InteropServices

The InteropServices import is what we're going to be using to read our DLL. Now, we'll want to declare every single function at the beginning of our class that we want to import, and what it would look like if it were in VB. So, for example, the method that returns 5 as an int must be declared like so:

<DllImport("TheDLL.dll")> _
Public Shared Function returnFiveAsInteger() As Integer
End Function
the <DllImport("TheDLL.dll")> is what tells the program where to look to find the method declared immediately following, in this case, the method returnFiveAsInteger. We leave the body of the function blank. The compiler will fill that in for us. Repeat these steps for all of our methods. Now, in our Form1_Load event, let's go ahead and call these functions and print out a messageBox with the results.

So, here's what our VB.net class looks like:
Imports System
Imports System.Runtime.InteropServices

Public Class Form1
    <DllImport("WIN32DLL.dll")> _
    Public Shared Function returnFiveAsInteger() As Integer
    End Function

    <DllImport("WIN32DLL.dll")> _
    Public Shared Function returnFiveAsString() As String
    End Function

    <DllImport("WIN32DLL.dll")> _
    Public Shared Function getRoot(ByVal number As Integer) As Double
    End Function

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim intFive As Integer = returnFiveAsInteger()
        Dim stringFive As String = returnFiveAsString()
        Dim rootOfTen As Double = getRoot(10)
        MessageBox.Show(intFive & vbCrLf & stringFive & vbCrLf & rootOfTen)
    End Sub
End Class

Now, we need to something with that DLL file we build. Let's make it easy on the life of the compiler and manually copy and paste the DLL into the debug library of our VB project. Firstly, make sure you've built the VB program. Now, we'll want to go back, grab the .dll file our C++ file created, and paste into the debug folder of our VB project. You'll find the .dll in the debug folder of your C++ project. Copy and paste that file into the debug folder of your VB project. Voila! We're ready to compile and run this program.

Unfortunately, when you try to compile, the compiler might yell at you about a PInvokeStackImbalance. If this is the case, it means the signatures of the imported method don't match with what you're trying to pass to it. In our case though, the signatures do indeed match. To resolve the issue, go back to your DLL file, and modify the getRoot function like so:

extern "C" __declspec(dllexport) double __stdcall getRoot(int numberPassed) {
	return getSquareRoot(numberPassed);
}

And that should do it. Rebuild the DLL, copy it over to your project directory, run the program, and you should see a message box pop up with the correct values.

Got something to say? Tell me!

Name*

Homepage

Comment*



Aug.8.2011
18:02

Sam Marrocco

Very helpful tutorial, thanks!
Possible issues for you to look at: Your vb.net complete example code reads import "WIN32DLL.dll". I'm thinking you meant that to be "TheDLL.dll" in order for the example to compile correctly.

It would also be useful to to know why/more details about the PInvokeStackImbalance issue, rather than just how to resolve it. Why "might" you get the error--as opposed to definitely getting the error.

Thanks again!


Oct.18.2011
00:12

Johnson Lu

Really appreciate your tutorial! this is the easiest solution I have seen on the web so far for calling VC++2010 DLL with VB2010.
I have no much VB experience, but there was a urgent integration task, your posting really helps me a lot!


Oct.20.2011
07:46

Katherine Moss

Thank you so much. Your essay was clear and worked perfectly (after I changed the DLL declarations to _, someone else suggested.) I too had spent an entire day wandering around the internet looking for an example on point. Thanks again.

Katherine


Nov.6.2011
04:54

Janani

Could You please elaborate on the usage of Values entered in Textboxes in VB forms, inside C++ functions?

Btw, A very helpful tutorial! Way to go!

Thanks Again! :)