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:
- 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.
- 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:
- The outer object has aggregated two inner objects:
IO1
and IO2
.
IO1
implements interface IX1
; IO2
implements interface IX2
.
- The outer object implements interface
IX0
.
- 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:
- The outer object has aggregated two inner objects:
IO1
and IO2
.
IO1
implements interface IX1
; IO2
implements interface IX2
.
- The outer object implements interface
IX0
.
- 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