Technical Notes

Previous | Home

Reading The Type-Library


Type-Libraries? I want my Mamma!

Any object model, to be taken seriously, must support something called dynamic type discovery, i.e., it must provide mechanisms that enable the inspection and interpretation of types by programs dynamically at runtime. With COM this service is provided through the use of Type-Libraries.

Type-Libraries are 'simple' binary files that contain in them almost everything that you would want to (and sometimes wouldn't want to) or even need to know about a given type. Ideally, the client program (i.e., the application that will make use of the services from the component), if it has access to the component's type-library, has in effect all the information that it needs to create and use the component. The type-library is the friendly tour guide who helps you find your way around in the component at runtime.

OK, so what do we get?

If you think about it, to be able to successfully use a component, you would need to know the following possible things about it.

In fact a component is defined by these pieces of information. The type-library is the place where you can put all this crud to provide a complete description of the component. In this article we will attempt to research and find out exactly how we are to go about extracting the information that we want from the type-library.

I have written a simple type-library browser that you may download at the sources section of this site. You could perhaps download and play around with it to get a feel of what we are attempting to do here. Even though the source for the program is available for download, I would not recommend that you look at it yet. You see, I would like you to retain sanity for a while longer (by when, hopefully, the fog would have begun to dissipate)!

So lets get rolling...

Let us first try to gain a proper understanding of how the type-library is organised. The type-library, at the top-level, has the following bits of information (it has some more, but lets pretend its not there for now),

Figure 1 - The Type-Library

The Type-Library

Each type-info object, in turn would provide complete information about that type. So all we need to do is first query the library regarding the number of types stored in it and then ask for the type-info object for each of those types. The functionality of the type-library is defined by an interface called ITypeLib. This interface has a method called GetTypeInfoCount that gets you the count of types stored in the library. For each of those types, you would call GetTypeInfo to be handed a pointer to an object implementing an interface called ITypeInfo.

But I am getting ahead of myself here. Before we can do all that, obviously, we need to have loaded the library in the first place. Let us see how we can achieve that next.

Loading the library

There are two ways you can go about doing this. Typically, a type-library would simply be a file on the disk with a .tlb extension. It could also have been made a part of a DLL or an EXE as a resource (with a special resource identifier). The type-library may or may not be registered, meaning, information about its location, version information etc., may or may not have been recorded in the Windows registry. If it is recorded, then it is recorded under the HKEY_CLASSES_ROOT\\TypeLib key.

If you have explicit knowledge about the physical location of the type-library, then you can use the LoadTypeLib COM library function to load the library. LoadTypeLib returns a pointer to an object implementing the ITypeLib interface through an output parameter. You can load the library like so,

ITypeLib *pTLib;
HRESULT hr;

hr = LoadTypeLib( L"C:\\File.tlb", &pTLib );

If it is successfully loaded, then pTLib will be a valid pointer to a COM object implementing ITypeLib.

Alternately, if you have the following information,

then you can use the LoadRegTypeLib API to do the loading. Like this,

HRESULT hr;
ITypeLib *pTLib;
WORD wVerMajor, wVerMinor;
LIBID libid;

//get hold of the libid and the version somehow

hr = LoadRegTypeLib( libid, wVerMajor, wVerMinor,
            LOCALE_SYSTEM_DEFAULT, &pTLib );

This function simply pokes the registry to find the physical location of the library before loading it. You might ask, exactly how we are supposed to be in posession of this information. Well, if you just happen to have it with you (ESP perhaps) then fine! If you don't, then maybe you can manually look in the registry! Isn't that what LoadRegTypeLib does? Well, yes. But we'll talk more about this later. Right now, lets move on and figure out what we can do with this pointer to the object that implements ITypeLib.

The ITypeLib Interface - Get the basics right

Like I said earlier, the type-library, at the top level gives you the following 4 pieces of information.

Let us now see, how we can get this data starting with the library name.

What is thy sweet name Oh! Type-Library?

Retrieving the name of the library is not an immediately obvious operation. The ITypeLib interface has a method called GetDocumentation to which you can supply an index to retrieve the human readable name of the type. If you supply the value -1 for the index, it gives you the name of the library itself! Weird!

ITypeLib *pTLib;
HRESULT hr;
BSTR bstrLibName;

//load the library somehow

hr = pTLib->GetDocumentation( -1, &bstrLibName, NULL, NULL, NULL );

As you may have noticed, the name of the library is returned as a BSTR. Once you are done using the string you must free it by calling SysFreeString.

Type-Library Attributes

By attributes, we are really talking about the following aspects of the type-library.

This information is provided to you through a structure called TLIBATTR that has been declared like so in oaidl.h,

typedef struct  tagTLIBATTR
    {
    GUID guid;
    LCID lcid;
    SYSKIND syskind;
    WORD wMajorVerNum;
    WORD wMinorVerNum;
    WORD wLibFlags;
    }	TLIBATTR;

SYSKIND is an enumeration that has been declared in the same file like this,

typedef /* [v1_enum] */ 
enum tagSYSKIND
    {	SYS_WIN16	= 0,
	SYS_WIN32	= SYS_WIN16 + 1,
	SYS_MAC	= SYS_WIN32 + 1
    }	SYSKIND;

As you can see, it tells you something about the system on which the library was built. Now, why you would want to know this is anybody's guess (in other words, I don't know ;).

The wLibFlags member can take values from the LIBFLAGS enumeration.

So, how do we get this information? This is how,

ITypeLib *pTLib;
TLIBATTR *pTLibAttr;
HRESULT hr;

//load the type-library somehow

hr = pTLib->GetLibAttr( &pTLibAttr );

Note that, ITypeLib::GetLibAttr takes a pointer to a pointer to a TLIBATTR object as its argument. In other words, the type-library object allocates the memory for holding a TLIBATTR. Once you are done using the structure, you will release the memory held by it by calling ITypeLib::ReleaseTLibAttr. Like this,


pTLib->ReleaseTLibAttr( pTLibAttr );

The Type Count

This probably is the single most important piece of information that you can pull out of the type-library without which you wouldn't be able to proceed further. Retrieving it however is a delightfully trivial affair :).

ITypeLib *pTLib;
HRESULT hr;
UINT uiCount;

//load the type-library somehow

uiCount = pTLib->GetTypeInfoCount();

That's it! For once, we have a function that does what it sounds like it does. It gives you the count of the type-info objects in the library. Lets now see how we can use this to get hold of the type-info objects which of course is our ultimate aim in life!

The ITypeInfo Interface

From here on, the level of complexity (or tediousness) begins to ascend. There aren't a lot of fans when it comes to making sense of the information stored in the type-library. If you ever wondered why, then this interface is the reason! We'll try to lay it bare however, one tiny step at a time, so that towards the end, you may actually find yourself appreciating it!

I had earlier indicated the kind of information that you can pull out of the type-library. Here is a more complete listing.

Each of these types (except typedefs), are represented in the type-library by separate type-info objects. Let us now see how we can go about enumerating all these different type-info objects.

Enumerating Type-Info Objects

You can run through (or enumerate) all the objects in the library using the count that is returned by ITypeLib::GetTypeInfoCount. If all you want to do is find out what the type of a given object in the library is, then you can invoke ITypeLib::GetTypeInfoType passing in the index of the object whose type you want to know. This method returns the type as an instance of the TYPEKIND enumeration which has been declared in oaidl.h like this,


typedef /* [v1_enum] */ 
enum tagTYPEKIND
    {	TKIND_ENUM	= 0,
	TKIND_RECORD	= TKIND_ENUM + 1,
	TKIND_MODULE	= TKIND_RECORD + 1,
	TKIND_INTERFACE	= TKIND_MODULE + 1,
	TKIND_DISPATCH	= TKIND_INTERFACE + 1,
	TKIND_COCLASS	= TKIND_DISPATCH + 1,
	TKIND_ALIAS	= TKIND_COCLASS + 1,
	TKIND_UNION	= TKIND_ALIAS + 1,
	TKIND_MAX	= TKIND_UNION + 1
    }	TYPEKIND;

Let us write a little function that will take a TYPEKIND object as its argument and return a string representation (an STL string, to be precise) of it so that we can use it later on whenever we need it.


string StringifyTypeKind( TYPEKIND tkind )
{
	switch( tkind )
	{
		case TKIND_ENUM: return "Enum";
		case TKIND_RECORD: return "Structure";
		case TKIND_MODULE: return "Module";
		case TKIND_INTERFACE: return "Interface";
		case TKIND_DISPATCH: return "Dispinterface";
		case TKIND_COCLASS: return "Coclass";
		case TKIND_ALIAS: return "Typedef";
		case TKIND_UNION: return "Union";
		default: return "BAD BAD BAD";
	}
}

As you can see, this is a trivial little function that simply returns the type in a human readable form. What follows is a simple program that opens a type-library and prints out the type of all the objects in the library. Error checks have been removed for brevity.


#include <windows.h>
#include <iostream>
#include <string>

using namespace std;

string StringifyTypeKind( TYPEKIND tkind );

int main()
{
	ITypeLib *pTLib;
	TYPEKIND typeKind;
	UINT uiCount;
	HRESULT hr;

	hr = CoInitialize( NULL );

	hr = LoadTypeLib( L"C:\\File.tlb", &pTLib );

	uiCount = pTLib->GetTypeInfoCount();

	for( UINT i = 0 ; i < uiCount ; ++i )
	{
		cout<<i + 1<<") ";

		hr = pTLib->GetTypeInfoType( i, &typeKind );
		cout<<StringifyTypeKind( typeKind )<<endl;
	}
	
	pTLib->Release();
	CoUninitialize();
	return 0;
}

Simple eh? Let us go one step further and wrench out the name of each of those types. You might remember that we invoked ITypeLib::GetDocumentation in order to retrieve the name of the library itself. The same method can be used to retrieve the name of all the other types stored in the library simply by passing in the index of the type in the place of the -1. The loop in the code given above has been modified and reproduced below with the modifications highlighted in bold.

	BSTR bstrName;

	for( UINT i = 0 ; i < uiCount ; ++i )
	{
		cout<<i + 1<<") ";

		hr = pTLib->GetDocumentation( i, &bstrName,
				NULL, NULL, NULL );
		wcout<<bstrName<<L" - ";
		SysFreeString( bstrName );

		hr = pTLib->GetTypeInfoType( i, &typeKind );
		cout<<StringifyTypeKind( typeKind )<<endl;
	}

Since ITypeLib::GetDocumentation returns the name of the type as a BSTR we need to treat it as such. It is the caller's reponsibility to call SysFreeString on the BSTR returned by ITypeLib::GetDocumentation.

Finally, getting a pointer to the actual type-info itself involves a rather simple call to ITypeLib's GetTypeInfo method. You hand in the index of the object whose type-info you want and it dutifully returns the pointer to you. Like this.


ITypeLib *pTLib;
ITypeInfo *pTInfo;
HRESULT hr;
UINT index = 0;

//get hold of a type-library somehow
//initialize the index to something you want

hr = pTLib->GetTypeInfo( index, &pTInfo );
if( SUCCEEDED( hr ) )
{
	//voila! you've got a handle on the beast!
	//do your stuff and get rid of it

	pTInfo->Release();
}

pTLib->Release();

That's it! Now that we've got a pointer to the type-info object we can go right ahead and peer into the deep dark crevices of the type-library and find the truth that we all know is out there!

The Type-Info - In Detail

Now would be a good time for you to take a deep breath, summon all your extra sensory capabilities, do the magic dance etc., because from here on the ride begins to get a bit bumpy (yes, thats right, even more bumpy!).

Each type-info object can be thought of as containing the following three components

Figure 2 - The Type-Info Object

The Type-Info Object

The type's attributes are handed to us through a structure called TYPEATTR. There is a whole lot of information packed up in this structure and this is the first thing that you would want to pull out once you get the pointer to the type-info object. Among others, it tells you the exact type of the object that we are dealing with, i.e., TYPEATTR has a member variable called typeKind, which is an instance of the TYPEKIND enumeration (that we got to know and love so much earlier), that tells you whether the type-info object describes an enumeration or a structure or a union or an interface and so on. This is the same value as returned by the GetTypeInfoType method of the ITypeLib interface.

You retrieve a pointer to the TYPEATTR structure from the type-info object by invoking ITypeInfo::GetTypeAttr. Like this.


ITypeInfo *pTInfo;
TYPEATTR *pTAttr;
HRESULT hr;

//get hold of a pointer to a type-info object somehow

hr = pTInfo->GetTypeAttr( &pTAttr );

//use the structure

hr = pTInfo->ReleaseTypeAttr( pTAttr );

As you can see, GetTypeAttr yields a pointer to a TYPEATTR object. What this means is that, the memory for storing the TYPEATTR object has been allocated by the object itself. Consequently once we are done using it, the job of releasing that memory is delegated to the object by calling ReleaseTypeAttr.

If the type-info describes

the rest of the type-info object then contains as many VARDESC objects as indicated by the cVars member of the TYPEATTR structure.

If on the other hand, it describes

the rest of the type-info object then contains as many FUNCDESC objects as indicated by the cFuncs member of the TYPEATTR structure.

Previous | Home

1