Dynamically Loading DLLs and binding to them at Runtime in .NET 2.0
On marshaling / platform invocation it is sometimes necessary to load and bind to the DLLs at runtime. I got this problem on NHunspell, a .NET wrapper of the Open Office spell checker Hunspell, because it should be able to load the platform specific version (32Bit / 64Bit) of the native DLL and bind to it at runtime. This allows XCOPY deployment of this library. The basic steps to do this are:
- Get the platform via GetSystemInfo
- Load the specfic DLL via LoadLibarary
- Get the DLL functions viaGetProcAddress and convert them to delgates via Marshal.GetDelegateForFunctionPointer
- Use the Libaray
- After Use, free the libarary via FreeLibrary
Template File
A template for a static loader class is downloadable as release: Dynamic DLL Loader Code Template It contains instruction how to customize it for your needs. The sample below is made from this template.
Loader and wrapper for the Hunspell DLLs as a real wold sample:
All these steps can be encapsulated in a marshaling class for the specific library. In case of Hunspell i've choosen a internal static class.
internal static class MarshalHunspellDll
{
static IntPtr dllHandle = IntPtr.Zero;
internal delegate IntPtr HunspellInitDelegate([MarshalAs(UnmanagedType.LPWStr)] string affFile, [MarshalAs(UnmanagedType.LPWStr)] string dictFile, string key);
internal delegate void HunspellFreeDelegate(IntPtr handle);
internal delegate bool HunspellAddDelegate(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string word);
internal delegate bool HunspellAddWithAffixDelegate(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string word, [MarshalAs(UnmanagedType.LPWStr)] string affix);
internal delegate bool HunspellSpellDelegate(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string word);
internal delegate IntPtr HunspellSuggestDelegate(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string word);
static internal HunspellInitDelegate HunspellInit;
static internal HunspellFreeDelegate HunspellFree;
static internal HunspellAddDelegate HunspellAdd;
static internal HunspellAddWithAffixDelegate HunspellAddWithAffix;
static internal HunspellSpellDelegate HunspellSpell;
static internal HunspellSuggestDelegate HunspellSuggest;
internal delegate IntPtr HyphenInitDelegate([MarshalAs(UnmanagedType.LPWStr)] string dictFile);
internal delegate void HyphenFreeDelegate(IntPtr handle);
internal delegate IntPtr HyphenHyphenateDelegate(IntPtr handle, [MarshalAs(UnmanagedType.LPWStr)] string word );
static internal HyphenInitDelegate HyphenInit;
static internal HyphenFreeDelegate HyphenFree;
static internal HyphenHyphenateDelegate HyphenHyphenate;
static MarshalHunspellDll()
{
try
{
// Initialze the dynamic marshall Infrastructure to call the 32Bit (x86) or the 64Bit (x64) Dll respectively
SYSTEM_INFO info = new SYSTEM_INFO();
GetSystemInfo( ref info );
// Load the correct DLL according to the processor architecture
switch( info.wProcessorArchitecture )
{
case PROCESSOR_ARCHITECTURE.Intel:
string pathx86 = AppDomain.CurrentDomain.BaseDirectory;
if (!pathx86.EndsWith("\\"))
pathx86 += "\\";
pathx86 += Resources.HunspellX86DllName;
dllHandle = LoadLibrary(pathx86);
if (dllHandle == IntPtr.Zero)
throw new DllNotFoundException(string.Format(Resources.HunspellX86DllNotFoundMessage, pathx86));
break;
case PROCESSOR_ARCHITECTURE.Amd64:
string pathx64 = AppDomain.CurrentDomain.BaseDirectory;
if (!pathx64.EndsWith("\\"))
pathx64 += "\\";
pathx64 += Resources.HunspellX64DllName;
dllHandle = LoadLibrary(pathx64);
if (dllHandle == IntPtr.Zero)
throw new DllNotFoundException(string.Format(Resources.HunspellX64DllNotFoundMessage, pathx64));
break;
default:
throw new NotSupportedException(Resources.HunspellNotAvailabeForProcessorArchitectureMessage + info.wProcessorArchitecture.ToString());
break;
}
HunspellInit = (HunspellInitDelegate) GetDelegate("HunspellInit", typeof(HunspellInitDelegate));
HunspellFree = (HunspellFreeDelegate)GetDelegate("HunspellFree", typeof(HunspellFreeDelegate));
HunspellAdd = (HunspellAddDelegate)GetDelegate("HunspellAdd", typeof(HunspellAddDelegate));
HunspellAddWithAffix = (HunspellAddWithAffixDelegate)GetDelegate("HunspellAddWithAffix", typeof(HunspellAddWithAffixDelegate));
HunspellSpell = (HunspellSpellDelegate)GetDelegate("HunspellSpell", typeof(HunspellSpellDelegate));
HunspellSuggest = (HunspellSuggestDelegate)GetDelegate("HunspellSuggest", typeof(HunspellSuggestDelegate));
HyphenInit = (HyphenInitDelegate)GetDelegate("HyphenInit", typeof(HyphenInitDelegate));
HyphenFree = (HyphenFreeDelegate)GetDelegate("HyphenFree", typeof(HyphenFreeDelegate));
HyphenHyphenate = (HyphenHyphenateDelegate)GetDelegate("HyphenHyphenate", typeof(HyphenHyphenateDelegate));
}
catch(Exception e)
{
if( dllHandle != IntPtr.Zero )
FreeLibrary( dllHandle );
throw e;
}
}
static Delegate GetDelegate(string procName, Type delegateType )
{
IntPtr procAdress = GetProcAddress(dllHandle, procName);
if (procAdress == IntPtr.Zero)
throw new EntryPointNotFoundException("Function: " + procName);
return Marshal.GetDelegateForFunctionPointer(procAdress, delegateType);
}
internal enum PROCESSOR_ARCHITECTURE : ushort
{
Intel = 0,
MIPS = 1,
Alpha = 2,
PPC = 3,
SHX = 4,
ARM = 5,
IA64 = 6,
Alpha64 = 7,
Amd64 = 9,
Unknown = 0xFFFF
}
[StructLayout(LayoutKind.Sequential)]
internal struct SYSTEM_INFO
{
internal PROCESSOR_ARCHITECTURE wProcessorArchitecture;
internal ushort wReserved;
internal uint dwPageSize;
internal IntPtr lpMinimumApplicationAddress;
internal IntPtr lpMaximumApplicationAddress;
internal IntPtr dwActiveProcessorMask;
internal uint dwNumberOfProcessors;
internal uint dwProcessorType;
internal uint dwAllocationGranularity;
internal ushort dwProcessorLevel;
internal ushort dwProcessorRevision;
}
[DllImport("kernel32.dll")]
internal static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);
[DllImport("kernel32.dll")]
internal static extern IntPtr LoadLibrary(string fileName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
}
}
Links:
|