Skip to content

Instantly share code, notes, and snippets.

@b0urb4k1
Last active June 11, 2025 22:34
Show Gist options
  • Select an option

  • Save b0urb4k1/da912b4e047583fdb56af9fe37c3047d to your computer and use it in GitHub Desktop.

Select an option

Save b0urb4k1/da912b4e047583fdb56af9fe37c3047d to your computer and use it in GitHub Desktop.
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);
}
}
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;
}
}
}
}
https://blogs.msdn.microsoft.com/jmstall/2005/02/21/tool-to-allow-inline-il-in-c-vb-net/
@Unknown6656
Copy link

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.

@xgalaxy
Copy link

xgalaxy commented Apr 25, 2022

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