What Is COM?
COM is a software architecture that facilitates the dynamic composition of software. The DLL model doesn't handle versioning well, and the raw remote procedure call (RPC) mechanism is difficult to manage. COM tries to solve these issues.The "problem" is that there's no standard way for Windows program modules to communicate with one another. But what about the DLL, with its exported functions, Dynamic Data Exchange (DDE), the Windows Clipboard, and the Windows API itself, not to mention legacy standards such as Visual Basic custom controls (VBXs) and OLE 1? Aren't they good enough? Well, no. This potpourri of standards makes integrating software a nightmare.
The Essence of COM
What's wrong with the old standards? A lot. The Windows API has too large a programming "surface area"—more than 350 separate functions. VBXs don't work in the 32-bit world. DDE comes with a complicated system of applications, topics, and items. How you call a DLL is totally application-specific. COM, in contrast, provides a unified, expandable, object-oriented communications protocol for Windows that supports the following features:
A standard, language-independent way for a Win32 client EXE to load and call a Win32 DLL
A general-purpose way for one EXE to control another EXE on the same computer (the DDE replacement)
A replacement for the VBX, called an ActiveX control
A powerful new way for application programs to interact with the operating system
Expansion to accommodate new protocols such as Microsoft's OLE DB database interface
Distributed COM (DCOM), which allows one EXE to communicate with another EXE residing on a different computer, even if the computers use different microprocessor-chip families
So what is COM? That's an easier question to ask than to answer. At DevelopMentor, a training facility for software developers, the party line for years has been that "COM is love." That is, COM is a powerful integrating technology that allows you to mix all sorts of disparate software parts together at run time. COM allows developers to write software that runs together regardless of issues such as thread-awareness and language choice.The COM protocol connects one software module with another and then drops out of the picture. After the connection is made, the two modules can communicate through a mechanism called an interface. Interfaces require no statically or dynamically linked entry points or hard-coded addresses other than the few general-purpose COM functions that start the communication process. Interface (more precisely, COM interface) is a term that you'll be seeing a lot of.
What Is a COM Interface?
Before digging into the topic of interfaces, let's reexamine the nature of inheritance and polymorphism in normal C++. We'll use a planetary-motion simulation to illustrate C++ inheritance and polymorphism. Imagine a spaceship that travels through our solar system under the influence of the sun's gravity. In ordinary C++, you can declare a CSpaceship class and write a constructor that sets the spaceship's initial position and acceleration. You can then write a nonvirtual member function named Fly that implements Kepler's laws to model the movement of the spaceship from one position to the next—say, over a period of 0.1 second. You can also write a Display function that paints an image of the spaceship in a window. The most interesting feature of the CSpaceship class is that the interface of the C++ class (the way the client talks to the class) and the implementation are tightly bound. One of the main goals of COM is to separate a class's interface from its implementation.
If we think of this example within the context of COM, the spaceship code can exist as a separate EXE or DLL (the component), which is a COM module. In COM, the simulation manager (the client program) can't call Fly or any CSpaceship constructor directly: COM provides only a standard global function to gain access to the spaceship object, and then the client and the object use interfaces to talk to one another.Before we tackle real COM, let's build a COM simulation in which both the component and the client code are statically linked in the same EXE file. For our standard global function, we'll invent a function named GetClassObject. In this COM simulation, clients will use this global single abstract function for objects of a particular class. In real COM, clients get a class object first and then ask the class object to manufacture the real object in much the same way that MFC does dynamic creation.GetClassObject has the following three parameters:
BOOL GetClassObject(int nClsid, int nIid, void** ppvObj);
The first parameter, nClsid, is a 32-bit integer that uniquely identifies the CSpaceship class. The second parameter, nIid, is the unique identifier of the interface that we want. The third parameter is a pointer to an interface to the object. Remember that we'll be dealing with interfaces, which are different from classes. As it turns out, a class can have several interfaces, so the last two parameters exist to manage interface selection. The function will return TRUE if the call is successful.Now let's back up to the design of CSpaceship. We haven't really explained spaceship interfaces yet. A COM interface is a C++ base class (actually, a C++ struct) that declares a group of pure virtual functions. These functions completely control some aspect of derived class behavior. For CSpaceship, let's write an interface named IMotion, which controls the spaceship object's position. For simplicity's sake, we'll declare just two functions, Fly and GetPosition, and we'll keep things uncomplicated by making the position value an integer. The Fly function calculates the position of the spaceship, and the GetPosition function returns a reference to the current position. Here are the declarations:
struct IMotion
{
virtual void Fly() = 0;
virtual int& GetPosition() = 0;
};
class CSpaceship : public IMotion
{
protected:
int m_nPosition;
public:
CSpaceship() { m_nPosition = 0; }
void Fly();
int& GetPosition() { return m_nPosition; }
};
IMotion* pMot;
GetClassObject(CLSID_CSpaceship, IID_IMotion, (void**) &pMot);
Assume for the moment that COM can use the unique integer identifiers CLSID_CSpaceship and IID_IMotion to construct a spaceship object instead of some other kind of object. If the call is successful, pMot will point to a CSpaceship object that GetClassObject will somehow construct. As you can see, the CSpaceship class implements the Fly and GetPosition functions, and our main program can call them for the one particular spaceship object, as shown here:
int nPos = 50;
pMot->GetPosition() = nPos;
pMot->Fly();
nPos = pMot->GetPosition();
TRACE("new position = %d\n", nPos);
Now the spaceship is off and flying. We're controlling it entirely through the pMot pointer. Notice that pMot is technically not a pointer to a CSpaceship object. However, in this case, a CSpaceship pointer and an IMotion pointer are the same because CSpaceship is derived from IMotion. You can see how the virtual functions work here: It's classic C++ polymorphism.Let's make things a little more complex by adding a second interface, IVisual, which handles the spaceship's visual representation. One function is enough—Display. Here's the whole base class:
struct IVisual
{
virtual void Display() = 0;
};
Are you getting the idea that COM wants you to associate functions in groups? You're not imagining it. But why? Well, in our space simulation, we'll probably want to include other kinds of objects in addition to spaceships. Imagine that the IMotion and IVisual interfaces are being used for other classes. Perhaps a CSun class has an implementation of IVisual but does not have an implementation of IMotion, and perhaps a CSpaceStation class has other interfaces as well. If you "publish" your IMotion and IVisual interfaces, perhaps other space simulation software companies will adopt them.You can think of an interface as a contract between two software modules. The idea is that interface declarations never change. If you want to upgrade your spaceship code, you don't change the IMotion or the IVisual interface; rather, you add a new interface, such as ICrew. The existing spaceship clients can continue to run with the old interfaces, and new client programs can use the new ICrew interface as well. These client programs can find out at run time which interfaces a particular spaceship software version supports.
You can consider the GetClassObject function a more powerful alternative to the C++ new operator and class constructors. With the ordinary new operator and constructor mechanism, you obtain one object containing member functions. With the GetClassObject function, you obtain the object and a way to talk to the object (an interface). As you'll see later, you start with one interface and then use that interface to get other interfaces to the same object.So how do you program two interfaces for CSpaceship? You could use C++ multiple inheritance, but that doesn't work if two interfaces have the same member function name. The MFC library uses nested classes instead, so that's what we'll use to illustrate multiple interfaces on the CSpaceship class. Here's a first cut at nesting interfaces within the CSpaceship class:
class CSpaceship
{
protected:
int m_nPosition;
int m_nAcceleration;
int m_nColor;
public:
CSpaceship()
{ m_nPosition = m_nAcceleration = m_nColor = 0; }
class XMotion : public IMotion
{
public:
XMotion() { }
virtual void Fly();
virtual int& GetPosition();
} m_xMotion;
class XVisual : public IVisual
{
public:
XVisual() { }
virtual void Display();
} m_xVisual;
friend class XVisual;
friend class XMotion;
};
Note | It might make sense to make m_nAcceleration a data member of XMotion and make m_nColor a data member of XVisual. We'll make them data members of CSpaceship because that strategy is more compatible with the MFC macros, as you'll see later. |
Notice that the implementations of IMotion and IVisual are contained within the "parent" CSpaceship class. In COM, this parent class is known as the class with object identity. Be aware that m_xMotion and m_xVisual are actually embedded data members of CSpaceship. Indeed, you could have implemented CSpaceship strictly with embedding. Nesting, however, offers two advantages: First, nested class member functions can access parent class data members without the need for CSpaceship pointer data members. Second, the nested classes are neatly packaged along with the parent while remaining invisible outside the parent. Look at the following code for the GetPosition member function:
int& CSpaceship::XMotion::GetPosition()
{
METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis
return pThis->m_nPosition;
}
Notice the double-scope resolution operators, which are necessary for nested class member functions. METHOD_PROLOGUE is a one-line MFC macro that uses the C offsetof operator to retrieve the offset used in generating a this pointer to the parent class, pThis. The compiler always knows the offset from the beginning of parent class data to the beginning of nested class data. GetPosition can thus access the CSpaceship data member m_nPosition.Now suppose you have two interface pointers, pMot and pVis, for a particular CSpaceship object. (Don't worry yet about how you got these pointers.) You can call interface member functions in the following manner:
pMot->Fly();
pVis->Display();
What's happening under the hood? In C++, each class (at least, each class that has virtual functions and is not an abstract base class) has a virtual function table, which is otherwise known as a vtable. In this example, that means there are vtables for CSpaceship::XMotion and CSpaceship::XVisual. For each object, there's a pointer to the object's data, the first element of which is a pointer to the class's vtable. The pointer relationships are shown on the following page.

Theoretically, it's possible to program COM in C. If you look at the Windows header files, you'll see code such as this:
#ifdef __cplusplus
// C++-specific headers
#else
/* C-specific headers */
#endif
In C++, interfaces are declared as C++ structures, often with inheritance; in C, they're declared as C typedef structures with no inheritance. In C++, the compiler generates vtables for your derived classes; in C, you must "roll your own" vtables, and that gets tedious. It's important to realize, however, that in neither language do the interface declarations have data members, constructors, or destructors. Therefore, you can't rely on the interface having a virtual destructor—but that's not a problem because you never invoke a destructor for an interface.
The IUnknown Interface and the QueryInterface Member Function
Let's get back to the problem of how to obtain your interface pointers in the first place. COM declares a special interface named IUnknown for this purpose. As a matter of fact, all interfaces are derived from IUnknown, which has a pure virtual member function, QueryInterface, that returns an interface pointer based on the interface ID you feed it.Once the interface mechanisms are hooked up, the client needs to get an IUnknown interface pointer (at the very least) or a pointer to one of the derived interfaces. Here's the new interface hierarchy, with IUnknown at the top:
struct IUnknown
{
virtual BOOL QueryInterface(int nIid, void** ppvObj) = 0;
};
struct IMotion : public IUnknown
{
virtual void Fly() = 0;
virtual int& GetPosition() = 0;
};
struct IVisual : public IUnknown
{
virtual void Display() = 0;
};
To satisfy the compiler, we must add QueryInterface implementations in both CSpaceship::XMotion and CSpaceship::XVisual. What do the vtables look like after this is done? For each derived class, the compiler builds a vtable with the base class function pointers on top, as shown here:

GetClassObject can get the interface pointer for a given CSpaceship object by getting the address of the corresponding embedded object. Here's the code for the QueryInterface function in XMotion:
BOOL CSpaceship::XMotion::QueryInterface(int nIid,
void** ppvObj)
{
METHOD_PROLOGUE(CSpaceship, Motion)
switch (nIid) {
case IID_IUnknown:
case IID_IMotion:
*ppvObj = &pThis->m_xMotion;
break;
case IID_IVisual:
*ppvObj = &pThis->m_xVisual;
break;
default:
*ppvObj = NULL;
return FALSE;
}
return TRUE;
}
Because IMotion is derived from IUnknown, an IMotion pointer is a valid pointer if the caller asks for an IUnknown pointer.
Note | The COM standard requires that QueryInterface return exactly the same IUnknown pointer value for IID_IUnknown, no matter which interface pointer you start with. Thus, if two IUnknown pointers match, you can assume that they refer to the same object. IUnknown is sometimes known as the "void*" of COM because it represents the object's identity. |
The following is a GetClassObject function that uses the address of m_xMotion to obtain the first interface pointer for the newly constructed CSpaceship object:
BOOL GetClassObject(int& nClsid, int& nIid,
void** ppvObj)
{
ASSERT(nClsid == CLSID_CSpaceship);
CSpaceship* pObj = new CSpaceship();
IUnknown* pUnk = &pObj->m_xMotion;
return pUnk->QueryInterface(nIid, ppvObj);
}
Now your client program can call QueryInterface to obtain an IVisual pointer, as shown here:
IMotion* pMot;
IVisual* pVis;
GetClassObject(CLSID_CSpaceship, IID_IMotion, (void**) &pMot);
pMot->Fly();
pMot->QueryInterface(IID_IVisual, (void**) &pVis);
pVis->Display();
Notice that the client uses a CSpaceship object, but it never has an actual CSpaceship pointer. Thus, the client cannot directly access CSpaceship data members, even if they're public. Notice also that we haven't tried to delete the spaceship object yet—that will come shortly.There's a special graphical representation for interfaces and COM classes. Interfaces are shown as small circles (or jacks) with lines attached to their class. The IUnknown interface, which every COM class supports, is at the top, and the others are on the left. The CSpaceship class can be represented like this:

Reference Counting: The AddRef and Release Functions
COM interfaces don't have virtual destructors, so it isn't cool to write code like the following:
delete pMot; // pMot is an IMotion pointer; don't do this
COM has a strict protocol for deleting objects. The two other IUnknown virtual functions, AddRef and Release, are the key. Each COM class has a data member—m_dwRef in the MFC library—that keeps track of how many "users" an object has. Each time the component program returns a new interface pointer (as in QueryInterface), the program calls AddRef, which increments m_dwRef. When the client program is finished with the pointer, it calls Release. When m_dwRef goes to 0, the object destroys itself. Here's an example of a Release function for the CSpaceship::XMotion class:
DWORD CSpaceship::XMotion::Release()
{
METHOD_PROLOGUE(CSpaceship, Motion) // makes pThis
if (pThis->m_dwRef == 0)
return 0;
if (--pThis->m_dwRef == 0) {
delete pThis; // the spaceship object
return 0;
}
return pThis->m_dwRef;
}
In MFC COM-based programs, the object's constructor sets m_dwRef to 1. This means that it isn't necessary to call AddRef after the object is first constructed. A client program should call AddRef, however, if it makes a copy of an interface pointer.
Class Factories
Object-oriented terminology can get a little fuzzy sometimes. Smalltalk programmers, for example, talk about objects the way C++ programmers talk about classes. The COM literature often uses the term component object to refer to the object plus the code associated with it. COM carries with it the notion of a class object, which is sometimes referred to as a class factory. To be more accurate, it should probably be called an object factory. A COM class object represents the global static area of a specific COM class. Its analog in MFC is the CRuntimeClass. A class object is sometimes called a class factory because it often implements a special COM interface named IClassFactory. This interface, like all interfaces, is derived from IUnknown. IClassFactory's principal member function is CreateInstance, which in our COM simulation is declared like this:
virtual BOOL CreateInstance(int& nIid, void** ppvObj) = 0;
Why use a class factory? You've already seen that you can't call the target class constructor directly—you have to let the component module decide how to construct objects. The component provides the class factory for this purpose and thus encapsulates the creation step, as it should. Locating and launching component modules—and thus establishing the class factory—is expensive, but constructing objects with CreateInstance is cheap. We can therefore allow a single class factory to create multiple objects.What does all this mean? It means we messed up when we let GetClassObject construct the CSpaceship object directly. We were supposed to construct a class factory object first and then call CreateInstance to cause the class factory (object factory) to construct the actual spaceship object.Let's properly construct the spaceship simulation. First, we'll declare a new class, CSpaceshipFactory. To avoid complication, we'll derive the class from IClassFactory so we don't have to deal with nested classes. In addition, we'll add the code that tracks references:
struct IClassFactory : public IUnknown
{
virtual BOOL CreateInstance(int& nIid, void** ppvObj) = 0;
};
class CSpaceshipFactory : public IClassFactory
{
private:
DWORD m_dwRef;
public:
CSpaceshipFactory() { m_dwRef = 1; }
// IUnknown functions
virtual BOOL QueryInterface(int& nIid,
void** ppvObj);
virtual DWORD AddRef();
virtual DWORD Release();
// IClassFactory function
virtual BOOL CreateInstance(int& nIid,
void** ppvObj);
};
Next, we'll write the CreateInstance member function:
BOOL CSpaceshipFactory::CreateInstance(int& nIid, void** ppvObj)
{
CSpaceship* pObj = new CSpaceship();
IUnknown* pUnk = &pObj->m_xMotion;
return pUnk->QueryInterface(nIid, ppvObj);
}
Finally, the new GetClassObject function will construct a class factory object and return an IClassFactory interface pointer:
BOOL GetClassObject(int& nClsid, int& nIid,
void** ppvObj)
{
ASSERT(nClsid == CLSID_CSpaceship);
ASSERT((nIid == IID_IUnknown) || (nIid == IID_IClassFactory));
CSpaceshipFactory* pObj = new CSpaceshipFactory();
*ppvObj = pObj; // IUnknown* = IClassFactory* = CSpaceship*
}
The CSpaceship and CSpaceshipFactory classes work together and share the same class ID. Now the client code looks like this (without error-checking logic):
IMotion* pMot;
IVisual* pVis;
IClassFactory* pFac;
GetClassObject(CLSID_CSpaceship, IID_IClassFactory, (void**) &pFac);
pFac->CreateInstance(IID_IMotion, &pMot);
pMot->QueryInterface(IID_IVisual, (void**) &pVis);
pMot->Fly();
pVis->Display();
Notice that the CSpaceshipFactory class implements the AddRef and Release functions. It must do this because AddRef and Release are pure virtual functions in the IUnknown base class. We'll start using these functions in the next iteration of the program.
The CCmdTarget Class
We're still a long way from real MFC COM-based code, but we can take one more step in the COM simulation before we switch to the real thing. As you might guess, some code and data can be "factored out" of our spaceship COM classes into a base class. That's exactly what the MFC library does. The base class is CCmdTarget, the standard base class for document and window classes. CCmdTarget, in turn, is derived from CObject. We'll use CSimulatedCmdTarget instead, and we won't put too much in it—only the reference-counting logic and the m_dwRef data member. The CSimulatedCmdTarget functions ExternalAddRef and ExternalRelease can be called in derived COM classes. Because we're using CSimulatedCmdTarget, we'll bring CSpaceshipFactory in line with CSpaceship and we'll use a nested class for the IClassFactory interface.We can also do some factoring out inside our CSpaceship class. The QueryInterface function can be "delegated" from the nested classes to the outer class helper function ExternalQueryInterface, which calls ExternalAddRef. Thus, each QueryInterface function will increment the reference count, but CreateInstance will call ExternalQueryInterface, followed by a call to ExternalRelease. When the first interface pointer is returned by CreateInstance, the spaceship object will have a reference count of 1. A subsequent QueryInterface call will increment the count to 2, and in this case, the client will have to call Release twice to destroy the spaceship object.
One last thing: We'll make the class factory object a global object so we won't have to call its constructor. When the client calls Release, there won't be a problem because the class factory's reference count will be 2 by the time the client receives it. (The CSpaceshipFactory constructor sets the reference count to 1, and ExternalQueryInterface, called by GetClassObject, sets the count to 2.)