Register and Unregister COM DLL from .NET Code

January 1, 2009 - 4 minute read -
com csharp

Background

The command regsvr32 is used to register a native, unmanaged code DLL so that it is available via COM. With a registered DLL you can use COM Interop to call that code from .NET Managed code.

Regsvr32 is unmanaged code and as such makes use of some existing functions that are defined in the kernel32.dll. Fortunately .NET makes available a pretty easy to use foreign function interface (FFI) in the form of P/Invoke.

In general to call an unmanaged function you just need to use the DllImport annotation on an extern function to tell the CLR how to access the function.

e.g.:

[DllImport("shell32.dll")]
static extern int DllGetVersion(ref DLLVERSIONINFO pdvi);

Registering an Unmanaged DLL with C# Code

regsvr32 actually calls functions defined within the DLL itself in what is known as a self-registering DLL. So assuming your DLL is self-registering then you should be able to use this approach as well. The only thing we need to do is figure out what functions to call.

It ends up there are 2 basic functions: LoadLibrary and GetProcAddress.

LoadLibrary

LoadLibrary returns a handle the module (a pointer to a structure).

After you are done with you library you can clean up by calling FreeLibrary passing it the handle that was returned from LoadLibrary.

GetProcAddress

GetProcAddress finds a function defined in a module and returns a pointer to that function. A function pointer allows you to call a method in a dynamic way. It is functionally equivalent to a delegate in managed code.

In C e.g.:

(*some_func)();

Put it All Together

Now we have a basic algorithm to register a DLL:

  1. LoadLibrary to get a handle to the library
  2. GetProcAddress to get a function pointer to the proper function to register the DLL
  3. Call the function returned from GetProcAddress
  4. Cleanup

Mix that in with some error checking code and I got the following:

public class Registrar : IDisposable
{
    private IntPtr hLib;</p>
<p>    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
    internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);</p>
<p>    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern IntPtr LoadLibrary(string lpFileName);</p>
<p>    [DllImport("kernel32.dll", SetLastError = true)]
    internal static extern bool FreeLibrary(IntPtr hModule);</p>
<p>    internal delegate int PointerToMethodInvoker();</p>
<p>    public Registrar(string filePath)
    {
        hLib = LoadLibrary(filePath);
        if (IntPtr.Zero == hLib)
        {
            int errno = Marshal.GetLastWin32Error();
            throw new Win32Exception(errno, "Failed to load library.");
        }
    }</p>
<p>    public void RegisterComDLL()
    {
        CallPointerMethod("DllRegisterServer");
    }</p>
<p>    public void UnRegisterComDLL()
    {
        CallPointerMethod("DllUnregisterServer");
    }</p>
<p>    private void CallPointerMethod(string methodName)
    {
        IntPtr dllEntryPoint = GetProcAddress(hLib, methodName);
        if (IntPtr.Zero == dllEntryPoint)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
        PointerToMethodInvoker drs =
               (PointerToMethodInvoker) Marshal.GetDelegateForFunctionPointer(dllEntryPoint,
                           typeof(PointerToMethodInvoker));
        drs();
    }</p>
<p>    public void Dispose()
    {
      if (IntPtr.Zero != hLib)
      {
          UnRegisterComDLL();
          FreeLibrary(hLib);
          hLib = IntPtr.Zero;
      }
    }
}

Note: The requirement I was dealing with was a build script so I wanted to register the unmanaged DLL, use it, and then unregister it so the computer would be in its previous state. If you want to leave the DLL registered, such as for an install program, you would need to modify the above example.

To call this code you just need to pass it a path to the dll that needs to be registered.

using (Registrar registrar = new Registrar("path\\to\\com.dll"))
{
    registrar.RegisterComDLL();
    return base.Execute();
}

Resources: Check out pinvoke.net for a lot of good documentation and example of how to call native methods from managed code.