views:

2083

answers:

3

After asking about what Visual Studio does to register a COM Library, it became clear that VS did two things for COM registration:

  1. Registered the COM Library
  2. Creates and registers a Type Library

Visual Studio seems to do this registration using regasm.exe. For the first part (the direct COM registration) using tallow or heat (WiX 2.0 or WiX 3.0) seems to get all of the basic COM registration information correct.

However, what tallow/heat doesn't seem to do is set up a type library installation. It would be possible to create a custom action to do this with a WiX installer and regasm.exe, but invoking custom actions are not best practices when it comes to Microsoft installer based installers.

Upon further research, it looks like an msi has the ability to generate the type library upon installation. In fact, WiX seems to have direct support for it! In a file element, you can add a Typelib element. In fact, an article over here on wix has an example of filling out the TypeLib element with Interface elements.

It seems there's at least two required attributes to an Interface element:

  1. Id
  2. Name

Larry Osterman speaks about the other parts of the interface that need to be registered for a TypeLib in general, and this Interface entry seems to take care of the individual parts. Larry says we need to specify the ProxyStubClassId32 as "{00020424-0000-0000-C000-000000000046}", so we can easily add that.

Where to go from there and what to fill out for the various Interface elements has me stumped. I've gone ahead and added the TypeLib element to my wix file, and it successfully compiles. I'm a bit clueless as to how to set up the Interface elements though. What do we need to do to properly fill out the TypeLib element, and what apps or tools can I use to get it?

The answer below by wcoenen looks promising...I'm going to give it a shot.

Update: Posted my final solution below as an answer.

+3  A: 

The following trick can help with harvesting registry changes and turning them into a wxs file, including the typelib element you're after. (I did not mention this in my answer to your original question because wix v3 appears to have dropped support for the conversion of .reg files to .wxs files.)

First, bring you registry back in a state where the type library was not registered:

c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb /u mylib.dll

Export this clean state of the registry to hklm-before.reg:

c:\WINDOWS\system32\reg.exe export HKLM hklm-before.reg

Register the type library again:

c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb mylib.dll

Export the new state of the registry to hklm-after.reg:

c:\WINDOWS\system32\reg.exe export HKLM hklm-after.reg

Now we have two text files, hklm-before.reg and hklm-after.reg. Create a diff.reg file which only holds the relevant differences between these. You can find the differences easily with a diffing tool. I like to use the diff tool included in TortoiseSVN since I already use that every day. (WinDiff doesn't seem to work well in this case because of text-encoding issues.)

Convert diff.reg to a wxs file with tallow. This will create the wix typelib element you're after:

tallow.exe -nologo -reg diff.reg > mytypelib.wxs

This file will still require some manual editing.

This procedure only works in wix2, since heat.exe (the tallow.exe replacement in wix3) no longer support .reg file processing. However, you can use wixcop to upgrade the wix2 file to wix3:

wixcop.exe -f mytypelib.wxs
Wim Coenen
Ouch. No wonder they say that WiX authoring right now is a manual process, euch!
Robert P
That's because WIX is mostly just an XML format that maps to a windows installer database (msi/msm/mst/pcp). For example, there is a "typelib" element because there is also a "typelib" table. The harvesting tool heat.exe is useful added-value but it is in need of experts to complete it.
Wim Coenen
+1  A: 

It looks like to register a Type library, the best way would be to generate your own IDL or ODL file, which will contain your GUIDs. The Typelibs generated directly from the Assembly are [i]dependent[/i] on the assembly version numbers : the GUIDs are generated based on that information, even if the interface hasn't changed. Visual Studio uses regasm to register and generate the typelib. Underneath that, it uses RegisterTypeLib, a win32 call. Using the typelib element seems to do something similar. No good.

However! Creating the type library by hand is painful. It is possible to get those GUIDs another way: digging them out of the typelib and creating the elements yourself.

Larry Osterman has the information that's needed: there's certain registry keys that need to be set. You can do those with the Registry table (and in Wix3, that means RegistryValue elements.) The trick here is getting the GUIDs: any old GUID will not work. Normally, getting the GUIDs is simply a matter of looking in the IDL for your library (you wrote your own IDL, right? :) ).

If you didn't write an IDL or ODL file to compile into a typelib, they still exist, in the file. Microsoft provides several handy tools: LoadTypeLibEx and the ITypeLib interface. With these interfaces, you can browse the type library and get all sorts of information. How do we browse the library?

I simply took a look at how Regasm did it! A quick dissassemble later, and we find that regasm is written in C# too. Glory day. I started up a project, and with a few using statements and a PInvoke later, we have:

using System.Runtime.InteropServices;          // for struct marshaling 
using System.Runtime.InteropServices.ComTypes; // for the ITypeLib + related types

// TYPELIBATTR lives in two places: Interop and ComTypes, but the one
// in Interop is deprecated.
using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; 

/// <summary>
/// The registry kind enumeration for LoadTypeLibEx.  This must be made
/// here, since it doesn't exist anywhere else in C# afaik.  This is found
/// here: http://msdn.microsoft.com/en-us/library/ms221159.aspx
/// </summary>
enum REGKIND
{
    REGKIND_DEFAULT,
    REGKIND_REGISTER,
    REGKIND_NONE
}

// and this is how we get the library.
[DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
  private static extern void LoadTypeLibEx(string strTypeLibName, REGKIND regKind, out ITypeLib TypeLib);

Whew! Once we have this out, we have to navigate the structure. This is interacting with unmanaged resources, so get ready to be Marshaling stuff around.

ITypeLib lib = null;
LoadTypeLibEx(Value, REGKIND.REGKIND_NONE, out lib);
IntPtr libInfoPtr = IntPtr.Zero;
lib.GetLibAttr(out libInfoPtr);
TYPELIBATTR libInfo = 
    (TYPELIBATTR) Marshal.PtrToStructure(libInfoPtr, typeof(TYPELIBATTR));
int typeCount = lib.GetTypeInfoCount();
for (int i = 0; i < typeCount; ++i)
{
    ITypeInfo info;
    lib.GetTypeInfo(i, out info);
    IntPtr typeDescrPtr = IntPtr.Zero;
    info.GetTypeAttr(out typeDescrPtr);
    TYPELIBATTR type =
        (TYPELIBATTR)Marshal.PtrToStructure(typeDescrPtr, typeof(TYPELIBATTR));
    // get GUID, other info from the specific type
}

lib.ReleaseTLibAttr(libInfoPtr);
libInfoPtr = IntPtr.Zero;

Whew. So, you have to write some code to extract the information. Once you do, you have to fill that information into Registy Entries, as specified By Larry Osterman.

Of course, you could avoid that step by simply writing your own IDL file to begin with. The choice in pain: it's up to you!

Robert P
I reread this a couple of times, but I am wildly confused. No idea what you achieved here.
Wim Coenen
The code isn't the solution. Visual Studio generates all the information you're supposed to do yourself in an IDL file as part of regasm. This lets you figure out what regasm did on your behalf. What you're supposed to do is make an IDL file.
Robert P
If you were trying to figure out what regasm puts in the tlb, then why not just open the tlb file generated by regasm with oleview.exe?
Wim Coenen
Yeah, you could do it the easy way...I wanted a tool that could do it, and you don't really get much closer to the metal than this. :)
Robert P
I respect that you did your homework and found out what really happened in regasm. Even if I don't use the code solution now, knowing how to wire my own software up to do this is useful. Thanks for the great info.
Jeremy E
+4  A: 

Here's the lazy man's way of solving this problem: Use heat from WiX 3.0.

If you have a type library generated automatically and installed via regasm, heat can take the .tlb as an argument in

heat file c:\my\path\to\my.tlb -out tlb.wxs

It will generate all the typelib and interface elements you need to register. This won't solve the problem of needing to know them ahead of time, and it won't solve the problem of GUIDs changing when the version of the assembly changes (even if the interface doesn't - which is the only time you're supposed to change it) but it will get you partway there.

Robert P
+1 I didn't know you could harvest tlb files with heat.exe
Wim Coenen
Though I don't think there are any variable GUIDs if you declare them in your .NET code with a GUID [assembly: Guid("...")] attribute for the typelib and a [Guid("...")] attribute for the COM interfaces and classes
Wim Coenen
For the COM guid's I think you're correct. Unfortunately the Type Library has different guids. :| I wonder if there's an attribute to set those guids too...
Robert P
wix is awesome!
alfred barthand