CComObject Classes [Previous] [Next]

CComObject Classes

At the bottom of an ATL object instance inheritance tree you'll find CComObject or some variation of it. CComObject classes are the entry point for all the IUnknown methods on an object. Because the implementation of QueryInterface varies depending on whether the object is being aggregated, the particular flavor of CComObject you use is determined based on the object's declared aggregation support and the context in which the creation occurs. Aggregation support is declared statically with one of a set of aggregation macros. Table 6-2 shows the available aggregation macros, the corresponding CComObject class that is used as the base class for the object, and the resulting ATL base class used with the associated aggregation macro. In most cases, the base class varies depending on whether the object is instantiated as an aggregate.

Table 6-2. Aggregation Macros.

Macro Normal Case Aggregated Case
DECLARE_NOT_AGGREGATABLE CComObject Not supported
DECLARE_AGGREGATABLE CComObject CComAggObject
DECLARE_ONLY_AGGREGATABLE Not supported CComAggObject
DECLARE_POLY_AGGREGATABLE CComPolyObject CComPolyObject

Each macro declares a typedef for the creator class that is used in normal and aggregated cases. Creators work with class objects to create an object instance when a client requests it. (Creators and class objects are covered in more detail in Chapter 7.) By default, objects inherit DECLARE_AGGREGATABLE from CComCoClass. You can override the default by inserting one of the other macros in your object header file or by making a choice other than "Yes" for aggregation on the Attributes tab in the Object Wizard. By implementing QueryInterface, AddRef, and Release, the CComObject classes relieve us from writing the boilerplate code over and over again, and they also offer free aggregation support. CComObject also handles incrementing and decrementing the module count when the object is created and destroyed—that's more boilerplate code you don't have to write.

Each CComObject class has a static CreateInstance member that is used to create the object. If the object is created by means of CoCreate, the class object makes the call in its IClassFactory::CreateInstance method. If you're creating objects internally, however, you can also call this function directly and bypass the class object entirely. CreateInstance returns a pointer to a newly created CComObject, as shown here:

template <class Base>
HRESULT WINAPI CComObject<Base>::CreateInstance(CComObject<Base>** pp)
{
    ATLASSERT(pp != NULL);
    HRESULT hRes = E_OUTOFMEMORY;
    CComObject<Base>* p = NULL;
    ATLTRY(p = new CComObject<Base>())
    if(p != NULL)
    {
        p->SetVoid(NULL);
        p->InternalFinalConstructAddRef();
        hRes = p->FinalConstruct();
        p->InternalFinalConstructRelease();
        if(hRes != S_OK)
        {
            delete p;
            p = NULL;
        }
    }
    *pp = p;
    return hRes;
}

You can then use the returned object as you would any other COM object. Notice that CreateInstance doesn't call AddRef on the object, so you might want to call QueryInterface after creation to get the automatic deletion when the reference count reaches 0. The following code shows how you can use CreateInstance directly:

CComObject<CSimpleObject> *pObj;
HRESULT hr = CComObject<CSimpleObject>::CreateInstance(&pObj);
if(SUCCEEDED(hr))
{
    CComPtr<ISimpleObj> pISimple;
    hr =  pObj->QueryInterface(&pISimple);
    if(SUCCEEDED(hr))
        pISimple->Test();
}

The use of the CComObject QueryInterface override allows the use of a typedef pointer. Using CComObject as shown in the preceding code does save some cycles by not creating a class object through the CoCreate mechanism. Even so, this process is somewhat tedious for a stack-based object. ATL provides the CComObjectStack class to simplify object creation and usage within a stack frame. We can write the same code using CComObjectStack.

CComObjectStack<CSimpleObject> Obj;
Obj.Test();

Use CComObjectStack when the lifetime of the object is contained in a stack frame. When an object's lifetime is contained in a stack frame, reference counting isn't required and therefore isn't implemented. QueryInterface isn't allowed on CComObjectStack instances.

ATL has several other variations of CComObject to handle specific reference counting, module locking, and QueryInterface cases. These variations and their descriptions are listed here:

One of the fundamental tasks of the CComObject classes is to appropriately route QueryInterface. Our next step is to look more closely at the implementation of QueryInterface once CComObject hands it off. We'll start by examining how ATL uses the COM map to manage the list of interfaces available from an object.