The following illustration depicts the relationship among the various classes and interfaces used in defining an ATL COM object.
ATL Structure
ATL implements IUnknown in two phases.
Other aspects of your ATL COM object are handled by other classes:
For more information, see the following sections in this article:
CComObjectRootEx is essentialall ATL objects must have one instance of CComObjectRootEx or CComObjectRoot in their inheritance. CComObjectRootEx provides the default QueryInterface mechanism based on COM map entries.
Through its COM map, an object's interfaces are exposed to a client when the client queries for an interface. The query is performed through CComObjectRootEx::InternalQueryInterface. InternalQueryInterface only handles interfaces in the COM map table.
You can enter interfaces into the COM map table with the macro COM_INTERFACE_ENTRY or one of its variants. For example, the following code from the BEEPER sample enters the interfaces IDispatch, IBeeper, and ISupportErrorInfo into the COM map table:
BEGIN_COM_MAP(CBeeper) COM_INTERFACE_ENTRY(IDispatch) COM_INTERFACE_ENTRY(IBeeper) COM_INTERFACE_ENTRY_TEAR_OFF(IID_ISupportErrorInfo, CBeeper2) END_COM_MAP( )
The template classes, CComObject, CComAggObject, and CComPolyObject, are always the most derived classes in the inheritance chain. It is their responsibility to handle all the methods in IUnknown: QueryInterface, AddRef, and Release. In addition, CComAggObject and CComPolyObject (when used for aggregated objects) provide the special reference counting and QueryInterface semantics required for the inner unknown.
Whether CComObject, CComAggObject, or CComPolyObject is used depends on whether you declare the DECLARE_POLY_AGGREGATABLE macro and on whether your object is being aggregated:
The advantage of using CComAggObject and CComObject is that the implementation of IUnknown is optimized for the kind of object being created. For instance, a nonaggregated object only needs a reference count, while an aggregated object needs both a reference count for the inner unknown and a pointer to the outer unknown.
The advantage of using CComPolyObject is that you avoid having both CComAggObject and CComObject in your module to handle the aggregated and nonaggregated cases. A single CComPolyObject object handles both cases. This means only one copy of the vtable and one copy of the functions exist in your module. If your vtable is large, this can substantially decrease your module size. However, if your vtable is small, using CComPolyObject can result in a slightly larger module size because it is not optimized for an aggregated or nonaggregated object, as are CComAggObject and CComObject.
The DECLARE_POLY_AGGREGATABLE macro is automatically added to your class definition by the ATL Object Wizard when you create a full control or Internet Explorer control. For more information about the wizard, see the article Creating an ATL Project.
The template class IDispatchImpl can be used to provide a default implementation of the IDispatch portion of any dual interfaces on your object.
If your object uses the IErrorInfo interface to report errors back to the client, then your object must support the ISupportErrorInfo interface. The template class ISupportErrorInfoImpl provides an easy way to implement this if you only have a single interface that generates errors on your object.
ATL uses CComCoClass to define the default class factory and aggregation model for your object. CComCoClass specifies the following two macros:
You can override either of these defaults by specifying another macro in your class defintion. For example, to use CComClassFactory2 instead of CComClassFactory, specify the DECLARE_CLASSFACTORY2 macro:
class CMyClass : ..., public CComCoClass<CMyClass, &CLSID_CMyClass> { public: DECLARE_CLASSFACTORY2(CMyLicense) ... };
Two other macros that define a class factory include DECLARE_CLASSFACTORY_AUTO_THREAD and DECLARE_CLASSFACTORY_SINGLETON.
ATL also uses the typedef mechanism to implement default behavior. For example, the DECLARE_AGGREGATABLE macro uses typedef to define a type called _CreatorClass, which is then reference throughout ATL. Note that in a derived class, a typedef using the same name as the base classs typedef results in ATL using your definition and overriding the default behavior.
To create an aggregate:
If you use and release an interface from the aggregate during FinalConsruct, you should add the DECLARE_PROTECT_FINAL_CONSTRUCT macro to the definition of your class object.