Classes and IDL

As noted early in this chapter, COM treats interfaces and classes as distinct entities. In light of this, COM classes (like COM interfaces) should be defined in IDL to provide a language-neutral description of the concrete data types a server may export. The IDL definition of a COM class contains the list of interfaces that instances of the class export barring catastrophic failure:

[uuid(753A8A7D-A7FF-11d0-8C30-0080C73925BA)]
coclass Gorilla {
  interface IApe;
  interface IWarrior;
}

IDL coclass definitions always appear in the context of a library definition. In IDL, library definitions are used to group a collection of data types (e.g., interfaces, coclasses, typedefs) into a logical unit or namespace. All data types that appear in the context of an IDL library definition will be tokenized into the resultant type library. Type libraries are used in lieu of IDL files by environments such as Visual Basic and Java.

An IDL file can have at most one library statement, and all data types defined or used inside the library definition will appear in the generated type library:

// apes.idl /////////////////////////////////

// bring in IDL definitions of ape interfaces
import "apeitfs.idl"; 
[
  uuid(753A8A80-A7FF-11d0-8C30-0080C73925BA), // LIBID
  version(1.0),  // version number of library
  lcid(9),      // locale ID of library (english)
  helpstring("Library of the Apes") // title of library
]
library ApeLib
{
  importlib("stdole32.tlb"); // bring in std defs.

  [uuid(753A8A7D-A7FF-11d0-8C30-0080C73925BA)]
  coclass Gorilla {
    [default] interface IApe;
    interface IWarrior;
  }

  [uuid(753A8A7E-A7FF-11d0-8C30-0080C73925BA)]
  coclass Chimpanzee {
    [default] interface IApe;
    interface IEgghead;
  }

  [uuid(753A8A7F-A7FF-11d0-8C30-0080C73925BA)]
  coclass Orangutan {
    [default] interface IApe;
    interface IKeeperOfTheFaith;
  }
}

The [default] attribute indicates which interface most closely represents the intrinsic type of the class. In languages that recognize this attribute,
[default] allows the programmer to declare object references using only the COM coclass name:

Dim ursus as Gorilla

Based on the IDL definition of Gorilla, this statement is equivalent to

Dim ursus as IApe

because IApe is the default interface for the class Gorilla. In either case, the programmer could call the methods EatBanana and SwingFromTree on the variable ursus. If the [default] attribute is not specified, it is implicitly added to the first interface in the coclass definition.

Given the preceding IDL library definition, the resultant header file apes.h would use the C preprocessor to include the file apesitfs.h. The file apesitfs.h would contain the abstract base class definitions of the four COM interfaces IApe, IWarrior, IKeeperOfTheFaith, and IEgghead. The file apes.h also would contain the declarations for each of the classes’ GUIDs:

extern "C" const CLSID CLSID_Gorilla;
extern "C" const CLSID CLSID_Chimpanzee;
extern "C" const CLSID CLSID_Orangutan;

The corresponding apes_i.c file would contain the definitions of these CLSIDs. The generated type library, apes.tlb, would contain the descriptions of each of the interfaces and classes, allowing Visual Basic programmers to write the following:

Dim ape As IApe
Dim warrior as IWarrior
Set ape = New Gorilla ' ask COM for a new Gorilla
Set warrior = ape

Alternately, the following is the Java version of the same code:

IApe ape;
IWarrior warrior;
ape = new Gorilla(); // no cast needed for [default]
warrior = (IWarrior)ape;

Both of these code fragments tell the underlying virtual machine to use the CLSID of Gorilla to indicate to CoCreateInstanceEx what type of object to create.

In the preceding IDL, the interfaces IApe, IWarrior, IEgghead, and IKeeperOfTheFaith are each referred to from within the library definition. This causes their definitions to be present in the generated type library despite the fact that they are defined outside the scope of the library definition. In fact, any data types that are used as parameters or as base interfaces for these interfaces will also be present in the generated library. It is good practice to define an implementation’s library statement in a separate IDL file that imports any interface definitions that it needs from an external IDL file that contains only interface definitions. This practice is mandatory on large projects with multiple IDL files, as it is an error for an IDL file that contains a library definition to import another IDL file that also contains a library definition. By segregating library definitions into distinct IDL files, the interfaces used by a library can be cleanly imported into other projects without having to worry about multiple library definitions. If this practice is not used, then the only way to import an interface definition from an IDL file that contains a library definition is to import the generated type library using the importlib directive:

// humans.idl ////////////

// apeitfs.idl DOESN’T have a library statement, so import
import "apeitfs.idl"; 
[
  uuid(753A8AC9-A7FF-11d0-8C30-0080C73925BA),
  version(1.0), lcid(9), helpstring("Humans that need apes")
]
library HumanLib {
  importlib("stdole32.tlb"); // bring in std defs.
// Dogs.idl DOES have a library definition, so importlib
// its corresponding type library
  importlib("dogs.tlb"); 

  [uuid(753A8AD1-A7FF-11d0-8C30-0080C73925BA)]
  coclass DogApe {
    interface IDog;
    interface IApe;
  }
}

Simple projects often use a single IDL file to define both the interfaces and classes exported from a project. For simple interfaces, this is reasonable, as the generated type library will contain one-to-one mappings of the original IDL definitions, allowing users of the library to use importlib with no loss of information. Unfortunately, for complex interfaces, many of the original IDL-isms are lost in the resultant type library, and using importlib will not work as expected. A future version of the MIDL compiler may be able to generate type libraries that contain all of the original IDL.

© 1998 by Addison Wesley Longman, Inc. All rights reserved.