Click here to Skip to main content
Click here to Skip to main content

Dynamic Invoke C++ DLL function in C#

By , 26 Jun 2008
 

Introduction

This article introduces why I use dynamic invoke C++ DLL function in C# and how to call it.

Why I Use Dynamic Invoke Instead of Static Invoke?

Sometimes when I want to write and call some unmanaged function (C++ DLL function), I do the following:

In the *.cpp file...

int __declspec(dllexport) a(int b)
{
      return b;
}

... compile the *.cpp file to a *.dll file and in the C# file:

class Program
{
   [DllImport(@"c:\a.dll")]
   private static extern int a(int b);
   static void Main(string[] args)
   {
       Console.WriteLine(a(3));
   }
}

In this method, you see that I have to declare a static DLL filename, and when I compile to an excutable file (*.exe), I cannot change the DLL filename.

The second case is, I want to write some C++ DLL function for an ASP.NET/IIS (Internet Information Services) Web site. If I static invoke unmanaged, when I want to update the DLL file, I stop the Web site in IIS and replace the old file with the new file, but .. IIS still keeps the old DLL file. It does not release the file, and I have to stop the entire Web site, all services running in IIS for replacing the file.

So, I want to dynamically invoke an unmanaged file. I can load, invoke and free unmanaged DLL function initiatively.

How to Dynamic Invoke Unmanaged DLL Function?

The first way is by referring to dynamicinvokedll.aspx.

The second way that I want to introduce is how to use the WindowAPI function. We use three functions in kernel32.dll (this is a kernel DLL file and appears in [WindowRoot]\System32) LoadLibrary, GetProcess, and FreeLibrary. You can find the C++ declare type functions on MSDN (msdn.com). I only show the declare in C#:

class Program
{
        [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
        static extern int LoadLibrary(
            [MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
        static extern IntPtr GetProcAddress( int hModule,
            [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

                [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
        static extern bool FreeLibrary(int hModule);
}

The LoadLibrary function loads an unmanaged file, the GetProcAddress function gets the function pointer of an unmanaged file and the FreeLibrary function frees the unmanaged file. Now I can get the function pointer in a.dll very easily:

int hModule = LoadLibrary(@"c:\a.dll");
if (hModule == 0) return false;
IntPtr intPtr = GetProcAddress(hModule, "a");

And when I want to free the a.dll file, I can call:

FreeLibrary(hModule);

But C# doesn’t suport C++ function pointer, so we cannot invoke a C++ function pointer here. C# only has Delegate objects and we have to convert the function pointer to Delegate by Marshal.GetDelegateForFunctionPointer. It is declared in System.Runtime.InteropServices as follows:

public static Delegate GetDelegateForFunctionPointer (
      IntPtr ptr,
      Type t
)

(You can find more support for this function in MSDN.)

We first declare the Delegate for a function:

delegate int A(int b);

And get the delegate object as follows:

A a = (A)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(A));

Now we can invoke a function as follows:

static void Main(string[] args)
{
    Console.WriteLine(a(3));
}

Hope you found this article helpful.

History

  • 26th June, 2008: Initial post

License

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

About the Author

Namdn
Engineer NaisCorp - Socbay.com
Vietnam Vietnam
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralThis helps me a lot!!!!memberKydz_Leo19 May '12 - 5:01 
As is said above, gratitude~~
QuestionIt's very goodmemberAndrewpeter22 Mar '12 - 21:47 
My vote is 5. Thanks.
GeneralMy vote of 5memberAndrewpeter22 Mar '12 - 21:46 
It's very good!
GeneralMy vote of 5membercmma8624 Feb '12 - 4:48 
Alright
GeneralMy vote of 5memberMorries13 Dec '11 - 15:38 
Good
QuestionPInvoke function unbalanced the stackmemberSilaghi20 Nov '11 - 1:29 
Hi, I'm just starting to use DLLs. I found your tutorial very helpful , however whenever I try debugging the code in visual studio 2010 I keep getting
A call to PInvoke function 'ConsoleApplication1!ConsoleApplication1.Program+func::Invoke' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PIn
voke signature match the target unmanaged signature.
 
Running it without debugging outputs the correct answer ,however I would like to know what is causing this Smile | :)
Cheers
GeneralMy vote of 5memberXanblax11 Oct '11 - 6:01 
You explained pretty clear something that was not so obvious.
GeneralMy vote of 4memberJimmyGong24 May '11 - 16:00 
very detailed
GeneralMy vote of 5memberJangjeet Singh25 Apr '11 - 4:26 
Clear, concise, runnable example that produces easily-verifiable output! Thanks Namdn
GeneralMy vote of 5memberLeKoosy31 Mar '11 - 3:27 
easy to follow
GeneralI have a problemmemberhenry369521 Nov '10 - 0:30 
My System is 64-bit Windows
 
use this method
 
have a problem
 
 [DllImport("kernel32.dll", EntryPoint = "LoadLibraryA")]
        static extern int LoadLibrary(
            [MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
 
        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
        static extern IntPtr GetProcAddress(int hModule,
            [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
 
        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
        static extern bool FreeLibrary(int hModule);
 
        public delegate int Add(int a, int b);
        static void Main(string[] args)
        {
            int hModule = LoadLibrary("E:\\DotNetTest\\DotNetTest\\bin\\Debug\\MyDll.dll");
            if (hModule == 0) 
                return;
            IntPtr intPtr = GetProcAddress(hModule, "Add");
            Add afn = (Add)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(Add));
            Console.WriteLine(afn(1,3));
            FreeLibrary(hModule);
            Console.ReadLine();
        }
 
the hModule  value is  zero
 
I am sure the path is right
 


GeneralRe: I have a problem [modified]memberAli Fakoor1 Oct '11 - 22:16 
I had the similar issue and solved it, read below:
 
Symptom


Path is not recognized, however visually correct!
 
Reason


The path is interpreted unicode-ly, but should be treated as ASCII to work.
 
Solution


Please add CharSet = CharSet.Ansi to your DllImport attributes list for the LoadLibrary function entry:
[DllImport ..., CharSet = CharSet.Ansi , ...]
 
Result


The inputted string (path of the dynamic load library) is interpreted correctly in ASCII and thus recognized by the API.
 
Finally


Come on!, vote up Thumbs Up | :thumbsup: and Have fun Wink | ;-)

modified 14 Jul '12 - 2:26.

GeneralRe: I have a problemmemberpurgatory28 May '12 - 21:40 
Your problem is misunderstanding pointer.
 
In x64 environment, HMODULE is 8 bytes long.
 
Use IntPtr instead int
 
public class MyDynamicDll : Idisposible
{
    [DllImport("kernel32.dll", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode)]
    private extern IntPtr LoadLibrary(
               [MarshalAs(UnmanagedType.LPWStr)] string lpLibFileName);
 
    [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", CharSet = CharSet.Ansi)]
    private extern IntPtr GetProcAddress(IntPtr hModule,
               [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
 
    [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
    private extern bool FreeLibrary(IntPtr hModule);
 
    private IntPtr _module;
    private delegate int _ADD_(int a, int b);
    private _ADD_ AddUsingDLL;
 
    public MyDynamicDll()
    {
        _module = IntPtr.Zero;
 
        if (4 == IntPtr.Size)
        {
            _module = LoadLibrary("MyModule32.dll");
        }
        else
        {
            _module = LoadLibrary("MyModule64.dll");
        }
 
        if (IntPtr.Zero == _module)
        {
            if (4 == IntPtr.Size)
            {
                System.Windows.Forms.MessageBox.Show("Can't load Module32.dll");
                throw new Exception("Can't load Module32.dll");
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("Can't load Module64.dll");
                throw new Exception("Can't load Module64.dll");
            }
        }
 
        IntPtr fp = GetProcAddress(_module, "ADD");
        AddUsingDLL = (_ADD_)Marshal.GetDelegateForFunctionPointer(fp, typeof(_ADD_));
    }
 
    ~MyDynamicDll()
    {
        Dispose();
    }
 
    public void Dispose()
    {
        if (IntPtr.Zero != _module)
        {
            FreeLibrary(_module);
            _module = IntPtr.Zero;
        }
    }
 
    public int Add(int a, int b)
    {
        return AddUsingDLL(a, b);
    }
}
 

above code contains some compile error maybe... Poke tongue | ;-P
GeneralA note about FreeLibrary()membermaeloc26 Jun '08 - 21:26 
Only a call to FreeLibrary() doesn't really free the invoked library (monitor the IIS pool process with ProcessExplorer and you will see the invoked dll still loaded after the first FreeLibrary). Only a second call to FreeLibrary, frees it truly.
See http://blogs.msdn.com/eldar/archive/2006/09/22/how-to-call-c-dll-from-asp-net-c-code.aspx
GeneralRe: A note about FreeLibrary()membertwaindev1 Jul '08 - 5:18 
In that code the library is loaded twice, which is not the case in the code above, so one call to FreeLibrary is enough to unload it.

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 26 Jun 2008
Article Copyright 2008 by Namdn
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid