Writing COM Code

As you can see, writing COM client code isn't a whole lot different from writing regular C++ code. However, the C++ classes that the client talks to are abstract base classes. Instead of calling operator new as you would in C++, you create COM objects and acquire COM interfaces by explicitly calling some sort of API function. And instead of deleting the object outright, you simply follow the COM interface rule of balancing calls to AddRef with calls to Release.

What does it take to get the COM class up and running? You saw how to do it using MFC in Chapter 24. Here's another example of implementing CSpaceship as a COM class. This example uses the multiple inheritance approach to writing COM classes. That is, the C++ class inherits from several interfaces and then implements the union of all the functions (including IUnknown, of course).

struct CSpaceship : IMotion, IDisplay {
    ULONG m_cRef;
    int m_nPosition;

    CSpaceship() : m_cRef(0),
                   m_nPosition(0) {
    }

    HRESULT QueryInterface(REFIID riid,
                           void** ppv);
    ULONG AddRef() {
        return InterlockedIncrement(&m_cRef);
    }
    ULONG Release() {
        ULONG cRef = InterlockedIncrement(&m_cRef);
        if(cRef == 0){
            delete this;
            return 0;
        } else
            return m_cRef;
    }

    // IMotion functions:
    void Fly() {
        // Do whatever it takes to fly here
    }
    int GetPosition() {
        return m_nPosition;
    }

    // IVisual functions:
    void Display() {
        // Uncloak
    }
};

COM Classes Using Multiple Inheritance

If you're used to seeing plain C++ code, the preceding code might look a little strange to you. This is a less common form of multiple inheritance called interface inheritance. Most C++ developers are used to an implementation inheritance in which the derived class inherits everything from the base class—including the implementation. Interface inheritance simply means the derived class inherits the interfaces of the base class. The preceding code effectively adds two data members to the CSpaceship class—a vptr for each implied vtable.

When using the multiple inheritance approach to implementing interfaces, each interface shares CSpaceship's implementation of IUnknown. This sharing illustrates a rather esoteric yet important concept known as COM identity. The basic idea of COM identity is that IUnknown is the void* of COM. IUknown is the one interface guaranteed to be hanging off any object, and you can always get to it. COM identity also says (in the previous example) the client can call QueryInterface through the CSpaceship IMotion interface to get the IVisible interface. Conversely, the client can call QueryInterface through the CSpaceship IVisible interface to get the IMotion interface. Finally, the client can call QueryInterface through IUnknown to acquire the IMotion or the IVisible interface, and the client can call QueryInterface through either IMotion or IVisual to get a pointer to IUnknown. To learn more about COM identity, see Essential COM by Don Box (Addison-Wesley, 1997) or Inside COM by Dale Rogerson (Microsoft Press, 1997).

Often you'll see COM classes illustrated with "lollipop" diagrams depicting the interfaces implemented by a COM class. You can see an example of a lollipop diagram in "The IUnknown Interface and the QueryInterface Member Function" in Chapter 24.

The multiple inheritance method of implementing CSpaceship automatically fulfills the rules of COM identity. Note that all calls to QueryInterface, AddRef, and Release land in the same place in the C++ class, regardless of the interface through which they were called.

This is more or less the essence of COM. As a COM developer, your job is to create useful services and expose them through COM interfaces. At the most basic level, this means wiring up some function tables to follow COM's identity rules. You've seen two ways to accomplish this so far. (Chapter 24 showed you how to do it using nested classes and MFC. This chapter just showed you how to write a COM class using multiple inheritance in C++.) However, in addition to interface programming and writing classes to implement interfaces, there are several other pieces to the COM puzzle.