Dynamically calling an unmanaged dll from .NET (C#)
This sample is in response to a question left on my previous post, namely how to call an unmanaged dll from managed code when the dll in question isn't known until runtime (for instance, the path is stored in the registry, or an xml file, etc etc).
Apologies if this sample seems a little hurried, but I have another presentation to write and so time is short!
So let's begin.
To start and to refresh our memories, let's create a very basic C++ dll that does very little..... your code should resemble the following (check out my previous post for more info on this):
Header file
extern "C" __declspec(dllexport) int MultiplyByTen(int numberToMultiply);
Source code file
#include "DynamicDLLToCall.h"
int MultiplyByTen(int numberToMultiply)
{
int returnValue = numberToMultiply * 10;
return returnValue;
}
As you can probably infer from the function name, an int is passed into this function and it will return the number passed in multiplied by ten. Told you it would be simple.
Now comes the more interesting part, actually calling this dll dynamically from your C# source code. There are two Win32 functions that are going to help us do this:
1) LoadLibrary - returns a handle to the dll in question
2) GetProcAddress - obtain the address of an exported function within the previously loaded dll
The rest is rather simple. We use LoadLibrary and GetProcAddress to get the address of the function within the dll we want to call, and then we use the GetDelegateForFunctionPointer static method within the Marshal class to assign this address to a C# delegate that we define. Take a look at the following C# code:
static class NativeMethods
{
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int MultiplyByTen(int numberToMultiply);
static void Main(string[] args)
{
IntPtr pDll = NativeMethods.LoadLibrary(@"PathToYourDll.DLL");
//oh dear, error handling here
//if (pDll == IntPtr.Zero)
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "MultiplyByTen");
//oh dear, error handling here
//if(pAddressOfFunctionToCall == IntPtr.Zero)
MultiplyByTen multiplyByTen = (MultiplyByTen)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(MultiplyByTen));
int theResult = multiplyByTen(10);
bool result = NativeMethods.FreeLibrary(pDll);
//remaining code here
Console.WriteLine(theResult);
}
}
The only item worthy of note is the UnmanagedFunctionPointer attribute, which was introduced to version 2.0 of the .NET framework, check out the docs online for more information.
Hope this helps.
Comments
Anonymous
October 04, 2006
Great follow-up Jonathan. Could you speak to any performance issues related to this type of mechanism. (Marhsalling, some-type-of-reflection, etc.).Anonymous
October 06, 2006
Thanks for posting a helpful response. Much appreciated. PS. Is it safe to assume that at some point you should call the native function FreeLibrary or is that uneccessary? =BAnonymous
October 09, 2006
Hi Steve and Blairio. First off, when explicitly calling LoadLibrary FreeLibrary should indeed be called. I'll modify my sample to show this call. As well as this, I'm going to add another post that shows how to free memory that was allocated by unmanaged code that the runtime can't clean up for you. It's quite interesting. Steve, in terms of performance then whichever way you look at it interop is going to be a hit, no question. The whole topic of performance is very subjective and depends entirely on your targets. What's slow to one application may well be quick to another. Importantly, how slow a particular interop call is seen to be depends on the rest of the request the call is - if database calls/IO etc are in the mix then I've no doubt the interop call will be a very small percentage of the whole, and so even if perceived as 'slow' won't make much difference to the overall performance. If however you're making an interop call in a tight loop in a request that only performs calculations, you could well feel the hit. If you have a particular scenario in mind please let me know, and I'll decompose it on here for you and show the most performant way to attack it. There are techniques to use that can help matters. Thanks guysAnonymous
October 10, 2006
Hello again Jonathan, Thanks for the followup. I didn't really have a particular scenario in mind...just a feeling that there might be some gotchyas there. You always here that kind of thing when talking about reflection and I wasn't sure if they were similar ideas (in the dynamic sense I mean). Like everything else though, it is a trade-off...and if you can use existing, robust functions maybe the "delay" is worthwhile. Anyways, thanks again. Looking forward to more.Anonymous
October 10, 2006
No worries Steve. After I've put up a post or two showing how to clear unmanaged memory from managed code I'll devote one or two to different aspects of interop performance, and looking at what types to use to increase performance and also refactoring for performance. Hopefully these will answer some or all of your questions. CheersAnonymous
October 17, 2006
Thanks Jonathan! This is exactly what we've been looking for...Anonymous
October 30, 2006
I'm working with a native library that exports a good number of functions. The problem is that I need to be able to unload the DLL, replace it, and then reload it. I've noticed that apparently DllImport only does a LoadLibrary() and GetProcAddress() the first time you call an imported function. So if you FreeLibrary() then LoadLibrary() it is possible it will be loaded into a different address space, so the old function pointers will no longer be valid and you will get access violations. Currently I've got a class something like this: class NativeWrapper { NativeWrapper(string filename) { m_strFileName = filename; } Load() { m_hModule = LoadLibrary(m_strFileName); } Free() { FreeLibrary(m_hModule); m_pfnSomeMethod = null; } int SomeMethod() { if(m_pfnSomeMethod == null) { //pseudocode; you get the idea m_pfnSomeMethod = (SomeMethodDelegate)GetProcAddress(m_hModule, "SomeMethod"); } return m_pfnSomeMethod(); } } I have like 30 functions implemented exactly the same way, and I need to do this for about 5 or 6 libraries I am using. My question is this: is there any way to automate this? I was thinking of implementing an attribute similar to DllImport, but I don't know where to start or if that is even possible. The way I understand it, DllImport basically calls LoadLibrary() the first time any function in the library is called, and GetProcAddress() the first time each function is called, and caches the results. Is DllImport a standard attribute, or does it have some kind of special consideration in the CLR?Anonymous
March 30, 2007
I declare functions for excel export like that <DllImport("ExcelFG.dll", EntryPoint:="SetCell")> _ Shared Sub SetCell(ByVal nRow As Integer, ByVal nColumn As Integer, ByVal cellValue As Byte()) End Sub <DllImport("ExcelFG.dll", EntryPoint:="WriteFile")> _ Shared Sub WriteFile() End Sub And loading the dynamic dll path like that Dim DLLPath As String DLLPath = Server.MapPath("bin") & "ExcelFG.dll" Dim mlib As IntPtr = LoadLibrary(DLLPath) If mlib = 0 Then Response.Write("Load Library Fail" & Server.MapPath("bin") & "ExcelFG.dll") Exit Sub End If And free the loaded library with FreeLibrary(mlib) Everything is work fine for my asp application. But after I call the function, ExcelFG.dll is access deny and can't delete or overwrite.Anonymous
July 25, 2007
thank you so very much for posting this its a very elegant fix for this problem.Anonymous
July 29, 2007
Gr8 work indeed. It has solved my problem. Thanx a lot.Anonymous
October 15, 2007
very helpful article!! thank you very much!!Anonymous
December 11, 2007
add this to the top of your code to use DllImport: using System.Runtime.InteropServices;Anonymous
March 25, 2008
thank you very much but your .net framework is 2005, this 'Marshal.GetDelegateForFunctionPointer' function not provider by vs2003 i want to ynamically call an unmanaged dll from .NET2003 (C#) you can help me thanks my eamil is "mingfei2007316@yahoo.com.cn"Anonymous
April 02, 2008
I have a FunctionInC (char* &dataPtr), how do I get the data from dataPtr? Thanks!Anonymous
May 08, 2008
Hello, Great article, thanks for the hardwork. Do you know how to use GetProcAddress with entry point numbers instead of names? I have a .def file containing entry points with noname and only numbers. This page.... http://msdn.microsoft.com/en-us/library/ms683212(VS.85).aspx Says that is it possible using a "low-order word". I have no idea what in c# would come close to a low order word. Any help you can offer would be greatly appreciated..Anonymous
May 08, 2008
Hi, Suppose I only know the signature of the native function I am calling at runtime (so I can't explicitly set up the appropriate delegate), is there a way to call the native function? Any help is appreciated!Anonymous
August 27, 2008
Thanks for a handy piece of code. Just what I wanted.Anonymous
October 09, 2008
Ahhh, just what I was looking for!! I was running in to a 126 error when trying to run load my legacy DLL and it turned out that it was because of the dependencies of the DLL itself. The dependencies where loaded in the same directory as the DLL I was trying to load but apparently I need them in the %PATH% in order for it to load properly. I fixed the issue by doing this: string path = Environment.GetEnvironmentVariable( "PATH" ); path += ";"+"My Path"+""; Environment.SetEnvironmentVariable( "PATH", path ); This sets the environment variable for the process while it is running and has no effect on the system itself. Hope that saves someone pulling hair out as much hair as I did.Anonymous
January 01, 2009
I have a question regarding calling C++ from C#. What about variable types compatibility. In case of int, float, double, and bool, It is good. But, I have one C++ function for printing in a file that uses ostream. When I use it in C# I tried to use streamwriter but it did not work it gives me violation exception. Any tips ThanksAnonymous
January 13, 2009
Brilliant tutorial Jonathan. I'd been trying to work out how to do this for ages. I found it was very important to use dumpbin to find out what the real function names of the dll are as the c compiler had prefixed "_" and postfixed "@0" to all my functions and until I discovered this my code didn't work.Anonymous
February 24, 2009
When I call FreeLibrary with correct handle of the loaded library, program hangs !!! What is the reason ?Anonymous
March 04, 2009
I'm also having problems when calling FreeLibrary, the app hangs and when I'm in debug I get "memory couldn't be read at 0x00000" or something like that. found this which might indicate the problem http://blogs.msdn.com/jmstall/archive/2007/01/06/Typesafe-GetProcAddress.aspxAnonymous
April 20, 2009
@ Brad Figler, thanks for your solution to error 126 i definitely was pulling my hair out!Anonymous
April 23, 2009
Thank you very much for doing this. I don't have to deal with unmanaged/cli C++ anymore.Anonymous
May 22, 2009
The comment has been removedAnonymous
November 25, 2009
Thank you very much for this code snippet. Because of it I have been able to successfully compile my application for any cpu and dynamically load x86/x64 native assemblies as required by the running platform. Best Regards, Se7en SoftAnonymous
December 11, 2009
Great piece of code! Is there a way to tell your C# project to include the file.. pull it into the bin directory even though you are doing the late binding? I am trying to reference a third party app and want to make sure that it gets pulled to the bin to make the the deployment easier. Any ideas on how to do this?Anonymous
January 08, 2010
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Reflection; using System.IO; using System.Runtime.InteropServices; using System.Threading; namespace Plug_Load { public partial class Form1 : Form { #region P/Invoke [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string lpFileName); [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32.dll")] static extern IntPtr GetModuleHandle(string lpFileName); [DllImport("kernel32.dll")] static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags); [DllImport("user32.dll", EntryPoint = "LoadMenu")] public static extern int LoadMenu(int hInstance, string lpString); [DllImport("user32.dll", EntryPoint = "LoadMenuIndirect")] public static extern int LoadMenuIndirectA(int lpMenuTemplate); [DllImport("user32.dll")] static extern int LoadString(IntPtr hInstance, int uID, StringBuilder lpBuffer, int nBufferMax); [DllImport("user32.dll")] public static extern IntPtr LoadImage(IntPtr hInstance, int uID, uint type, int width, int height, int load); [DllImport("user32.dll")] public static extern IntPtr LoadBitmap(IntPtr hInstance, int uID); [DllImport("user32.dll")] public static extern IntPtr LoadIcon(IntPtr hInstance, int uID); [DllImport("kernel32.dll")] public static extern IntPtr FindResource(IntPtr hModule, int lpID, string lpType); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); [DllImport("kernel32.dll", SetLastError = true)] public static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); [DllImport("user32.dll")] public static extern IntPtr LoadMenu(IntPtr hInstance, int uID); private Thread t = null; public string pathe = Directory.CreateDirectory("Plugins").Name; public string[] files = Directory.GetFiles("Plugins", "*.dll"); public const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; #endregion public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //windows exit Close(); } private void Form1_Load(object sender, EventArgs e) { t = new Thread(new ThreadStart(Plugin_loader)); t.Start(); } private void Plugin_loader() { foreach (string file in files) { FileInfo fr = new FileInfo(file); IntPtr hMod = LoadLibraryEx(fr.FullName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); Encoding.Default.GetString(BitConverter.GetBytes(36)); IntPtr hRes = FindResource(hMod, 36, "Bitmap"); uint size = SizeofResource(hMod, hRes); IntPtr pt = LoadResource(hMod, hRes); FreeLibrary(hMod); Bitmap bmp; byte[] bPtr = new byte[size]; Marshal.Copy(pt, bPtr, 0, (int)size); using (MemoryStream m = new MemoryStream(bPtr)) bmp = (Bitmap)Bitmap.FromStream(m); menuStrip1.Items.Add("Image", bmp); } t.Abort(); } private void button2_Click(object sender, EventArgs e) { } } }Anonymous
February 18, 2010
When I compile the app, I get the following error 'NativeMathWrapper.NativeMath.NativeMethods' does not contain a definition for 'LoadLibrary' I havea using System.Runtime.InteropServices; Also I am using VS 2010 RC. Any idea what the problem coule be? thanks JasAnonymous
February 18, 2010
Oops, I meant 'System.Runtime.InteropServices.NativeMethods' is inaccessible due to its protection levelAnonymous
June 07, 2010
The comment has been removedAnonymous
October 10, 2011
Thanks Jonathan for a an excellent blog, this is exactly what I want to do :o)Anonymous
October 12, 2011
I know this is an old blog but I'm having an issue that I thought I would throw out there to see if anyone has seen this. I am successful at loading my C++ DLL and calling the methods within that DLL, but the issue I'm having is that I am trying to store an instance of the delegate in my class so that I can call it later on in my program when needed. So in other words, I load everything up on my form load and then I want to call my delegate method when the user clicks a button. At the time of the button click though, my delegate is null so it crashes if I try to call it. I have verified that it does indeed work if I call the method right after my call to GetProcAddress. Has anyone seen this? TIAAnonymous
October 12, 2011
Ok - disregard my previous post. It was a stupid user error. "Stupid User" being the key words. ;-) I was refactoring my code and did not remove the declaration of my objects from within the method so I had a locally scoped object named the same as my class object and this was causing my problem.Anonymous
July 29, 2012
Thanks for you efforts. It helped me a lot.Anonymous
September 20, 2012
It won't compile if I use: extern "C" __declspec(dllexport) int MultiplyByTen(int numberToMultiply); but it will compile if I use: extern "C" __declspec(dllexport) int MultiplyByTen(int); VS2010, standard MS compiler. It took me a while to figure that out via other (newer) articles so it might be worth noting because these are probably some changes for newer versions of compilers or VS, and might be a typical problem if people upgrade (or maybe I'm the only one).Anonymous
November 11, 2012
Could someone tell me what is going wrong with this code: I am trying to talk to the following C++ code function: void SortStructDescF(long pStruct, int numStructures, short structSize, short numSortValues ) { mSortValues = numSortValues; qsort((void*)pStruct, numStructures, structSize, CompareFloatsDesc); } in a .NET application. I am using the following declarations for the C++ function in the .NET application: <DllImport("OptiDll.dll",EntryPoint:="SortStructDescF")> _ Public Shared Function SortStructDescF(ByRef pStruct0 As Object, ByVal numStructures As Integer, ByVal structSize As Short, ByVal outstrmax As Short) As Integer End Function When I try and call the function SortStructDescF in the .NET application using: SortStructDescF(mLeftPoints(0), pointCount, CShort(LenB(mLeftPoints(0))), CShort(1))SortStructDescF(mLeftPoints(0), pointCount, CShort(LenB(mLeftPoints(0))), CShort(1)) The application exits with the error: MyApplication.exe: Managed' has exited with code -1073740791 (0xc0000409).Anonymous
November 11, 2012
Could someone tell me what is going wrong with this code: I am trying to talk to the following C++ code function: void SortStructDescF(long pStruct, int numStructures, short structSize, short numSortValues ) { mSortValues = numSortValues; qsort((void*)pStruct, numStructures, structSize, CompareFloatsDesc); } in a .NET application. I am using the following declarations for the C++ function in the .NET application: <DllImport("OptiDll.dll",EntryPoint:="SortStructDescF")> _ Public Shared Function SortStructDescF(ByRef pStruct0 As Object, ByVal numStructures As Integer, ByVal structSize As Short, ByVal outstrmax As Short) As Integer End Function When I try and call the function SortStructDescF in the .NET application using: SortStructDescF(mLeftPoints(0), pointCount, CShort(LenB(mLeftPoints(0))), CShort(1))SortStructDescF(mLeftPoints(0), pointCount, CShort(LenB(mLeftPoints(0))), CShort(1)) The application exits with the error: MyApplication.exe: Managed' has exited with code -1073740791 (0xc0000409).