C# DllImport and Dllexport for DLL Interop

DLL

You need to use the DllImport and dllexport keywords to get Windows interop working. Use C++ DLL dynamic link library or a custom legacy DLL you can't rewrite but have the ability to make modifications to. Here we see how you can use interop in the .NET Framework and C# programming language.

Using DllImport

First, we need to make or modify the DLL. In this article, 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.

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 on CLR

For this article, we are not using the CLR in the C++ DLL. This is a very important point to consider because if you use CLR, you will have managed C++ code and you can use better interfaces between C++ and C#. Also, in the Linker property page, you can set the output name of the DLL, such as the name CoreDLL.dll.

Using dllexport in 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.

// 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);
}

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

// 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.
}

Using DllImport in 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. The following code needs to be added to a class in C# in your Windows Forms program.

/// <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 C#. We use the static UnsafeNativeMethods class to house the extern declaration in C#. 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.

Calling C++ methods

Look at the class GameSharp that shown in 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 class GameSharp
{
    // [code omitted, see above example]

    /// <summary>
    /// Simulate N games in the DLL.
    /// </summary>
    /// <param name="num">The number of games to simulate.</param>
    public static void SimulateGameCall(int num)
    {
        UnsafeNativeMethods.SimulateGameDLL(num, new Random().Next());
    }
}

Issues

There are many problems that can happen, and many of them probably will. Next in this document we list some gotchas and hint at some more problems.

Easily broken. There are so many things that can go wrong, and this whole setup is very fragile. In fact, the author doesn'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 exactly 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. The DLL export method must have exactly the same name as well, although its parameter names aren't important.

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

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.

See Visual Studio Post-Build, Pre-Build Macros.

Prefer single-solution. It is best to have both the C++ DLL and the C# code in the same solution in Visual Studio 2008. There are better ways to make them work together this way.

Summary

Here we saw how you can implement simple DLL interoperation using the DllImport and dllexport keywords in the C# programming language and the C++ programming language. Use the dllexport and DllImport keywords listed here for one way to make an old C++ DLL work with a C# GUI. This is not a comprehensive guide to C++ interop with C#, but it contains material that can help you get started. The approach here is not always optimal.

See Attribute Tips and Examples.

See Attribute Overview.

© 2007-2010 Sam Allen. All rights reserved.

Dot Net Perls  Sam Allen