Introduction
Reusability is the mantra for the success of any technology in the field of programming. C++ supports the reusability by providing the concept of inheritance. Inheritance is the way by which the subclass inherits the functionality from its base class (parent class). The subclass has the option to modify the functionality, which has been provided by the base class (overriding), or to continue with the same functionality as that of the base class. Inheritance has made the life of the programmer�s easier, but this has a problem associated with it. Implementation inheritance, which is supported by C++, creates a strong bonding or the contract between the base class and a subclass. Any changes in the base class can cause a drastic affect on the child class and may cause the clients of the child class to break. Implementation Inheritance is not an appropriate technology to create a reusable components, which could be used anywhere, at anytime, by anybody without worrying about the internal implementation of the component.
COM doesn�t supports the Implementation inheritance as it violates the motto of the COM technology i.e. to create the reusable components. COM does supports the Interface inheritance, in which the subclass inherits the interface of the base class and not the implementation. Interface inheritance protects the clients of a component from change. Implementation inheritance can be simulated in COM by using the concept of component containment. In COM, the reusability is achieved by using containment and aggregation. This article will be covering the Containment technique, in which the outer component uses the inner component, in detail. The Aggregation will be covered in a separate article
Containment
Everything in COM is related with the interfaces. Containment is also implemented at the interface level. The COM containment is same as the C++ containment, in which the outer component is a client of an inner component. The outer component has pointers to interfaces on the inner component. The inner component is not exposed directly to the client and hence only the IUnknown of the outer component will be exposed to the client. In Containment, the outer component forwards (delegates) the calls to the inner component.
There could be two scenarios in which the containment can be implemented. The first case is the outer component implements its own interfaces and uses the interfaces of the inner component. The second case could be that the outer component reimplements an interface supported by the inner component and forward the call to the interface of the inner component.
In Containment, the outer component is acting as a client and using the interface of the inner component. In implementing the containment, the inner component and client are unaware of the fact that they are being the part of the containment implementation. The outer component has to be modified to support the containment.
Sample code - explanation
This article will explore the first scenario to explain the containment technique. In this sample code, the outer component utilizes the functionality, which is being provided by an inner component. The outer component needs some modification to accommodate the inner component as a contained object. The client and an inner component won�t be affected and will be unaware of the fact that they are taking part in the containment implementation. This sample code will demonstrate that the client is unfamiliar with the fact that the outer component is using the services of an inner component.
The outer component i.e. CMath
will have a new member variable m_pISqaure
, which is a pointer to ISquare
interface on the inner component.
class CMath : public IMath {
public:
virtual HRESULT __stdcall QueryInterface(const IID& iid,void **ppv);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
virtual void _STDCALL SumSquare(int Val1,int Val2,int* pResult);
CMath();
~CMath();
ISquare* m_pISquare;
private:
long m_cRef;
};
As this COM Server (ContainmentSample.dll) supports two COM Components i.e. CMath
and CSquare
, therefore DllGetClassObject
should have a validation for these two ClassIDs.
STDAPI DllGetClassObject(const CLSID & clsid,const IID& iid,void **ppv)
{
if((clsid == CLSID_CMath) || (clsid == CLSID_CSquare)) {
cout<<"The requested component is supported by "
"COM server (ContainmentSample.dll)" << endl;
}
else
{
return CLASS_E_CLASSNOTAVAILABLE;
}
CFactory *pFactory = new CFactory();
if (pFactory == NULL) {
return E_OUTOFMEMORY;
}
HRESULT hResult = pFactory->QueryInterface(iid,ppv);
static_cast<< IUnknown* >>(pFactory)->Release();
return hResult;
}
The CreateInstance
for the outer component has to be modified to accommodate the creation of an inner component and storing the ISquare
interface on the inner component in its member variable i.e. m_pISqaure
. The outer component calls the CoCreateInstance
with the CLSID parameter as CLSID_CSquare
and queries for ISquare
interface on the inner component (CSquare
), and if the call is succeeds it stores the interface pointer in m_pISqaure
.
if ((iid == IID_IMath) || (iid == IID_IUnknown)) {
CMath* pMath = new CMath();
if(pMath == NULL) {
return E_OUTOFMEMORY;
}
cout<<"Call to Create the Inner Component" << endl;
hResult = CoCreateInstance(CLSID_CSquare,NULL,CLSCTX_INPROC_SERVER,
IID_ISquare,(void**)&pMath->m_pISquare);
cout<<"CoCreateInstance for CSquare has been called"<< endl;
hResult = pMath->QueryInterface(iid,ppv);
if(SUCCEEDED(hResult)) {
pMath->Release();
}
}
Before executing the client application, the COM Server (ContainmentSample.dll) needs to be registered by regsvr32 utility. The next article will cover the aggregation technique, which is a specialized case of the containment.