Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#

C#/C Interop with DllImport

Rate me:
Please Sign up or sign in to vote.
3.46/5 (15 votes)
25 Feb 2013CPOL4 min read 129.5K   25   21
Keep reading and in 10 minutes you’ll know DllImport (almost) like a pro.

Introduction

So you have or want to develop a native component (typically a DLL written in C or C++) with a simple API and you need to use it from another component developed in C#. You know that you can use the DllImport mechanism and you’ve seen the 156387 tutorials that show how to make it with kernel32.dll (and by the way this is sometimes only what you need). You’ve also seen some tutorials that to illustrate what you think is a simple matter are using huge codes and made you scream “pleeaaase get to the point!”, or the ones that ask you to setup a Visual Studio project, but you want to avoid useless plumbing and understand what happens under the hood. You’ve tried by yourself and feel you’re almost there but you still have errors and start to feel frustrated.

The good news is: first you’re right when you think DllImport is straightforward for simple interfaces, second this is the no-over-engineering, no-overhead, no-nonsense, KISS tutorial for it.

So keep reading and in 10 minutes, you’ll know DllImport (almost) like a pro.

(Source code is available here.)

The Awesome C Library

Here is the C code for our top-notch numerical library, lib.c:

MC++
__declspec(dllexport)
int next(int n)
{
    return n + 1;
}

The first line I’ve highlighted is the only thing to notice: it’s a directive that asks the compiler to mark the function as public so that it can be accessed from external components. Without this directive, the functions are by default internal to the library and are hidden from the outside.

Generating the library is straightforward and I give you three solutions:

  • If you have Visual Studio on your computer: open a Visual Studio Command Prompt, go to the folder containing your source code and invoke the CL compiler:
    >cl /LD lib.c
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    lib.c
    Microsoft (R) Incremental Linker Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:lib.dll
    /dll
    /implib:lib.lib
    lib.obj
       Creating library lib.lib and object lib.exp

    This will generate a bunch of files, you can keep only the “lib.dlllibrary. You can check that the “next” function has been correctly exported with dumpbin:

    >dumpbin /exports lib.dll
    Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    Dump of file lib.dll
    
    File Type: DLL
    
      Section contains the following exports for lib.dll
    
        00000000 characteristics
        512A5028 time date stamp Sun Feb 24 18:38:48 2013
            0.00 version
               1 ordinal base
               1 number of functions
               1 number of names
    
        ordinal hint RVA      name
    
              1    0 00001000 next
    
      Summary
    
            2000 .data
            2000 .rdata
            1000 .reloc
            5000 .text
  • If you do not use Visual Studio, you can use MinGW, a great cross-compiler: open the MinGW shell, go to the folder and invoke the GCC compiler:
    $ gcc -shared -o lib.dll lib.c

    This will only generate the “lib.dlldynamic library. You can check that the “next” function is exported with the NM utility:

    $ nm --extern-only --demangle lib.dll | grep next
    61fc1254 T next
  • You can also use the Windows SDK that contains the CL compiler: instructions and output are similar to the Visual Studio situation.

Whatever the tools you’ve used, you now have a “lib.dlldynamic library.

The C# Application

With such an incredible C library, we needed a decent C# application to demonstrate its power:

C#
using System; // Console
using System.Runtime.InteropServices; // DllImport

class App
{
    [DllImport("lib.dll", CallingConvention = CallingConvention.Cdecl)]
    extern static int next(int n);

    static void Main()
    {
        Console.WriteLine(next(0));
    }
}

When run, it will reveal to you what is the number following 0. But to maintain the suspense, we first analyze the code, at least the two highlighted lines: they tell the C# compiler that somewhere in the “lib.dll” file, there is a function named “next” which takes an integer as unique parameter and returns an integer. (If you’re in a hurry, you can skip the next paragraph and directly go to the compilation part.)

Moreover, this code says that a specific procedure should be used to call the native function: the so-called CDecl calling-convention. If you don’t specify this parameter, all will appear to work fine and you won’t notice any error, except if you activate the MDAs: you will then be presented with an error like “A call to PInvoke function has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature“. But why? First, you should know that the default calling convention for C functions is CDecl. But by default, DllImport uses StdCall, hence the need to explicitly specify CDecl to override the default behavior. Second, without diving too deep into the technical details, CDecl states that the calling side has to do some cleanup after the call, whereas StdCall states that this is the responsibility of the called side. So without more clues, the C# code would have called the function expecting it to cleanup but would have been surprised if it was not the case, hence the pInvokeStackImbalance MDA. (This kind of misconception about who has to do the cleanup after dinner has caused more than one divorce!)

Let’s now compile our C# application: locate the CSC C# compiler you want to use on your system (e.g. if you have .NET 4.0, you can find one in C:\Windows\Microsoft.NET\Framework\v4.0.30319) and invoke it this way:

>csc /platform:x86 App.cs
Microsoft (R) Visual C# Compiler version 4.0.30319.17929
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.

Simple, except the compilation flag /platform:x86: it specifies that this application targets a specific native platform (x86), and will then be allowed to call native x86 functions. If you forget this flag, you’ll end up with this kind of error when running the generated EXE: “Unhandled Exception: System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)“.

Hey! we’re done, we only have to run our Test.exe managed application to know the integer following 0:

>App.exe
1

Wow, I’d never guessed!

Conclusion

You’ve seen how to do simple DllImport and you should now be able to use .NET/native interop via DllImport in more complex, real-life, scenario.

If you have any questions or remarks about this article, feel free to let a comment.

This article was originally posted at http://pragmateek.com/cc-interop-with-dllimport

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer Pragmateek
France (Metropolitan) France (Metropolitan)
To make it short I'm an IT trainer specialized in the .Net ecosystem (framework, C#, WPF, Excel addins...).
(I'm available in France and bordering countries, and I only teach in french.)

I like to learn new things, particularly to understand what happens under the hood, and I do my best to share my humble knowledge with others by direct teaching, by posting articles on my blog (pragmateek.com), or by answering questions on forums.

Comments and Discussions

 
Questioncpp files in dll Pin
Member 109376372-Nov-16 20:20
Member 109376372-Nov-16 20:20 
AnswerRe: cpp files in dll Pin
Pragmateek14-Nov-16 10:28
professionalPragmateek14-Nov-16 10:28 
QuestionDLL to import calls for other library Pin
Creammy4-Nov-15 23:26
Creammy4-Nov-15 23:26 
AnswerRe: DLL to import calls for other library Pin
Pragmateek2-Dec-15 0:02
professionalPragmateek2-Dec-15 0:02 
Questioncall c/c++ function from c# when the c function has structure ptr/class ptr as parameter Pin
ArulrajM19-Aug-15 17:38
ArulrajM19-Aug-15 17:38 
AnswerRe: call c/c++ function from c# when the c function has structure ptr/class ptr as parameter Pin
Pragmateek20-Aug-15 12:44
professionalPragmateek20-Aug-15 12:44 
GeneralRe: call c/c++ function from c# when the c function has structure ptr/class ptr as parameter Pin
ArulrajM1-Sep-15 21:55
ArulrajM1-Sep-15 21:55 
GeneralRe: call c/c++ function from c# when the c function has structure ptr/class ptr as parameter Pin
Pragmateek2-Sep-15 13:00
professionalPragmateek2-Sep-15 13:00 
GeneralMy vote of 1 Pin
Member 816637218-Sep-14 6:55
Member 816637218-Sep-14 6:55 
GeneralMy vote of 1 Pin
Member 816637218-Sep-14 6:55
Member 816637218-Sep-14 6:55 
GeneralMy vote of 1 Pin
KarstenK22-Jul-14 20:25
mveKarstenK22-Jul-14 20:25 
GeneralRe: My vote of 1 Pin
Pragmateek23-Jul-14 0:13
professionalPragmateek23-Jul-14 0:13 
Questionpassing parameters Pin
AchLog20-May-14 11:10
AchLog20-May-14 11:10 
AnswerRe: passing parameters Pin
Pragmateek20-May-14 11:42
professionalPragmateek20-May-14 11:42 
GeneralRe: passing parameters Pin
AchLog20-May-14 22:55
AchLog20-May-14 22:55 
GeneralRe: passing parameters Pin
Pragmateek20-May-14 23:09
professionalPragmateek20-May-14 23:09 
GeneralRe: passing parameters Pin
AchLog20-May-14 23:55
AchLog20-May-14 23:55 
GeneralMy vote of 5 Pin
pjcoder7-Aug-13 10:27
pjcoder7-Aug-13 10:27 
GeneralRe: My vote of 5 Pin
Pragmateek8-Aug-13 3:03
professionalPragmateek8-Aug-13 3:03 
GeneralMy vote of 5 Pin
nevergiveupc3-Mar-13 3:10
nevergiveupc3-Mar-13 3:10 
GeneralRe: My vote of 5 Pin
Pragmateek3-Mar-13 3:21
professionalPragmateek3-Mar-13 3:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.