Click here to Skip to main content
Click here to Skip to main content
Technical Blog

Tagged as

C#/C interop with DllImport

, 25 Feb 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
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 are: 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:

__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 :

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 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 diner 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 question or remark about this article feel free to let a comment.

License

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

Share

About the Author

Pragmateek
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.
Follow on   Twitter   LinkedIn

Comments and Discussions

 
GeneralMy vote of 1 PinmemberMember 816637218-Sep-14 7:55 
GeneralMy vote of 1 PinmemberMember 816637218-Sep-14 7:55 
GeneralMy vote of 1 PinmemberKarstenK22-Jul-14 21:25 
GeneralRe: My vote of 1 PinprofessionalPragmateek23-Jul-14 1:13 
Questionpassing parameters PinmemberAchLog20-May-14 12:10 
AnswerRe: passing parameters PinprofessionalPragmateek20-May-14 12:42 
GeneralRe: passing parameters PinmemberAchLog20-May-14 23:55 
GeneralRe: passing parameters PinprofessionalPragmateek21-May-14 0:09 
GeneralRe: passing parameters PinmemberAchLog21-May-14 0:55 
GeneralMy vote of 5 Pinmemberpjcoder7-Aug-13 11:27 
GeneralRe: My vote of 5 PinmemberPragmateek8-Aug-13 4:03 
GeneralMy vote of 5 Pinmembernevergiveupc3-Mar-13 4:10 
GeneralRe: My vote of 5 PinmemberPragmateek3-Mar-13 4:21 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.1411023.1 | Last Updated 25 Feb 2013
Article Copyright 2013 by Pragmateek
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid