C# DllImport, Dllexport

DLL: Dynamic Link Library

DllImport and dllexport enable interop with DLL files. We can use a C++ DLL dynamic link library, or a custom legacy DLL—even one we can't rewrite but have the ability to modify.

Settings

Visual Studio logo

First, we need to make or modify the DLL. We have the DLL set up with the following properties. In Visual Studio's Solution Explorer, you can right-click on the DLL project and look at the Property Pages to see these settings.

Visual Studio
DLL settings:

Configuration Type:
    Dynamic Library (.dll)

Use of MFC:
    Use Standard Windows Libaries

Use of ATL:
    Not Using ATL

Common Language Runtime Support:
    No Common Language Runtime Support

Whole Program Optimization:
    Use Link Time Code Generation
Note

For this article, we are not using the CLR in the C++ DLL. This is an important point to consider because if you use CLR, you will have managed C++ code and you can use better interfaces between your C++ code and your C# code.

Also:In the Linker property page, you can set the output name of the DLL, such as the name CoreDLL.dll.

Dllexport, C++

The DLL and the C# EXE need to communicate. You do this by defining extern "C" functions in your C++ DLL. There is a special syntax for doing this that you must use. The next block of code shows an extern function you put in your DLL.

Program fragment that uses dllexport: C++

// This code should be put in a header file for your C++ DLL. It declares
// an extern C function that receives two parameters and is called SimulateGameDLL.
// I suggest putting it at the top of a header file.
extern "C" {
    __declspec(dllexport) void __cdecl SimulateGameDLL (int a, int b);
}

In the example, the __declspec(dllexport) part is standard and not required to be referenced. Our C# GUI will call this function from an UnsafeNativeMethods class. Let's look at the extern function in the same header file in the C++ DLL.

Program fragment that implements method: C++

// The keywords and parameter types must match the above extern
// declaration.
extern void __cdecl SimulateGameDLL (int num_games, int rand_in) {

    // This is part of the DLL, so we can call any function we want
    // in the C++. The parameters can have any names we want to give
    // them and they don't need to match the extern declaration.
}

DllImport, C#

First compile your DLL and make sure it is in the same directory as the C# Windows Forms GUI. Let's look at the code we need to write in the C# interop code. It needs to be added to a class in C# in your Windows Forms program.

Program fragment that calls DLL method: C#

/// <summary>
/// A C-Sharp interface to the DLL, written in C++.
/// </summary>
static class GameSharp
{
    /// <summary>
    /// The native methods in the DLL's unmanaged code.
    /// </summary>
    internal static class UnsafeNativeMethods
    {
	const string _dllLocation = "CoreDLL.dll";
	[DllImport(_dllLocation)]
	public static extern void SimulateGameDLL(int a, int b);
    }
}

Using the extern methods in your C# program. We use the static UnsafeNativeMethods class to house the extern declaration in your C# program. The function name is the same as the one in the C++ DLL.

We can use a const string to designate the filename of the DLL. The DllImport("name.dll") must have the name of the C++ you are building. The brackets indicate we are using an attribute.

Call C++ methods

Look at the class GameSharp from the previous example. We need to add another static method to that class so that the rest of the C# program can use the native method. This is essentially a proxy method that forwards the method call.

Static Method
Program fragment that uses UnsafeNativeMethods: C#

static class GameSharp
{
    // [code omitted, see above example]

    /// <summary>
    /// Simulate N games in the DLL.
    /// </summary>
    public static void SimulateGameCall(int num)
    {
	UnsafeNativeMethods.SimulateGameDLL(num, new Random().Next());
    }
}

Discussion

Warning: exclamation mark

DLL handling is fraught with errors. There are many problems that can happen, and many of them probably will. Next in this document we list some possible errors and hint at yet more problems.

Easily broken. There are so many things that can go wrong, and this whole setup is very fragile. I don't recommend it unless it is necessary for your scenario. A fully managed C# program is safer and not too much slower.

DLL name incorrect. Obviously, if your DLL name is not the same in both places, the code won't work. As a developer, you should know that typos always occur in places we can't detect them.

Also:The DLL export method must have the same name, although its parameter names aren't important.

MarshalAs:You must use MarshalAs for when you have a char* pointer, although there are other options.

Unsafe

Use macros. It is best to use Visual Studio macros to move DLLs around as you are testing both ends of your code. Otherwise, moving things around can become very time-consuming and tedious.

Visual Studio Post-Build, Pre-Build Macros

Tip:It is often best to have both the C++ DLL and the C# code in the same solution in Visual Studio. This helps them work together.

Summary

We implemented simple DLL interoperation using the DllImport and dllexport keywords in the C# language and the C++ language. Use the dllexport and DllImport keywords listed here for one way to make an old C++ DLL work with a C# GUI.

Review:This is not a comprehensive guide to C++ interop with C# code, but it contains material that can help you get started.


C#: Class: Attribute