header image
 

Creating COM wrappers programatically

In my previous post I wrote about using Windows Installer’s COM object to edit MSI packages. To easily use COM methods you need a managed wrapper assembly for the particular native COM server DLL. Visual Studio can generate one automatically, or you can use TlbImp, a tool from Windows SDK. But what if you don’t have any of that? Don’t worry, there is still hope!

It’s very easy in fact, (almost) all you need is aptly named TypeLibConverter class in System.Runtime.InteropServices namespace. You also need AssemblyBuilder to actually create the assembly. And finally we need one import from oleaut32.dll — LoadTypeLibEx, which allows us to load type library embedded in an exe/dll without extracting it ourselves. So without further ado, here is the code:

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;

// This utility extracts COM type library definition from unmanaged DLL and creates matching managed wrapper assembly.

namespace Utils
{
    static class TlbConvert
    {
        private enum RegKind
        {
            RegKind_Default = 0,
            RegKind_Register = 1,
            RegKind_None = 2
        }

        [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
        private static extern void LoadTypeLibEx(String strTypeLibName, RegKind regKind,
            [MarshalAs(UnmanagedType.Interface)] out Object typeLib);

        static int Main(string[] args)
        {
            if (args.Length < 2)
            {
                Console.WriteLine("Usage: tlb-convert <unmanaged COM DLL path> <managed wrapper path> [root namespace]");
                return 2;
            }

            try
            {
                string srcDll = args[0];
                string outDll = args[1];
                string rootNamespace = args.Length == 3 ? args[2] : null;

                Object typeLib;
                LoadTypeLibEx(srcDll, RegKind.RegKind_None, out typeLib);
                TypeLibConverter tlbConv = new TypeLibConverter();
                AssemblyBuilder asm = tlbConv.ConvertTypeLibToAssembly(typeLib, outDll, 0, new ConversionEventHandler(), null, null, rootNamespace, null);
                asm.Save(outDll);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: {0}\n{1}", e.Message, e.StackTrace);
                return 1;
            }

            Console.WriteLine("\nConversion successful.");
            return 0;
        }
    }

    public class ConversionEventHandler : ITypeLibImporterNotifySink
    {
        public void ReportEvent(ImporterEventKind eventKind, int eventCode, string eventMsg)
        {
            Console.WriteLine("{0}", eventMsg);
        }

        public Assembly ResolveRef(object typeLib)
        {
            return null;
        }
    }
}

~ by omeg on October 21, 2013.

C#, code

5 Responses to “Creating COM wrappers programatically”

  1. I’m desperately looking for a way how to change the save path of the created assembly. Documentation refers to “AppDomain.DefineDynamicAssembly Method (AssemblyName, AssemblyBuilderAccess, String)” but this seems to create another AssemblyBuilder. Any ideas for that?

    Thank you
    Andreas

  2. > Well, in the code above you can pass a path to output assembly as the second argument (used in lines 40-41).

    Unfortunately, it does not take a path (i.e. directory information), just a filename without a prepended directory. If you try to pass a directory, you get an exception:
    “The filename must not include a path specification.
    Parameter name: assemblyFileName”. Any other idea to change the path?

    Thank you
    Andreas

    • Ah, I see. I think I “solved” this problem by temporarily changing working directory to one you want the output DLL in.

  3. > Ah, I see. I think I “solved” this problem by temporarily changing working directory to one you want the output DLL in.

    This works!
    Thanks a lot for this valuable hint!
    Happy Andreas 😉

Leave a Reply