Following on from the previous C# article – dynamic loading of a C DLL at run-time, the next step is to make the dynamic loading functions into a class.
The class will be in global scope for this example – in practice I might put it into a namespace.
The class manages the DLL so that it can be unloaded or unloaded at any time. Since the DLL is an unmanaged assembly, the class will need a destructor to unload the DLL as well as a constructor to load it. Also, it is good practice to allow for deferred loading – that is, an instance of the class can be constructed without loading its associated DLL, then the DLL can be loaded on demand later.
So, the outline class looks like this:
class dynaloader
{
// construct an unloaded object
public dynaloader()
{
}
// construct a loaded object
public dynaloader(string dll_name)
{
}
// destructor
~dynaloader()
{
}
// test whether this object is unloaded or loaded
public bool loaded()
{
}
// load the DLL
public bool load(string name)
{
}
// unload the DLL
public bool unload()
{
}
// Generic method for loading a function from the DLL
public T load_function<T>(string name) where T : class
{
}
}
So, these functions now need implementing using the external WIN32 functions to load/unload the DLL and to load functions from the DLL.
The class needs to contain a handle that points to the DLL, i.e. contains it’s unmanaged address. This is managed by the constructors/destructor in conjunction with the load/unload methods:
using System;
using System.Runtime.InteropServices;
class dynaloader
{
private IntPtr m_dll = IntPtr.Zero;
public dynaloader()
{
}
public dynaloader(string dll_name)
{
load(dll_name);
}
~dynaloader()
{
if (loaded()) unload();
}
public bool loaded()
{
return m_dll != IntPtr.Zero;
}
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllToLoad);
public bool load(string name)
{
if (loaded()) unload();
m_dll = LoadLibrary(name);
if (!loaded())
return false;
return true;
}
[DllImport("kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hModule);
public bool unload()
{
if (!loaded()) return true;
if (!FreeLibrary(m_dll))
return false;
m_dll = IntPtr.Zero;
return true;
}
}
The final stage is to provide the generic method for loading a function from the DLL:
class dynaloader
{
...
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
public T load_function<T>(string name) where T : class
{
IntPtr address = GetProcAddress(m_dll, name);
if (address == IntPtr.Zero)
return null;
System.Delegate fn_ptr = Marshal.GetDelegateForFunctionPointer(address, typeof(T));
return fn_ptr as T;
}
}
In this case, the method returns null on failure, but it could instead throw an exception.
Thanks for the code! It was great to learn this.Thanks again.