P/Invoke Refresher

Many older devs in .NET will know exactly what I’m talking about when I mention P/Invoke; a lot of newer devs, however, will very likely not. This is not because they’ve never been trained in it, or their college/university had gaps in their education; it’s simply because there’s really not much call for it to be used these days. Before I go on to explain what it is, however, you need a little bit of background knowledge…

Programming Windows Apps in the Pre-.NET Era

Windows as an operating system came about way before .NET or anything like it ever existed, and it started way back in 1985 with Windows version 1. Since version 1, we had Windows 2 and Windows 3 before the version that most people remember (Windows 3.11) was released in 1991. From there MS, as we now know, went on to produce Windows 95, Windows 98, Windows 2000, Windows XP, and so on.

.NET originally came onto the scene in late 2000, with the first version of Visual Studio .NET following in 2002. Prior to that time, software still had to be developed for the Windows operating system, and this was achieved by using a C++ compiler and the “Win32 API”. Which compiler you used was largely based on preference; dominant players at the time were Microsoft C++, Zortech C++, and Borland C++. There were others, of course, and there were even other languages, but the original Windows API was designed to be accessed from the C or C++ languages. During this pre-.NET era, a huge amount of software was written for the platform, and quite a lot of it still exists.

All of these strange, random DLL files you see hanging around in your Windows system directory, many of the DLL and EXE files you see in desktop application folders, were and indeed still are written in C++ directly against the underlying Windows API. Many businesses that have been going since the pre-.NET days still have internal business software that was written in compiled languages and is still in use today. All of this software, if it was written to follow Microsoft’s standards at the time, is actually still useable today.

Old Software in the New Era

DLL libraries and EXEs that were written using the older compiled languages typically “Exported” a number of functions. Often, DLL files would “export”, and EXE files would consume or “Import”; you can check this out even today. If you look on your Start menu, under your Visual Studio install, you should see somewhere there is a developer command prompt.

Open this prompt up and type:

dumpbin

You should see something like the contents of Figure 1:

PInvoke1
Figure 1: Viewing the dumpbin output

Now, browse to another folder where you have a DLL you can inspect (even though it’s read-only, PLEASE DON’T use main system DLL files in the Windows directory to experiment with). In my case, it’s a a DLL copied from one of my system folders to a safe location. That way, if I damaged anything accidentally I won’t kill my operating system.

I created a folder on my C drive called ‘test’, and then copied the file ‘c:\windows\system32\user32.dll’ into this new folder, as you can see in Figure 2:

PInvoke2
Figure 2: Contents of the ‘test’ file

If we now use our ‘dumpbin’ tool on this file, we can ask it for a list of “Exports”, like so:

dumpbin /exports user32.dll

You’ll see a LOT of output scroll past. It should end up looking something like the following:

PInvoke3
Figure 3: Viewing the output

Each of these entries that you see starting with three numbers and then a name are an exported function that you can call. In this case, user32 is actually one of the main Windows API DLL libraries, and quite a lot of the functionality contained in this lib is already made available to you in a managed environment via the core .NET assemblies. My intention here was to show you what actually lies under the hood.

If you scroll back through your command prompt window, you may see things that ring a bell of two, such as ‘SendMessage’ and ‘PeekMessage’. These are core functions called by code written that does not use the .NET runtime.

Thanks for the History Lesson, but Why Does This Matter to Me?

It matters more than you might think. Yes, you have MOST of the functionality available in .NET today, but there are still a few small places where the Win32 API contains potentially useful functions that just are not available in .NET. For the purposes here, though, I’m going to leave finding those as an exercise to the reader. Instead, I’m going to show you one scenario that’s still a very valid use in today’s modern development environment, and that’s protecting your intellectual property.

We all know just how easy it is to decompile and reverse engineer a .NET application. If you provide your software as a desktop application, everything buried inside that app is available for the taking. Tools such as reflector, dotpeek, and Just-Decompile will make mincemeat of any .NET-compiled assembly in a matter of seconds. Don’t forget, too, that although web applications may be a little more secure in this respect, all it takes is a mis-configured web server or disgruntled employee and it’s game-over.

One of the reasons the applications from the pre-.NET days were so difficult to pirate and extract secrets from was due to the way they were built with the compiler tools used. Compiled code requires much more skill and knowledge to reverse engineer, and even though there are applications out there to assist with this task, you still need a great deal of in-depth knowledge to know what you’re looking at, way more knowledge than is required to simply drop a binary file onto an application icon and see familiar C# code get produced. By using software DLLs compiled with these older methods, and the .NET P/Invoke syntax, you can easily compile intellectually sensitive parts of your application into older style DLLs, and then invoke the functionality inside these DLLs directly from your .NET application.

A Practical Example

To show you what I mean, I’m going to create a simple C++ DLL in Visual Studio. It’s nothing fancy—just a single function that will add two numbers together and produce a result. Once this is done, I’m then going to create a simple .NET win-form desktop application that will call the DLL using P/Invoke.

First things first. Fire up Visual Studio (I’m using 2013), and start a ‘Blank Solution’. This is usually found under ‘Templates->Other Project Types->Visual Studio Solutions->Blank Solution’. Set the .NET framework to Version 4 or above, give your project and a name. and click OK. This should give you an empty project, with an empty solution explorer:

PInvoke4
Figure 4: The empty Solution Explorer

Right-click the solution tree root and add a new Win32 project from the other languages section in Visual Studio:

Note: For this to be present, you MUST have installed the C++ tools when you installed Visual Studio. Furthermore, if you’re using the express versions, you’ll need to use “Visual Studio Express for Windows Desktop” to get all the tools needed here.

PInvoke5
Figure 5: Adding a new Win32 project

Call the project something like “compiledlib”, click OK on the new project dialog, and select the following options:

  • Application type: DLL
  • Additional options: Un-tick everything that can be un-ticked
  • Add common header files for: Un-tick all

Click finish, and you should see a C++ DLL project appear in your solution explorer. We’ll change this project to be our simple adding function. You should see that “compiledlib.cpp” opens automatically when you finish creating the project.

This file will initially contain very little. Replace any code in this file with the following:

#include "stdafx.h"
#include "compiledlib.h"

extern "C"
{
   COMPILEDLIB_API int addnumbers(int paramone, int paramtwo)
   {
      return paramone + paramtwo;
   }
}

This will be our main function that will be called from .NET.

Right-click “Header Files” in the C++ project and Add->New Item. Then, find and add a new Header File called “compiledlib.h” (Note: If you called your DLL something else, you’ll need to name the file appropriately and update the .cpp file’s include line to reference the correct file name.

Into this header file, add the following code:

#ifdef COMPILEDLIB_EXPORTS
#define COMPILEDLIB_API __declspec(dllexport)
#else
#define COMPILEDLIB_API __declspec(dllimport)>
#endif

extern "C"
{
   COMPILEDLIB_API int addnumbers(int paramone, int paramtwo);
}

The rest of the files, such as DLLMain.cpp, can be left untouched for this example. You may, however, need to make other changes here depending on the needs of your code. At this point, if you right-click your compiled lib project and select ‘Build’, you should be able to build your un-managed native DLL without any problems.

If you still have your Visual Studio tools console window open from earlier on, change to the folder where your project has saved “compiledlib.dll”, and try “dumpbin /exports” on it as you did previously with “user32.dll”. You should see that the add function you just created has been exported correctly:

PInvoke6
Figure 6: The exported file

If you get an export as shown in Figure 6, your DLL is ready to be used by your managed .NET application.

Let’s Add a Winforms App

Right-click your solution, and select “Add->New Project->Visual C#->Windows Desktop->Windows Forms Application”. Let’s call this something like “ManagedUI”:

PInvoke7
Figure 7: The new Windows Forms Application project

When the project loads, add two text boxes, four labels, and a button to the form that’s created for you. I’ve renamed my control names and laid the form out so it’s nice and linear:

PInvoke8
Figure 8: The text boxes, labels, and button are added

Double-click your calculate button so that the code editor adds a click handler in for you. Then, we can start adding the code we need.

To reference the C++ DLL we created, you’ll need to use an attribute called “DLL Import”. Just inside the public partial class for your form, add the following code:

[DllImport("compiledlib.dll",
   CallingConvention = CallingConvention.Cdecl)]
private static extern int addnumbers(int paramone, int paramtwo);

Make sure that if you called your DLL by a different name, that you change “compiledlib.dll” as required. Another important thing to take notice of is the calling convention. In older versions of Visual Studio, directly specifying this wasn’t important because the compiler usually managed to figure it out for itself. Since VS2010, however, this no longer seems to be the case. If you think back to the C++ code, the methods and functions were wrapped in ‘extern “C” { … }’, which is known as the “Cdecl” function specification.

You can select different calling conventions here, such as omitting the extern-c directive, and telling .NET to use a C++ calling convention. Doing this, though, requires a more in-depth knowledge of what you’re doing. Using extern-c is by far the easiest way to accomplish our task.

To use DLL Import, you’ll also need to reference ‘System.Runtime.InteropServices’. This should be in your references list by default, meaning you only need the following using clause in your code:

using System.Runtime.InteropServices;

Once you’ve specified the DLL import, all you then need to do is add the code to your button handler to get the text box inputs and call it.

Your finished webforms class should look something like this:

using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace ManagedUI
{
   public partial class Form1 : Form
   {
      [DllImport("compiledlib.dll",
         CallingConvention = CallingConvention.Cdecl)]
      private static extern int addnumbers(int paramone, int paramtwo);

      public Form1()
      {
         InitializeComponent();
      }

      private void BtnCalculateClick(object sender, EventArgs e)
      {
         int numOne = Convert.ToInt32(txtFirstNum.Text);
         int numTwo = Convert.ToInt32(txtSecondNum.Text);

         int result = addnumbers(numOne, numTwo);

         lblResult.Text = result.ToString(CultureInfo.InvariantCulture);

      }
   }
}

You should be able to hit Ctrl+Shift+B at this point and everything will build.

Before we can run it, though, we need to copy our compiledlib.dll file to the output folder for our .NET app. If you right-click the solution, and select ‘open folder in file explorer’, you’ll see a “Debug” folder. The unmanaged “compiledlib.dll” file will be in there. Navigate to that folder, right-click the DLL and copy it, and then navigate to your .NET app’s debug folder and paste the DLL into there.

You can configure your C++ project to output its compiled DLL directly to this location if you wish, and if you had a larger, more complex project you would certainly want to do this. For this example, however, copying the DLL directly is simpler. If you now right-click your .NET project, make that the start-up project, and then press F5 to run things, you should be able to enter two numbers into the form and press calculate to get a result.

And that’s it. Making your .NET app consume non-.NET functions really is this easy.

There’s much more under the surface for those who choose to dig deeply into it, but if all you want to do is re-use old non-managed code, or to make it harder for folks to reverse engineer your IP, this is definitely the quickest route to go. Bear in mind, too, that you can do this from within ASP.NET Webforms, MVC, WPF, WCF, and Silverlight apps, too. In fact, if you have an old custom business application that you’ve been tasked with converting into a modern day web application, this can be a fantastically easy way to take all that C++ code, recompile it into a DLL, and then simply call it by using PInvoke from your ASP.NET MVC-based User Interface.

I’ll make the code from this article available on my github page at

https://github.com/shawty?tab=repositories

for those who want to download it and use it as a basis for their own experiments.

If you have any burning questions, or are struggling with a topic in .NET that you can’t find explained, feel free to hunt me down. Most of the time, you can find me floating around in the Lidnug .NET users group on Linked-in and hanging around in Twitter as @shawty_ds. I have a Google+ page, a Linked-in page, and all sorts of other ways of getting in touch. Let me hear your thoughts and ideas and I’ll do what I can to incorporate them in future articles.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read