Type Library Deployment

The end result of whatever means you use to create a type library is a binary file containing nothing more than the type data structures.6 When you ship a component that depends on this type information you must also ship this type library. You can either attach a type library to the component's server module (EXE or DLL) as a resource, store it in a stream named "\006typelib" (located in the root storage object) in a compound file, or ship the stand-alone TLB file directly.

Regardless of how you decide to ship a type library, you have to associate that type library with every component CLSID that uses it so that clients given the CLSID can retrieve your type information. This, of course, involves the registry once again. What we know about the registry so far is that any object that needs to have registry entries should have a ProgID, a Version-IndependentProgID, and a CLSID entry, as described in Chapter 2. There we mentioned that an object that didn't need a CLSID doesn't need any registry information. If you want to associate type information with an object, however, you have to assign the object a CLSID, even if it's not part of a custom component that could be instantiated with that CLSID—you still need it for association purposes.

In Chapter 2, we listed the following entries under the CLSID key for some hypothetical object:


\
CLSID
{42754580-16b7-11ce-80eb-00aa003d7352} = Acme Component 3.0
ProgID = Acme.Component.3
VersionIndependentProgID = Acme.Component

To associate a type library with this CLSID, you need to add the following subkey (on the same level as ProgID):


            TypeLib = {<LIBID>}

Here {<LIBID>} is the uuid attribute for the library itself. For example, if I were to assign the GUID a4f8a400-16b7-11ce-80eb-00aa003d7352 to the library, this entry would appear as follows:


            TypeLib = {a4f8a400-16b7-11ce-80eb-00aa003d7352}

This GUID now refers to a set of entries that you must also store under the TypeLib section of the registry, which is on the same level as the CLSID section. Here is the format of the entries:


\
TypeLib
{<LIBID>} = <name of type library>
DIR = <path of type library files; no filename!>
HELPDIR = <path of help files; no filename!>
<version>
<LangID>
[Win16 | Win32] = <filename>
<LangID>
§
<version>
§
§

Basically you create a key with an LIBID (all spelled out in hex with the hyphens and the braces) with the value of some readable name for the library. Under this key, you store directory information and keys for each version of the type library, demonstrating that different versions of a type library can share the same LIBID. Under each version entry, you create a subkey equal to the language ID (part of an LCID) identifying the national language of the library in question, and then you create another subkey identifying the "bitness" (16 or 32) of the library. The value of this last key is either the name of a raw TLB file, an EXE, or a DLL (in which the library is a resource), or the name of a compound file that contains the library in a stream. Let's now look at each entry in more detail.

The DIR and HELPDIR keys provide the paths on which you installed your type libraries and help files. This means that any filenames you build into code, such as those of TLB files or HLP files named in helpfile attributes, need not include the path because this path is known only at installation time and not at build time. The DIR key identifies the default location of any TLB, EXE, DLL, or compound file referenced farther down in the registry entries. If these files are located in another place, you should store the complete pathnames in those later entries. The HELPDIR key works much the same way, identifying where any HLP file was installed. When anyone asks the type library to return its "documentation," that is, the name of the helpfile attribute for any element and the helpcontext, OLE automatically prepends the HELPDIR pathname to the filename in the library itself.

Each version number entry uses the form major.minor, as in "1.0" or "0.0" and so forth. This allows multiple versions to coexist using the same LIBID, but each registered library must have a major version number that matches the version key under which it is registered. Under each version, you create keys containing the hexadecimal representation of the LANGID of the information. The LANGID is the lower 16 bits of an LCIDthat is, the primary-language ID and the sub-language ID. The language identified by this key must match the lcid attribute given to the library itself. The name does not include the "0x" prefix (as with ODL) and is stripped of any leading zeros. If I were writing a specifically American English type library, my language ID would be 0x0409, which is what I'd use in the lcid attribute. In the registry, I would use only the string "409" as the key name. If I were writing a type library in basic English (not specific to a dialect), the language ID would be 0x0009 and the registry entry simply "9" with no leading zeros. If I were writing a language-neutral library, lcid would be 0x0000 to match "0" in the registry.

Finally, underneath the language ID, the Win16 subkey points to a 16-bit type library location and the Win32 subkey points to the 32-bit type library location. The location path is for one of the names mentioned before, a TLB, an EXE, a DLL, or a compound file.

The biggest difficulty in creating registry entries is matching the language, and you want to use the most generic language ID you possibly can, both in the registry and in the lcid attribute. The OLE API functions that load the type library, which we'll see in a moment, take a language ID as an argument. These functions look first for an exact match in the registry, next for a primary language ID entry, and then for a zero ("neutral") entry, before failing completely. The more specifically you register a library, the fewer the cases in which some loading call will succeed. So again, be as generic as possible. It's a good idea to always register something under "0" so that loading will always work in some capacity.

We're now ready to look at some of the loading API functions and the interfaces that they return. Through these interfaces, both the object and any client can browse through the information in the type library.

6 The OLE Programmer's Reference incorrectly states that this file is a compound file. A type library is actually a standard flat binary file.