CComObjectRootEx Class

 

The new home for Visual Studio documentation is Visual Studio 2017 Documentation on docs.microsoft.com.

The latest version of this topic can be found at CComObjectRootEx Class.

This class provides methods to handle object reference count management for both nonaggregated and aggregated objects.

template<class ThreadModel>  
class CComObjectRootEx : public CComObjectRootBase

Parameters

ThreadModel
The class whose methods implement the desired threading model. You can explicitly choose the threading model by setting ThreadModel to CComSingleThreadModel, CComMultiThreadModel, or CComMultiThreadModelNoCS. You can accept the server's default thread model by setting ThreadModel to CComObjectThreadModel or CComGlobalsThreadModel.

Methods

CComObjectRootExConstructor.
InternalAddRefIncrements the reference count for a nonaggregated object.
InternalReleaseDecrements the reference count for a nonaggregated object.
LockIf the thread model is multithreaded, obtains ownership of a critical section object.
UnlockIf the thread model is multithreaded, releases ownership of a critical section object.

CComObjectRootBase Methods

FinalConstructOverride in your class to perform any initialization required by your object.
FinalReleaseOverride in your class to perform any cleanup required by your object.
OuterAddRefIncrements the reference count for an aggregated object.
OuterQueryInterfaceDelegates to the outer IUnknown of an aggregated object.
OuterReleaseDecrements the reference count for an aggregated object.

Static Functions

InternalQueryInterfaceDelegates to the IUnknown of a nonaggregated object.
ObjectMainCalled during module initialization and termination for derived classes listed in the object map.

Data Members

m_dwRefWith m_pOuterUnknown, part of a union. Used when the object is not aggregated to hold the reference count of AddRef and Release.
m_pOuterUnknownWith m_dwRef, part of a union. Used when the object is aggregated to hold a pointer to the outer unknown.

CComObjectRootEx handles object reference count management for both nonaggregated and aggregated objects. It holds the object reference count if your object is not being aggregated, and holds the pointer to the outer unknown if your object is being aggregated. For aggregated objects, CComObjectRootEx methods can be used to handle the failure of the inner object to construct, and to protect the outer object from deletion when inner interfaces are released or the inner object is deleted.

A class that implements a COM server must inherit from CComObjectRootEx or CComObjectRoot.

If your class definition specifies the DECLARE_POLY_AGGREGATABLE macro, ATL creates an instance of CComPolyObject<CYourClass> when IClassFactory::CreateInstance is called. During creation, the value of the outer unknown is checked. If it is NULL, IUnknown is implemented for a nonaggregated object. If the outer unknown is not NULL, IUnknown is implemented for an aggregated object.

If your class does not specify the DECLARE_POLY_AGGREGATABLE macro, ATL creates an instance of CAggComObject<CYourClass> for aggregated objects or an instance of CComObject<CYourClass> for nonaggregated objects.

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. Therefore, 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.

If your object is aggregated, IUnknown is implemented by CComAggObject or CComPolyObject. These classes delegate QueryInterface, AddRef, and Release calls to CComObjectRootEx's OuterQueryInterface, OuterAddRef, and OuterRelease to forward to the outer unknown. Typically, you override CComObjectRootEx::FinalConstruct in your class to create any aggregated objects, and override CComObjectRootEx::FinalRelease to free any aggregated objects.

If your object is not aggregated, IUnknown is implemented by CComObject or CComPolyObject. In this case, calls to QueryInterface, AddRef, and Release are delegated to CComObjectRootEx's InternalQueryInterface, InternalAddRef, and InternalRelease to perform the actual operations.

Header: atlcom.h

The constructor initializes the reference count to 0.

CComObjectRootEx();

You can override this method in your derived class to perform any initialization required for your object.

HRESULT FinalConstruct();

Return Value

Return S_OK on success or one of the standard error HRESULT values.

Remarks

By default, CComObjectRootEx::FinalConstruct simply returns S_OK.

There are advantages to performing initialization in FinalConstruct rather than the constructor of your class:

  • You cannot return a status code from a constructor, but you can return an HRESULT by means of FinalConstruct's return value. When objects of your class are being created using the standard class factory provided by ATL, this return value is propagated back to the COM client allowing you to provide them with detailed error information.

  • You cannot call virtual functions through the virtual function mechanism from the constructor of a class. Calling a virtual function from the constructor of a class results in a statically resolved call to the function as it is defined at that point in the inheritance hierarchy. Calls to pure virtual functions result in linker errors.

    Your class is not the most derived class in the inheritance hierarchy — it relies on a derived class supplied by ATL to provide some of its functionality. There is a good chance that your initialization will need to use the features provided by that class (this is certainly true when objects of your class need to aggregate other objects), but the constructor in your class has no way to access those features. The construction code for your class is executed before the most derived class is fully constructed.

    However, FinalConstruct is called immediately after the most derived class is fully constructed allowing you to call virtual functions and use the reference-counting implementation provided by ATL.

Example

Typically, override this method in the class derived from CComObjectRootEx to create any aggregated objects. For example:

class ATL_NO_VTABLE CMyAggObject :
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass<CMyAggObject, &CLSID_MyAggObject>,
   public IDispatchImpl<IMyAggObject, &IID_IMyAggObject, &LIBID_NVC_ATL_COMLib, /*wMajor =*/ 1, /*wMinor =*/ 0>
{
public:
   DECLARE_GET_CONTROLLING_UNKNOWN()
   HRESULT FinalConstruct()
   {
      return CoCreateInstance(CLSID_MyCustomClass, GetControllingUnknown(), 
         CLSCTX_ALL, IID_IUnknown, (void**)&m_pMyCustomClass);
   }

   IMyCustomClass* m_pMyCustomClass;

   // Remainder of class declaration omitted.

If the construction fails, you can return an error. You can also use the macro DECLARE_PROTECT_FINAL_CONSTRUCT to protect your outer object from being deleted if, during creation, the internal aggregated object increments the reference count then decrements the count to 0.

Here is a typical way to create an aggregate:

  • Add an IUnknown pointer to your class object and initialize it to NULL in the constructor.

  • Override FinalConstruct to create the aggregate.

  • Use the IUnknown pointer you defined as the parameter to the COM_INTERFACE_ENTRY_AGGREGATE macro.

  • Override FinalRelease to release the IUnknown pointer.

You can override this method in your derived class to perform any cleanup required for your object.

void FinalRelease();

Remarks

By default, CComObjectRootEx::FinalRelease does nothing.

Performing cleanup in FinalRelease is preferable to adding code to the destructor of your class since the object is still fully constructed at the point at which FinalRelease is called. This enables you to safely access the methods provided by the most derived class. This is particularly important for freeing any aggregated objects before deletion.

Increments the reference count of a nonaggregated object by 1.

ULONG InternalAddRef();

Return Value

A value that may be useful for diagnostics and testing.

Remarks

If the thread model is multithreaded, InterlockedIncrement is used to prevent more than one thread from changing the reference count at the same time.

Retrieves a pointer to the requested interface.

static HRESULT InternalQueryInterface(
    void* pThis,
    const _ATL_INTMAP_ENTRY* pEntries,
    REFIID iid,
    void** ppvObject);

Parameters

pThis
[in] A pointer to the object that contains the COM map of interfaces exposed to QueryInterface.

pEntries
[in] A pointer to the _ATL_INTMAP_ENTRY structure that accesses a map of available interfaces.

iid
[in] The GUID of the interface being requested.

ppvObject
[out] A pointer to the interface pointer specified in iid, or NULL if the interface is not found.

Return Value

One of the standard HRESULT values.

Remarks

InternalQueryInterface only handles interfaces in the COM map table. If your object is aggregated, InternalQueryInterface does not delegate to the outer unknown. You can enter interfaces into the COM map table with the macro COM_INTERFACE_ENTRY or one of its variants.

Decrements the reference count of a nonaggregated object by 1.

ULONG InternalRelease();

Return Value

In both non-debug and debug builds, this function returns a value which may be useful for diagnostics or testing. The exact value returned depends on many factors such as the operating system used, and may, or may not, be the reference count.

Remarks

If the thread model is multithreaded, InterlockedDecrement is used to prevent more than one thread from changing the reference count at the same time.

If the thread model is multithreaded, this method calls the Win32 API function EnterCriticalSection, which waits until the thread can take ownership of the critical section object obtained through a private data member.

void Lock();

Remarks

When the protected code finishes executing, the thread must call Unlock to release ownership of the critical section.

If the thread model is single-threaded, this method does nothing.

Part of a union that accesses four bytes of memory.

long m_dwRef;

Remarks

With m_pOuterUnknown, part of a union:

union

{

long m_dwRef;

IUnknown* m_pOuterUnknown;

};

If the object is not aggregated, the reference count accessed by AddRef and Release is stored in m_dwRef. If the object is aggregated, the pointer to the outer unknown is stored in m_pOuterUnknown.

Part of a union that accesses four bytes of memory.

IUnknown*
    m_pOuterUnknown;

Remarks

With m_dwRef, part of a union:

union

{

long m_dwRef;

IUnknown* m_pOuterUnknown;

};

If the object is aggregated, the pointer to the outer unknown is stored in m_pOuterUnknown. If the object is not aggregated, the reference count accessed by AddRef and Release is stored in m_dwRef.

For each class listed in the object map, this function is called once when the module is initialized, and again when it is terminated.

static void WINAPI ObjectMain(bool bStarting);

Parameters

bStarting
[out] The value is true if the class is being initialized; otherwise false.

Remarks

The value of the bStarting parameter indicates whether the module is being initialized or terminated. The default implementation of ObjectMain does nothing, but you can override this function in your class to initialize or clean up resources that you want to allocate for the class. Note that ObjectMain is called before any instances of the class are requested.

ObjectMain is called from the entry point of the DLL, so the type of operation that the entry-point function can perform is restricted. For more information on these restrictions, see Run-Time Library Behavior and DllMain.

Example

class ATL_NO_VTABLE CMyApp :
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass<CMyApp, &CLSID_MyApp>,
   public IMyApp
{
public:
   CMyApp()
   {
   }

   static void WINAPI ObjectMain(bool bStarting)
   {
      if (bStarting)
         ;// Perform custom initialization routines
      else
         ;// Perform custom termination routines
   }

   // Remainder of class declaration omitted.

Increments the reference count of the outer unknown of an aggregation.

ULONG OuterAddRef();

Return Value

A value that may be useful for diagnostics and testing.

Retrieves an indirect pointer to the requested interface.

HRESULT OuterQueryInterface(REFIID iid, void** ppvObject);

Parameters

iid
[in] The GUID of the interface being requested.

ppvObject
[out] A pointer to the interface pointer specified in iid, or NULL if the aggregation does not support the interface.

Return Value

One of the standard HRESULT values.

Decrements the reference count of the outer unknown of an aggregation.

ULONG OuterRelease();

Return Value

In non-debug builds, always returns 0. In debug builds, returns a value that may be useful for diagnostics or testing.

If the thread model is multithreaded, this method calls the Win32 API function LeaveCriticalSection, which releases ownership of the critical section object obtained through a private data member.

void Unlock();

Remarks

To obtain ownership, the thread must call Lock. Each call to Lock requires a corresponding call to Unlock to release ownership of the critical section.

If the thread model is single-threaded, this method does nothing.

CComAggObject Class
CComObject Class
CComPolyObject Class
Class Overview

Show: