Calling an unmanaged dll from .NET (C#)


OK, so this first example is going to show how to call an unmanaged dll from .NET (C#). There’s no better way to explain how it all fits together than by example, so first off we’re going to create an unmanaged dll in C++. The function we’re exporting from the dll would obviously be of vital importance to your business in the real world and contain a wealth of logic, but for the sake of simplicity let’s have a void function that takes a basic struct as an argument and does nothing more than alter the fields within it.


The header file in your project should contain the following definitions:


struct MyStruct
{
      
int
       SomeId;
      
double
SomePrice;
};


extern “C” __declspec(dllexport) void PassStructIn(MyStruct* myStruct);


OK, I’m hoping the struct decleration doesn’t need any explanation, we’re simply defining a structure that contains two fields, one of type int and one of type double.


Our function definition is a little more complicated however, so let’s start from left to right and work our way through it.


In C++, because functions can be overloaded (differing not by name but by signature [mixture of name and parameters]) the compiler goes through a process of ‘decorating’ the names internally so it can uniquely identify them when they’re called. To simplify this example, we want to use the function name as we’ve written it from within our C# code and not a mangled representation. Using extern “C” forces the compiler to use the actual function name (as it would in C). This prevents us from overloading this function but we’re not bothered about that in this example.


On a related note, if you want to examine a dll to find out, amongst other things, exported function names, you can use the dumpbin command from the Visual Studio command prompt. Typing dumpin /exports filename will list the exported function names from the dll. Try it on our simple dll with and without the extern “C” keywords to see the decoration in action.


__declspec(dllexport) puts the plumbing in place that’s actually going to allow our function to be exported from our dll. It adds the export directive to the object file so we don’t need to bother around with a .def file.


void PassStructIn(MyStruct* myStruct); OK, so our function is void (doesn’t return anything), is named PassStructIn and takes a single argument of type pointer-to MyStruct.


The actual function definition in the source file should look something like this:


void PassStructIn(MyStruct* myStruct)
{
      
if (myStruct != NULL)
      {

            myStruct->SomeId = 234;
            myStruct->SomePrice = 456.23;
      }
}


This is basic indeed. All it does is check that the pointer to our struct isn’t NULL and then attempts to alter the two fields within it.


OK, that’s the unmanaged code out the way, let’s move on to the managed  code now and utilise our ‘feature rich’ dll… ; )


I started off by creating a C# console application, and then adding a class within it named NativeMethods. This class is going to neatly wrap all of our native calls and such like. Because our unmanaged function requires a structure as a parameter, the structure needs to be defined in the managed code as well as in the unmanaged code. Following is our NativeMethods class definition:


static class NativeMethods
{
      
public struct MyStruct
      
{
            
public int SomeId;
            
public double SomePrice;
      
}

      [
DllImport(@”YouDirStructure\YourDLLName.DLL”)]
      public static extern void PassStructIn(ref MyStruct theStruct);
}


Notice that the fields within the structure definition are defined in the same order as in the unmanaged C++ structure and are of the same type. If they weren’t, we would have to decorate the structure with the [StructLayout] attribute, passing in a value from the LayoutKind enumeration. If it’s not provided (as in our example), it defaults to:


[StructLayout(LayoutKind.Sequential)]


This tells the marshaller that the fields within our structure should be laid out in the same sequence as they’re defined. The other two permissable values are Auto and Explicit. Auto instructs the runtime to lay the fields out how it sees fit, and Explicit gives you the ability to define precisely how each field is to be laid out.


Next up is our DllImport attribute, where we specify the full name of the unmanaged DLL that our function is contained within. There are some optional parameters we can provide this attribute with, which I’ll cover in later posts. The only one I need to mention now is the EntryPoint parameter, which we haven’t specified (and for good reason). This allows us to specify the name of the function within the dll if we want the name of our managed wrapper function to be different. In our case, PassStructIn is the name of our unmanaged function, as well as our managed function and so EntryPoint can be ommitted. If our unmanaged function name was decorated and rather unwieldy, we might be tempted to specify this in the EntryPoint parameter and keep our managed function name neat and tidy.


All that remains is for us to utilise our code like so and hey presto!


static void Main(string[] args)
{
      
NativeMethods.MyStruct myStruct;
      myStruct.SomeId = 23;
      myStruct.SomePrice = 30.52;
      
NativeMethods.PassStructIn(ref myStruct);

      
Console.WriteLine(“SomeId={0}; SomePrice={1}”, myStruct.SomeId, myStruct.SomePrice);
}


We define our managed struct and set it’s fields to two arbitrary values, before calling our managed wrapper and passing the struct in. Notice we pass it in by reference, as our unmanaged function expects a pointer.


Our output shows that the two fields were then changed within the unmanaged C++ code, simple eh?


 


Comments (42)

  1. blairio says:

    This is all find and dandy if you know the location of your unmanaged DLL at compile time, but what about the case where the location of the DLL to import is only known at runtime?

    For instance, the path to the DLL is found from a value in the Windows Registry?

  2. Saurabh Jain says:

    Hey!

    Very neatly done.

    I have been searching for "PInvoke" tutorials for beginners and yours is the one I have understood the best.

    Thanks a tonne 😀

    – Saurabh

  3. Ram Anam says:

    I have an unmanaged C DLL with function that has Char ** as I/P parameter. In that case what shall be the marshalling signature of that parameter.

    Thank-you

    Ram

  4. Anonymous says:

    That depends on how the parameter is used.  For the simple case of returning a pointer to a string, you can do this:

    public static extern void Function(out IntPtr p);

    Then to read the string:

    IntPtr p;

    Function(out p);

    string s = Marshal.PtrToAnsiString(p);

  5. Hi Jonathan,

    This example code was great. It gave me a better understanding of unmanaged-managed code interop. Thanks.

    Kind regards,

    Gerben Heinen

  6. kamlesh says:

    It is a good example.  But what we have to do

    when function is not exported as extern c from

    C++ dll. And I do not have any ieda of source

    code.

    Thanks,

    kamlesh

  7. Fang says:

    I have a unmanaged Dll with define and struct like this:

    #ifdef QNCTOOLS_EXPORTS

    #define QNCTOOLS_API __declspec(dllexport)

    #else

    #define QNCTOOLS_API __declspec(dllimport)

    #endif

    struct QNCTOOLS_API UpdateResult

    {

    bool m_success;

    std::string m_qnc;

    std::string m_update;

    std::string m_number;

    std::string m_desc;

    UpdateResult() :

    m_success(false),

    m_qnc(""),

    m_update(""),

    m_number(""),

    m_desc("")

    {

    }

    };

    How to call this unmanaged dll from c#?

  8. AnnD says:

    I am new to C# and trying to reuse some of my  C++ unmanaged code.  I copied your example exactly, but I’m seeing a strange result.  In my unmanaged code, I assign and print the values and all looks good. In the managed code, when I print the values, the ‘int’ value is correct but the ‘double’ value is not.  

    Am I missing something.  

  9. Ray says:

    How do you mashal the structure if the C++ function as following:

    extern "C" __declspec(dllexport) void PassStructIn(MyStruct &myStruct);

  10. Bhanuprakash says:

    How do you call having a CString structure in C++ to your application. In my application i am using String in my structure. I tried it , but it returns an error.. Could you help me in sorting out this?

  11. steve says:

    I have a question. I have a vc++ mfc application that load c# .NET DLL. How can the c#.NET Dll that is load call a method in the VC++ code. All the examples I have see has C# application loading a DLL writtn in c++.

    How can a C# Dll that has been loaded into a C++ application call a method located in the c++ application?

    Thanks

    You can email me at steve_44@inbox.com

  12. steve says:

    I have a question. I have a vc++ mfc application that load c# .NET DLL. How can the c#.NET Dll that is load call a method in the VC++ code. All the examples I have see has C# application loading a DLL writtn in c++.

    How can a C# Dll that has been loaded into a C++ application call a method located in the c++ application?

    Thanks

    You can email me at steve_44@inbox.com

  13. Narender says:

    I had a VC++ dll which has exported using __declspec(dllexport), and it has some overlaoded methods, so when iam trying to call a method by its name its giving an error message like entry point for the method not found, but when iam trying to access with ordianl number iam able to call that method,

    I need help how to call a method by its name when we export it by decorating / mangling the method names

  14. Surya says:

    Nice and simple article! Easy to understand in just one reading and have basics to start working. Thanks!

  15. Taher Hassan says:

    How can i do the same if my C++ dll has classes.

    Thanks

  16. Taher Hassan says:

    Hi:

    I have a question, How you deal with types in C++ and C#. For example, if you want to write to a file

    in c++ == ostream

    in c#  == streamwriter

    Thanks

  17. himanshu says:

    i am implementing the example of dll in which i have one addition method in my dll.

    i m calling this method from my C# code but it is giving an exception"Unable to find an entry point named ‘addition’ in DLL ‘C:\WINDOWS\system32\MathFuncs.dll’.":""};

    what could be a possible solution for it.

  18. Vitalij says:

    One more variant:I have an unmanaged C DLL with function that has void ** as I/P parameter. In that case what shall be the marshalling signature of that parameter.

    Thanks,

    Vit

  19. Anand Patel says:

    I am having trouble with passing a char buffer to an unmanaged function :

    C++ code

    void GetPacket(char Data[], long length)

    {

      memcpy(Data, somememoryloc, length);

    }

    In C#

    [DllImport("Csp2.dll", EntryPoint = "GetPacket")]

    public static extern void GetPacket([MarshalAs(UnmanagedType.LPArray)] ref byte[] Data,long Length);

    byte[] Packet = new byte[63];

    GetPacket(ref Packet, 63);

    Function copies a load of data in the buffer that is passed to it. However, after the function is called Packet has shrunk to one element.

    Thanks,

    Anand

  20. Suzanna says:

    I have a dll file. The dll file has functions like that

    "typedef short apiStatus;

    apiStatus __declspec(dllexport) __stdcall DrfCommOpen (HANDLE * hCom, char *com_port);"

    how i can call and use this function in my c#.net application?

    thanks in advance..

    regards..

  21. sam says:

    I have some dll’s . I want to identify which are managed and which unmanaged. Please do guide me.

  22. dr says:

    "It is a good example.  But what we have to do

    when function is not exported as extern c from

    C++ dll. And I do not have any ieda of source

    code."

    You cannot call ANY C function from an external lib unless it has been defined as extern.

    "How do you mashal the structure if the C++ function as following:

    extern "C" __declspec(dllexport) void PassStructIn(MyStruct &myStruct);"

    You have to define your own version of myStruct within the C# program that mirrors the structure as it is defined in the DLL. Be careful to make sure the data types work between the two.

  23. prashant says:

    I have to pass nested structure from C# application in one of the dll API by reference. DLL is written in C. Below are the details of the structure and DLL API in C.

    typedef struct

    {

               unsigned char   m_Class_of_Device0;

               unsigned char   m_Class_of_Device1;

               unsigned char   m_Class_of_Device2;

    } sGAP_ClassofDevice_t;

    typedef struct _tagGAP_Settings

    {

               char m_strDeviceName[250];

               uint m_uiNameStrSize;

               sGAP_ClassofDevice_t  m_ClassOfDevice;

    } sGAP_Settings_t;

    int   InitializeInstance (sGAP_Settings_t *psGAPSettings);

    There corresponding mapping in C# is

    [StructLayout(LayoutKind.Sequential)]

           public struct sGAP_ClassOfDevice_t

           {

               public byte m_ClassOfDevice0;

               public byte m_ClassOfDevice1;

               public byte m_ClassOfDevice2;

           }

           [StructLayout(LayoutKind.Sequential)]

           public struct sGAP_Settings_t

           {

               public string m_strDeviceName;

               public uint m_uiNameStrSize;

               public sGAP_ClassOfDevice_t m_ClassOfDevice;

           }

    [DllImport("abc.dll")]

    public static extern int InitializeInstance(ref sGAP_Settings_t psGAPSettings);

    I tried this way but my DLL API is not getting the parameters which i pass them in this structure.

    Can you help on this

  24. Niraj Patel says:

    Hi There!

    Nice example ..i implemented it and working for me.

    But when i was told to convert this application in multithreaded environment this "static extern" is not letting me make different instances for differnt threads.

    can u pls tell me what should i do in such scenario?

  25. A Greaves says:

    Using VS 2010 I get the following exception: PInvokeStackImbalance

    It claims that the sigs between managed and unmanaged are not the same, however since I double checked my cut and paste from the code here I know that's not the problem.  I am compiling the C++ dll with /clr but nothing seems to work.  

    My only success thus far has been in passing C-style functions with no parameters that return a value.  That works.  But the minute I add a parameter… BOOM!  Hints, suggestions, cyanide pill?

  26. A Greaves says:

    Using VS 2010 I get the following exception: PInvokeStackImbalance

    It claims that the sigs between managed and unmanaged are not the same, however since I double checked my cut and paste from the code here I know that's not the problem.  I am compiling the C++ dll with /clr but nothing seems to work.  

    My only success thus far has been in passing C-style functions with no parameters that return a value.  That works.  But the minute I add a parameter… BOOM!  Hints, suggestions, cyanide pill?

  27. @ A Greaves :

    Try this in your C# Wrapper:

    [DllImport("************.DLL", CallingConvention = CallingConvention.Cdecl)]

    CallingConvention = CallingConvention.Cdecl works for me but i can't explain why …

  28. Balaji says:

    I am getting the "Unable to find an entry point named 'PassStructIn' in DLL 'dllname.dll' "

  29. loviji says:

    Is it a way to protect some most critical part of application logic from access of not wanted persons, because C++ DLL can not be disassembled?

  30. jyotiranjan says:

    Nice one… very clearly explained.:)

Skip to main content