Register and Unregister COM DLL from .NET Code

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;

[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName);

[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr LoadLibrary(string lpFileName);

[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool FreeLibrary(IntPtr hModule);

internal delegate int PointerToMethodInvoker();

public Registrar(string filePath)
{
hLib = LoadLibrary(filePath);
if (IntPtr.Zero == hLib)
{
int errno = Marshal.GetLastWin32Error();
throw new Win32Exception(errno, "Failed to load library.");
}
}

public void RegisterComDLL()
{
CallPointerMethod("DllRegisterServer");
}

public void UnRegisterComDLL()
{
CallPointerMethod("DllUnregisterServer");
}

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

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.

5 thoughts on “Register and Unregister COM DLL from .NET Code”

  1. Hi Geoff,

    I used your code to register and unregister some DLL.
    I had a problem when i registered a dll.
    Calling the register method in the following way has some problem :


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

    because…in the Dispose() method it is called the method UnRegisterComDLL()…so I couldn’t to register a DLL in the right way.
    Did you notice this thing ?
    Cheers.

    Stefano

  2. @Stafano,
    Right the idea is to register the DLL, call some code that needs that DLL, and then unregister it. This is not intended to Register a DLL forever. If you want to do that, you’ll need to change the code in the example.

  3. Hi Geoff,

    You are 100% right; I explained myself in the wrong way…I didn’t intend to “say” that it was a mistake….my needs are different compare to the needs of the example.
    Infact, I need to maintain the registration of a DLL.
    Thank you very for you answer….in you opinion what would be the best way to modify your code to mantain your idea and satisfy my needs ?
    Cheers.

    Stefano

  4. @Stefano,
    Using the externs at the top you should be able to call something like:


    IntPtr hLib = LoadLibrary(filePath);
    if (IntPtr.Zero == hLib)
    {
    int errno = Marshal.GetLastWin32Error();
    throw new Win32Exception(errno, "Failed to load library.");
    }

    IntPtr dllEntryPoint = GetProcAddress(hLib, "DllRegisterServer");
    if (IntPtr.Zero == dllEntryPoint)
    {
    throw new Win32Exception(Marshal.GetLastWin32Error());
    }
    PointerToMethodInvoker drs =
    (PointerToMethodInvoker) Marshal.GetDelegateForFunctionPointer(dllEntryPoint,
    typeof(PointerToMethodInvoker));
    drs();

  5. If you’re getting an exception similar to the following, make sure you’re registering the COM dll once only:

    System.InvalidCastException occurred
    Message=Unable to cast COM object of type ‘System.__ComObject’ to interface type ‘P4COM.p4′. This operation failed because the QueryInterface call on the COM component for the interface with IID ‘{E7D963D4-7AC8-4B89-B768-16F900DB2A93}’ failed due to the following error: No such interface supported (Exception from HRESULT: 0×80004002 (E_NOINTERFACE)).
    Source=Perforce
    StackTrace:
    at SourceLog.Plugin.Perforce.PerforcePlugin.CheckForNewLogEntries(Object state)
    InnerException:

    I’m using a static constructor to ensure only one registration:


    public class PerforcePlugin
    {
    static PerforcePlugin()
    {
    Registrar registrar = new Registrar(
    Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\p4com.dll");
    registrar.RegisterComDLL();
    finalizer = new Finalizer(registrar);
    }

    static readonly Finalizer finalizer;

    sealed class Finalizer
    {
    private Registrar _registrar;
    internal Finalizer(Registrar registrar)
    {
    _registrar = registrar;
    }

    ~Finalizer()
    {
    _registrar.Dispose();
    }
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>