Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

COM registration if you need a typelib

COM registration if you need a typelib

  • Comments 8
The problem with the previous examples I posted on minimal COM object registration is that they don't always work.  As I mentioned, if you follow the rules specified, while your COM object will work just fine from Win32 applications, you'll have problems if you attempt to access it from a managed environment (either an app running under the CLR or another management environment such as the VB6 runtime or the scripting host).

For those environments, you need to have a typelib.  Since typelib's were designed primarily for interoperating with visual basic, they don't provide full access to the functionality that's available via MIDL (for instance, unnamed unions get turned into named unions, the MIDL boolean type isn't supported, etc), but if you gotta interoperate, you gotta interoperate.

So you've followed the examples listed here and you've registered your COM object, now how do you hook it up to the system?

First, you could call the RegisterTypeLib function, which will perform the registration, but that would be cheating :)  More importantly, there are lots of situations where it's inappropriate to use RegisterTypeLib - for instance, if you're building an app that needs to be installed, you need to enumerate all the registry manipulations done by your application so they can be undone.

So if you want to register a typelib, it's a smidge more complicated than registering a COM component or interface.

To register a typelib, you need (from here):

Key: HKEY_CLASSES_ROOT\Typelib\<LibID>\
Key: HKEY_CLASSES_ROOT\Typelib\<LibID>\<major version>.<minor version>\   
    Default Value: <friendly name for the library> Again, not really required, but nice for oleview
Key: HKEY_CLASSES_ROOT\Typelib\<LibID>\<major version>.<minor version>\HELPDIR   
    Default Value: <Directory that contains the help file for the type library>
Key: HKEY_CLASSES_ROOT\Typelib\<LibID>\<major version>.<minor version>\FLAGS   
    Default Value: Flags for the ICreateTypeLib::SetLibFlags call (typically 0)
Key: HKEY_CLASSES_ROOT\Typelib\<LibID>\<major version>.<minor version>\<LCID for library>
Key: HKEY_CLASSES_ROOT\Typelib\<LibID>\<major version>.<minor version>\<LCID>\<Platform>
    Default Value: <File name that contains the typelib>

Notes:

If your typelib isn't locale-specific, you can specify 0 for the LCID.  Looking at my system, that's typically what most apps do.

<Platform> can be win32, win64 or win16 depending on the platform of the binary.
 

But this isn't quite enough to get the typelib hooked up  - the system still doesn't know how to get access to the type library.  To do that, you need to enhance your CLSID registration to let COM know that there's a typelib available.  With the typelib, a managed environment can synthesize all the interfaces associated with a class.  To do that, you enhance the class registration:

Key: HKEY_CLASSES_ROOT\CLSID\<CLSID>\TypeLib = <LibID>

But we're still not quite done.  For each of the interfaces in the typelib, you can let the system do the marshaling of the interface for you without having to specify a proxy library.  To do this, you can let the standard proxy marshaler do the work.  The universal marshaler has a clsid of {00020424-0000-0000-C000-000000000046}, so instead of using the interface registration mentioned in the last article, you can replace it with:

Key: HKEY_CLASSES_ROOT\Interface\<IID>\
    Default Value: <friendly name for the interface> Again, not really required, but nice for oleview
Key: HKEY_CLASSES_ROOT\Interface\<IID>\ProxyStubClsid32\
    Default Value: {00020424-0000-0000-C000-000000000046}
Key: HKEY_CLASSES_ROOT\Interface\<IID>\TypeLib\
    Default Value: <LibID>

Now instead of using the proxy code in a proxy DLL, the system will do the marshaling for you.

Next: Ok, but what if I don't want to deal with all those ugly GUID thingies?

  • If you're calling from .NET, then I didn't think you needed to actually register the typelib. Just use tlbimp.exe in a type library file and it generates the RCW - it doesn't use the type library at runtime.

    Even from the Visual Studio IDE, if you select the COM tab in "Add Reference", it lists all the registered type libraries, but you can also Browse for one that's not actually registered...
  • "More importantly, there are lots of situations where it's inappropriate to use RegisterTypeLib - for instance, if you're building an app that needs to be installed, you need to enumerate all the registry manipulations done by your application so they can be undone."

    What's wrong with UnRegisterTypeLib? The name seems to indicate it should do the job--or is there some catch? Raymond rails against developers who grunge in the registry when they should be using documented interfaces, and I don't want him to holler at me. :)
  • Dave, calling RegisterTypeLib or UnRegisterTypeLib requires that you have a custom action in your MSI file.

    It's my understanding that custom actions are a no-no because they can involve arbitrary changes to the registry that can't be automatically undone. That means that the installer framework can't correctly determine if your component has removed all of its code.

    If you explicitly add all your registry keys, that won't happen.

    Dean, you may be right, I'm not sure.
  • Is the LCID specified in hex or decimal? (ie: 1033 or 409 for english)?
  • Manipulating registry keys in an install via custom actions means that the installer doesn't know that you've changed a registry key.

    If you have a shared component in two installs, install both on a machine, then uninstall one of them, the second product will be broken (because the first product removed the registry keys needed by the second product on uninstall, instead of depending on component reference counts).

    There are actually several variations of the above case, though I can't remember them offhand at the moment.

    It also makes proper install, repair, remove, and rollback logic harder than it needs to be. Additionally, it lacks verifiability and repeatability (custom actions in MSI can just randomly fail for wierd non-obvious reasons).

    Odds are, most people can get away with it. But you'll get bad karma by doing it. :)
  • Thank you mirobin.

    Btw, I don't know if the LCID is decimal or hex. I suspect hex, but I didn't find any examples when I went looking yesterday
  • The MSI way to register COM server is the Class table, and the associated AppId table. I don't know if they support everything the COM registration requires, though.
  • Yeah, it's hex. So it would be:

    HKEY_CLASSES_ROOT\Typelib\<LibID>\<major version>.<minor version>\409

    or whatever...
Page 1 of 1 (8 items)