COM Aggregation   «Prev  Next»
Lesson 8 Aggregation guidelines: outer objects
Objective List guidelines for aggregating outer objects.

Aggregation Guidelines and Outer Objects

Synchronization

To aggregate other COM objects, an outer COM object must adhere to the following guidelines:
  1. The outer COM object must create an instance of each inner/aggregated COM object. Normally, as part of its initialization sequence, the outer object creates instances of the COM objects it aggregates.
  2. The outer object must release the inner object's nondelegating IUnknown when it is released. This guideline tells us to release the inner COM object when the outer COM object is released.

Guidelines 1 and 2 tell the outer object to keep the aggregated objects in synch with its own state. When the outer object is created, it creates and aggregates the inner objects. When the outer object terminates, it releases the inner objects. The outer object's implementation of IUnknown::QueryInterface must be able to use the inner object's nondelegating IUnknown to obtain interface pointers into the inner object to provide them to the client.

Interface Pointers

This guideline states that the outer object uses the nondelegating IUnknown interfaces of each aggregated object. When a client asks the outer COM object for an interface it does not support, the outer object can query each aggregated object for the requested interface by calling QueryInterface in the nondelegating IUnknown.
Outer objects can handle interface navigation into inner objects in two ways:

I. Explicitly: Supporting explicit interface navigation into Aggregated Objects

Explicit aggregation has the outer object coded to look for known interfaces implemented within aggregated objects.
The following pseudo-code makes the following assumptions:
  1. The outer object has aggregated two inner objects: IO1 and IO2.
  2. IO1 implements interface IX1; IO2 implements interface IX2.
  3. The outer object implements interface IX0.
  4. The outer object has nondelegating IUnknown interface pointers into each aggregated object in member variables m_pIUnknown_IO1 and m_pIUnknown_IO2.

class COuterCOMObj : public IX0 {
 IUnknown *m_pIUnknown_IO1;
 /* Assume this holds the non-delegating IUnknown for IO1 */
 IUnknown *m_pIUnknown_IO2;
 /* Assume this holds the non-delegating IUnknown for IO2 */
 HRESULT QueryInterface(REFIID riid, VOID **ppv) {
   /* See if the caller is asking for interface IX0 which is 
   implemented in the outer object */
   if (riid == IID_IX0) { 
     *ppv = (IX0 *) this;
   }
   /* See if the caller wants IX1 which is implemented 
   in aggregated object IO1 */
   else if (riid == IID_IX1) {
     //If so - call the non-delegating IUnknown in aggregated object IO1
      return m_pIUnknown_IO1->QueryInterface(riid, ppv);
    }
    //See if the caller wants IX2 which is implemented in aggregated object IO2
    else if (riid == IID_IX2) {
      //If so - call the non-delegating IUnknown in aggregated object IO2
      return m_pIUnknown_IO2->QueryInterface(riid, ppv);
    }
    else 
      return E_NOINTERFACE;      
     ((*IUnknown ) ppv)->AddRef();
     return S_OK;
  }
  ...
};

II. Blind Aggregation in COM

Blind aggregation means the outer object does not have code that looks for specific interfaces implemented in aggregated objects. Instead, if an unknown interface ID (IID) is passed into QueryInterface, the outer object calls into the nondelegating IUnknown of each aggregated object to see if the interface is supported. The following pseudo-code makes the following assumptions:
  1. The outer object has aggregated two inner objects: IO1 and IO2.
  2. IO1 implements interface IX1; IO2 implements interface IX2.
  3. The outer object implements interface IX0.
  4. The outer object has nondelegating IUnknown interface pointers into each aggregated object in member variables m_pIUnknown_IO1 and m_pIUnknown_IO2.

class COuterCOMObj : public IX0 {
   IUnknown *m_pIUnknown_IO1;
   IUnknown *m_pIUnknown_IO2;
   HRESULT hr = E_NOINTERFACE;

   HRESULT QueryInterface(REFIID riid, VOID **ppv) {
   /*See if the caller is asking for interface IX0  which is implemented in the outer object*/
   if (riid == IID_IX0) { 
     *ppv = (IX0 *) this;
     hr = S_OK;
   }
   else {
     /* If we don't recognize the IID, check with each aggregated object to see if 
     it is supported */
     hr = m_pIUnknown_IO1->QueryInterface(riid, ppv);
     if (SUCCEEDED(hr)) return hr;
     hr = m_pIUnknown_IO2->QueryInterface(riid, ppv);
     if (SUCCEEDED(hr)) 
       return hr;
     }
     if (FAILED(hr))
       return hr;
     ((IUnknown *) ppv)->AddRef();
     return S_OK;
   }
   ...
};

Fix Bug Outer-Com Object - Exercise

Click the Exercise link below to apply what you have learned about aggregated outer COM objects.
Fix Bug Outer-Com Object - Exercise