Last active
June 11, 2025 22:34
-
-
Save b0urb4k1/da912b4e047583fdb56af9fe37c3047d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Runtime.InteropServices; | |
| class Program | |
| { | |
| private delegate Int32 MyAdd(Int32 x, Int32 y); | |
| private static void Main() | |
| { | |
| // A simple Add function | |
| Byte[] myAddNativeCodeBytes = new Byte[] | |
| { | |
| 0x8B, 0x44, 0x24, 0x08, // mov eax,dword ptr [esp+8] | |
| 0x8B, 0x4C, 0x24, 0x04, // mov ecx,dword ptr [esp+4] | |
| 0x03, 0xC1, // add eax,ecx | |
| 0xC2, 0x08, 0x00 // ret 8 | |
| }; | |
| // We need to push the code bytes into a native buffer | |
| IntPtr myAddNativeCodeBytesPtr = IntPtr.Zero; | |
| try | |
| { | |
| // Allocate the native buffer | |
| myAddNativeCodeBytesPtr = | |
| Marshal.AllocCoTaskMem(myAddNativeCodeBytes.Length); | |
| // Push the code bytes over | |
| Marshal.Copy(myAddNativeCodeBytes, 0, | |
| myAddNativeCodeBytesPtr, myAddNativeCodeBytes.Length); | |
| // Get a function pointer for the native code bytes | |
| MyAdd myAdd = (MyAdd)Marshal.GetDelegateForFunctionPointer( | |
| myAddNativeCodeBytesPtr, typeof(MyAdd)); | |
| // Call the native code bytes | |
| Int32 result = myAdd(4, 5); | |
| // Did it work? | |
| Console.WriteLine(“Result: {0}”, result); | |
| } | |
| finally | |
| { | |
| // Free the native buffer | |
| if (myAddNativeCodeBytesPtr != IntPtr.Zero) | |
| { | |
| Marshal.FreeCoTaskMem(myAddNativeCodeBytesPtr); | |
| myAddNativeCodeBytesPtr = IntPtr.Zero; | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| https://blogs.msdn.microsoft.com/jmstall/2005/02/21/tool-to-allow-inline-il-in-c-vb-net/ |
What is the advantage of this over using DllImport P/Invoke?
I’m assuming performance.
I’m struggling to find any good blogs or articles discussing this and MS documentation makes no mention of this way of doing it in any of their interop documentation.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Do note that the syntax
delegate*<int, int, int>is now available for C#9.Variables of this type can be used as a pointer or as an (unmanaged) delegate.