C# – building a dynamic DLL loader

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);
        return loaded();
    }

    [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.

Comments

Leave a Reply