Chapter 5 Custom Components and the Component Object Model

Arthur: Camelot!

Galahad: Camelot…

Lancelot: Camelot…

Patsy: It's only a model….

Arthur: Sh!

From Monty Python and the Holy Grail

When OLE 2 was introduced, almost everyone talked about it in the context of compound documents because OLE 1 dealt with nothing but compound documents. I was one such person, and for a long time (before writing the first edition of this book) I struggled along trying to present OLE 2 as a better way to create compound documents.

But OLE 2 had all these other nagging details that remained enigmatic and confusing. In particular, there was something called COM, the Component Object Model, which apparently had something to do with the interface model that we saw in Chapter 2. COM is at the core of everything in OLE, but that nasty word "Model" just didn't fly with a lot of people. On a few occasions when I was giving an oral presentation, the mere mention of "object model" in the context of OLE interfaces brought glazed looks to many faces in the audience. Many people, especially those trained in traditional object-oriented development school of analysis, design, Booch notation, and so on, understood that an "object model" was a specific set of defined objects and their relationships in a hierarchy—in essence, a "model" or structure that represented some particular business problem. Any mention I made about the Component Object Model was interpreted in a variety of ways, and few people understood how a "model"—a specific design for a specific problem—could form the basis for an extensible service architecture in an operating system, which is what I was usually trying to talk about.

COM, if you think of it as a single word and forget to spell out the acronym, is really nothing more than a specification of how interfaces work and of the fundamental mechanisms that make the creation and use of custom components (services) possible. To use these mechanisms, a client need only know the unique identifier of a component—its class identifier, or CLSID—which it might read from the registry, extract from type information, or obtain at run time through some other means.

Given this CLSID and no other knowledge about a component (except that it supports at least the IUnknown interface), a client can ask some implementation of COM to retrieve for it an interface pointer to the root object of that component. From there the client can query for interfaces and check type information for more details about the component and its object. The COM implementation, called the COM Library, performs all the magic necessary to instantiate the object and establish any necessary marshaling across process or machine boundaries. So COM is quite full of some specific, and powerful, functionality. The COM Library, part of OLE32.DLL, also contains the COM API functions such as CoInitialize and CoGetMalloc and is the generic part of the specification that needs to be implemented only once per system.

The idea of a "model" was part of COM's name because the specification prescribes a certain structure on custom components servers—what exported functions a DLL should have, what an EXE server should do on startup and shutdown, and so forth. These servers implement the remaining parts of the COM specification that the COM Library does not. Between the two, they provide the complete implementation of the specification for clients.

In this chapter, we'll cover the majority of what constitutes the guts of COM as far as client and server implementations are concerned. As you might have noticed, the clients and components we've seen in sample code so far have all been contained within the same application, separated at most by a source file boundary. However, a component integration system such as OLE must facilitate communication between clients and components across arbitrary process and network boundaries. Furthermore, this communication must be transparent, so a client can always make interface member calls through a pointer in its own address space, just as for in-process objects. This is the concept of Local/Remote Transparency, the internal architecture of which is the topic of Chapter 6.

In this chapter, we're primarily concerned with the higher-level APIs and interfaces that are involved in establishing communication between a client and the root object of some component, regardless of the location of that component. This mechanism is centered on the instantiation of that root object based on a CLSID that is associated with a particular server module (a DLL or an EXE). A part of COM called the Service Control Manager (SCM, pronounced "scum") takes a CLSID and does all the work to find and run the server, have it create the object, set up Local/Remote Transparency, and return a meaningful and callable interface pointer to the client. After that, it gets out of the way, with the only thing standing between the client and the object being the necessary marshaling support.

This mechanism is the single most fundamental component/object creation mechanism in OLE, by which the CLSID uniquely identifies the component of interest. It is used with OLE Automation objects, OLE Document content objects, OLE Controls, and anything else that you want to act as an extended service to the existing component system. To support such a generic mechanism, a component server must follow a few standards, as we'll see, which include additional registry entries and can include licensing control and self-registration. We'll also see how to make one server emulate—that is, act in place of—another server and how you can break pieces of applications off into components. Through all of this you should gain a good understanding of what components are and how to create them. It is the implementation of components that makes OLE and COM really powerful. Without custom components, COM by itself is only a model.