Udostępnij za pośrednictwem


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

  • Saurabh
  • Anonymous
    October 28, 2006
    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

  • Anonymous
    October 31, 2006
    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);

  • Anonymous
    November 30, 2006
    Hi Jonathan, This example code was great. It gave me a better understanding of unmanaged-managed code interop. Thanks. Kind regards, Gerben Heinen

  • Anonymous
    June 16, 2007
    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

  • Anonymous
    August 09, 2007
    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#?

  • Anonymous
    August 24, 2007
    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.  

  • Anonymous
    January 29, 2008
    How do you mashal the structure if the C++ function as following: extern "C" __declspec(dllexport) void PassStructIn(MyStruct &myStruct);

  • Anonymous
    February 27, 2008
    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?

  • Anonymous
    May 19, 2008
    PingBack from http://www.mkoby.com/2008/05/20/links-for-2008-05-20/

  • Anonymous
    June 27, 2008
    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

  • Anonymous
    June 27, 2008
    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

  • Anonymous
    July 06, 2008
    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

  • Anonymous
    August 18, 2008
    Nice and simple article! Easy to understand in just one reading and have basics to start working. Thanks!

  • Anonymous
    January 01, 2009
    How can i do the same if my C++ dll has classes. Thanks

  • Anonymous
    January 01, 2009
    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

  • Anonymous
    January 04, 2009
    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.

  • Anonymous
    March 18, 2009
    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

  • Anonymous
    May 27, 2009
    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

  • Anonymous
    June 01, 2009
    PingBack from http://patiochairsite.info/story.php?id=1290

  • Anonymous
    June 13, 2009
    PingBack from http://outdoordecoration.info/story.php?id=1385

  • Anonymous
    June 14, 2009
    PingBack from http://patiosetsite.info/story.php?id=15

  • Anonymous
    June 17, 2009
    PingBack from http://patiosetsite.info/story.php?id=94

  • Anonymous
    June 17, 2009
    PingBack from http://patioumbrellasource.info/story.php?id=1776

  • Anonymous
    June 18, 2009
    PingBack from http://gardendecordesign.info/story.php?id=5154

  • Anonymous
    June 18, 2009
    PingBack from http://fancyporchswing.info/story.php?id=2253

  • Anonymous
    June 18, 2009
    PingBack from http://adirondackchairshub.info/story.php?id=772

  • Anonymous
    December 03, 2009
    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..

  • Anonymous
    March 23, 2010
    I have some dll's . I want to identify which are managed and which unmanaged. Please do guide me.

  • Anonymous
    April 02, 2010
    "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.

  • Anonymous
    April 14, 2010
    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

  • Anonymous
    July 31, 2010
    A good example can be found at following link www.apachetechnology.net/.../NativeDLL2Net.aspx

  • Anonymous
    September 07, 2010
    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?

  • Anonymous
    January 13, 2011
    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?

  • Anonymous
    January 13, 2011
    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?

  • Anonymous
    August 01, 2011
    @ 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 ...

  • Anonymous
    March 12, 2012
    I am getting the "Unable to find an entry point named 'PassStructIn' in DLL 'dllname.dll' "

  • Anonymous
    August 08, 2012
    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?

  • Anonymous
    March 28, 2014
    Nice one... very clearly explained.:)