After asking about what Visual Studio does to register a COM Library, it became clear that VS did two things for COM registration:
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;
///
/// 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
///
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 Marshal
ing 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!