Class Objects

[Previous] [Next]

A class object is a COM object that typically implements the IClassFactory interface to create instances of objects of a particular CLSID. CoCreateInstanceEx depends on the class object's IClassFactory::CreateInstance method to create the object of the requested type (CLSID) and return an interface pointer to the new instance. At that point, the class object is no longer needed, so CoCreateInstanceEx releases it. The class object can be obtained directly with CoGetClassObject in case you have other functionality in the class object that you need to access, or if you want to optimize creation of many objects of the same type.

ATL Support for Class Object Creation

ATL supports the COM idiom of a class object that implements IClassFactory through CComClassFactory. By default, all of your ATL objects use CComClassFactory as a common class object implementation. Your object inherits this behavior by deriving from CComCoClass. Inside CComCoClass (atlcom.h), the following macro is declared:


This macro expands to


and finally results in the following typedef in CComCoClass (for EXE servers):

 typedef CComCreator< CComObjectNoLock< CComClassFactory > >\     _ClassFactoryCreatorClass; 

For DLL servers, the following typedef is used instead:

 typedef CComCreator< CComObjectCached< CComClassFactory > >\     _ClassFactoryCreatorClass; 

In both cases, your object inherits a typedef for _ClassFactoryCreatorClass from CComCoClass. The derivation from CComObjectCached or CComObjectNoLock is related to server lifetime, which will be discussed later in this chapter. For now, let's stick to the task at hand—object creation. At the bottom of the typedef chain is CComCreator. The entire class is shown here:

 template <class T1> class CComCreator { public:     static HRESULT WINAPI CreateInstance(void* pv,         REFIID riid, LPVOID* ppv)     {         ATLASSERT(*ppv == NULL);         HRESULT hRes = E_OUTOFMEMORY;         T1* p = NULL;         ATLTRY(p = new T1(pv))         if(p != NULL)         {             p->SetVoid(pv);             p->InternalFinalConstructAddRef();             hRes = p->FinalConstruct();             p->InternalFinalConstructRelease();             if(hRes == S_OK)                 hRes = p->QueryInterface(riid, ppv);             if(hRes != S_OK)                 delete p;         }         return hRes;     } }; 

Notice that CComCreator has a single static CreateInstance function that creates a new instance of the templatized type T1, which will be CComObjectxxx<CComClassFactory >. This is where the actual creation of the class object occurs. It might seem like a lot of work simply to create a class object; but consider that through this mechanism, ATL uses a single implementation of a class object to create all types of objects. Also, the DECLARE_CLASSFACTORY macro that started this expansion declares the most common class object type configuration. You can override this default declaration by inserting one of the macros described in Table 7-3 directly into your object header file.

Table 7-3. Class Factory Macros.

Macro Description
DECLARE_CLASSFACTORY2(lic) Declares a class object that implements IClassFactory2 for licensed components
DECLARE_CLASSFACTORY_AUTO_THREAD() Allows objects in EXE servers to be created in multiple apartments
DECLARE_CLASSFACTORY_SINGLETON(obj) Allows only a single instance of your object to be created in the process that contains it

Because the default DECLARE_CLASSFACTORY macro is declared in CComCoClass, any macro declared in your derived class will create a _ClassFactoryCreatorClass typedef that overrides the CComCoClass version as long as your object class name is used to reference it. Regardless of the macro used, a class object can be created from your object class using this syntax:


Enabling CComClassFactory::CreateInstance

A class object needs to know how to create objects of a particular type. So far, we've learned how to create a generic CComClassFactory object. Now let's look at how this class object creates an instance of a specified type. First we need to look more closely at CComCreator::CreateInstance.

 static HRESULT WINAPI CreateInstance(void* pv,     REFIID riid, LPVOID* ppv) {     ATLASSERT(*ppv == NULL);     HRESULT hRes = E_OUTOFMEMORY;     T1* p = NULL;     ATLTRY(p = new T1(pv))     if(p != NULL)     {         p->SetVoid(pv);     }  } 

Notice that the void* pv parameter in the preceding code is passed up to the class factory in SetVoid. CComClassFactory::SetVoid reveals that void* is actually a function pointer, shown here:

 void SetVoid(void* pv) {     m_pfnCreateInstance = (_ATL_CREATORFUNC*)pv; } _ATL_CREATORFUNC* m_pfnCreateInstance; 

You'll see m_pfnCreateInstance again in the IClassFactory::CreateInstance implementation in CComClassFactory, shown here:

 STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter,     REFIID riid, void** ppvObj) {          hRes = m_pfnCreateInstance(pUnkOuter, riid, ppvObj);          return hRes; } 

Because this same code is executed for all class objects when the client calls IClassFactory::CreateInstance, or executed by COM inside CoCreateInstanceEx, m_pfnCreateInstance must be able to create a specific object type. Therefore, m_pfnCreateInstance resolves to yourobject::_CreatorClass::CreateInstance, which is the next typedef we need to look at. In addition to the DECLARE_CLASSFACTORY macro, CComCoClass has the DECLARE_AGGREGATABLE(T) macro, which creates the _CreatorClass typedef shown here:

 typedef CComCreator2< CComCreator<CComObject<x>>,\     CComCreator< CComAggObject<x>>> _CreatorClass; 

The template parameter x is your object class name, as passed in to CComCoClass. CComCreator2 is very much like CComCreator, which is used for class objects, except that CComCreator2 creates an aggregated component if an outer unknown is passed in to its CreateInstance method. The CComCreator2 class is shown here:

 template <class T1, class T2> class CComCreator2 { public:     static HRESULT WINAPI CreateInstance(void* pv,         REFIID riid, LPVOID* ppv)     {         ATLASSERT(*ppv == NULL);         return (pv == NULL) ?              T1::CreateInstance(NULL, riid, ppv) :              T2::CreateInstance(pv, riid, ppv);     } }; 

T1 and T2 are the CComCreator<CComObject<YourObject >> and CComCreator<CComAggObject<YourObject>> parameters from the DECLARE_AGGREGATABLE macro. Like the class factory macros, other macro choices are available, depending on whether aggregation is allowed or required by your component. Aggregation isn't the focus of this discussion, so we'll continue on the track of object creation. Assuming we're not aggregated, a call to YourClass:: CreatorClass::CreateInstance results in the static CComCreator2::CreateInstance (shown above) function calling T1::CreateInstance, where T1 is CComCreator<CComObject<YourObject>>. CComCreator::CreateInstance finally creates a new instance of your object using the new operator. That sequence, starting with IClassFactory::CreateInstance, is:

  1. CComClassFactory::CreateInstance
  2. CComCreator2::CreateInstance
  3. CComCreator::CreateInstance

After you create your object, it is initialized and clients call QueryInterface for the requested interface. (T1 is your object class name.) The complete code for CComCreator is shown in the section, "ATL Support for Class Object Creation," above.

Inside Atl
Inside ATL (Programming Languages/C)
ISBN: 1572318589
EAN: 2147483647
Year: 1998
Pages: 127 © 2008-2017.
If you may any questions please contact us: