The Hard Way: CreateTypeLib

In Chapter 2, we learned how the provider of a component gives access to the objects and interfaces of that component through one of four mechanisms, such as a specific API function that creates one type of component and gives you back one type of interface. This is exactly the means through which OLE exposes its type library creation service.

The API function of interest here is CreateTypeLib, which has the arguments you see at the top of the following page.

Argument

Description

syskind

A value from the SYSKIND enumeration to describe the target operating system, such as SYS_WIN16, SYS_WIN32, or SYS_MAC.

szFile

OLECHAR *, which provides the name of the disk file in which OLE stores the type library data structures. Type library files conventionally use TLB for the extension.

ppCTLib

ICreateTypeLib *, in which the function returns the interface pointer representing the new type library.


CreateTypeLib doesn't really do much more than create a disk file with one top-level data structure, which happens to be empty. At this point you have, in terms of the children's poem, a vacant lot. You have a place to put the house—the type information—but there isn't anything there but rocks, weeds, and crummy fill dirt.

Building something in that vacant lot is the purpose of the ICreateTypeLib pointer you get back from calling CreateTypeLib. The member functions of this interface, described in Table 3-3, deal mostly with setting the library's attributes that we saw earlier—that is, building the basic shell of a house. As with any pointer you obtain for any object, you must call Release when you're finished with the type library.

Member Function

Description

SetName, SetGuid,
SetVersion, SetLcid,
SetLibFlags

Assigns the name, GUID, version numbers, locale, and flags to a type library.

SetDocString,
SetHelpFileName,
SetHelpContext

Assigns help-related attributes to a type library.

SaveAllChanges

Commits all changes made to the type library (to any element within it) to the disk file. If this is not called before Release, the changes are lost.

CreateTypeInfo

Creates an element in the type library and returns an ICreateTypeInfo pointer through which that element is constructed.


Table 3-3.

Member functions of the ICreateTypeLib interface.

The two functions in the interface that don't have to do with attributes—CreateTypeInfo and SaveAllChanges—are the most critical. Everything you do to the library before calling SaveAllChanges is the same as drawing an architectural blueprint and planning everything else that should go in a house, but the house exists only on paper. Calling SaveAllChanges is similar to taking those plans and giving them to a builder who will actually create something tangible. In the case of a type library, it means writing everything to disk.

ICreateTypeLib::CreateTypeInfo is what you use to create a coclass, interface, dispinterface, module, or typedef (struct, union, enum) element in the library. The type of element is identified with a value from the enumeration TYPEKIND, which can be TKIND_ALIAS (a simple typedef to name a new type), TKIND_COCLASS, TKIND_DISPATCH (dispinterface), TKIND_ENUM (typedef enum), TKIND_INTERFACE, TKIND_MODULE, TKIND_RECORD (typedef struct), and TKIND_UNION (typedef union). Along with the type identifier, this function also takes a name attribute (text string) for the new element.4 CreateTypeInfo then returns an ICreateTypeInfo pointer.

The member functions of ICreateTypeInfo described in Table 3-4 allow you to build the rooms, the walls, and the mouse holes in your house.

Member Function

Description

SetGuid, SetVersion

Assigns a GUID or version to the element.

SetDocString,
SetHelpContext

Assigns help-related attributes to the element.

SetTypeDescAlias

Creates an alias for a type (a simple typedef).

SetAlignment

Specifies byte alignment for a data structure.

DefineFuncAsDllEntry

Defines an exported DLL function by name or by ordinal; applicable only to module elements.

SetTypeFlags

Sets additional flags for coclass elements, such as whether or not a coclass element is licensed, instantiable, or restricted from browsing.

AddImplType,
SetImplTypeFlags

Creates information about the interfaces in a coclass element. AddImplType adds the description of the interface; SetImplTypeFlags assigns default, source, and restricted attributes to those interfaces. Both require information returned from AddRefTypeInfo.

AddRefTypeInfo

Creates an HREFTYPE that refers to a coclass and is necessary for calling AddImplType to add interfaces and dispinterfaces to a coclass description. This function requires an ITypeInfo pointer, which is obtained by calling ICreateTypeInfo::QueryInterface.

AddFuncDesc,
SetFuncAndParamNames,
SetFuncDocString,
SetFuncHelpContext

Creates information about a member function in interface or dispinterface elements only. AddFuncDesc takes a pointer to a FUNCDESC structure, which contains the identifier, calling convention, return type, argument types, and permitted return codes. SetFuncParamsAndNames assigns names to the function and its arguments. SetFuncDocString and SetFuncHelpContext save help attributes for a function. With all of these functions you identify the member function in question by a common index.

AddVarDesc, SetVarName,
SetVarDocString,
SetVarHelpContext

Creates information about a global variable description to a module or a property to a dispinterface. AddVarDesc adds a variable through the VARDESC structure. The other functions set the variable's name and help information.

SetTypeIdlDesc, SetMops

Defines cross-process marshaling information for an interface or dispinterface.

LayOut

Assigns vtable positions for an interface element, dispatch identifiers for methods and properties in a dispinterface element, and offsets for exported global variables from a module element. This function should only be called for an element after all other information is stored and just before calling ICreateTypeLib::SaveAllChanges.

SetSchema

Reserved.


Table 3-4.

Member functions of the ICreateTypeInfo interface.

The overall process of creating a type library is illustrated in Figure 3-1. The illustration shows the steps of creating the library, then creating the elements within it (you can create as many elements of each type as desired), and then saving the changes on disk through ICreateTypeLib::SaveAllChanges.

Figure 3-1.

The type library creation process.

You can see that through ICreateTypeLib and ICreateTypeInfo you can create whatever type information structures you want. These interfaces and OLE's direct type library creation service are useful mostly for development tools, but some applications or other components may find these functions useful for creating some type information on the fly. The whole process, however, is somewhat involved, and this book will not get into any other details. For more information about these functions, see the OLE Programmer's Reference.

Note: You might encounter references to some data structures called INTERFACEDATA, PARAMDATA, and METHODDATA, which are an archaic way to create type information for a single dispinterface that existed before OLE's type library services were complete. Ignore these structures: they are obsolete.

4 This is only a convenience for coclass interface and dispinterface elements but necessary for typedefs and modules. The former three are programmatically identified with a GUID which is optional for a typedef but not allowed on a module.