Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

COM Concept : Containment

0.00/5 (No votes)
12 Nov 2003 1  
COM Concept : Containment

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.

// Code Snippet for CMath class.


  class CMath : public IMath {
    public:
  // Implementing IUnknown Interface.


    virtual HRESULT __stdcall QueryInterface(const IID& iid,void **ppv);
    virtual ULONG __stdcall AddRef();
    virtual ULONG __stdcall Release();

  //Implementing IMath Interface.

    virtual void _STDCALL SumSquare(int Val1,int Val2,int* pResult);

  // Constructor

    CMath();

  // Destructor

    ~CMath();

  // Pointer to ISquare interface on the inner component.


    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.

  // Code Snippet for DllGetClassObject.


  // COM SCM creates a class object only when the request has come for 

  // CLSID_CMath and CLSID_CSquare. After creating the   

 // class object, the IClassFactory interface pointer on the 

 // class object is returned back to the client.


  STDAPI DllGetClassObject(const CLSID & clsid,const IID& iid,void **ppv)
   {
     // This server supports two COM Components and hence 

     // validation is performed.


     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.

  //Code Snippet for CreateInstance of Class Object.


  //This snippet shows the part of the code which is 

  //executed during the creation of an outer

  //component.


  //This is executed when the client calls the CreateInstance, 

  //after getting the IClassFactory

  //interface pointer on CMath's instance.

  //The client gets the IClassFactory interface pointer by

  //calling CoGetClassObject.


  if ((iid == IID_IMath) || (iid == IID_IUnknown)) {
      CMath* pMath = new CMath();
      if(pMath == NULL) {
          return E_OUTOFMEMORY;
      }
      // Here, the Outer Component initializes the inner component. 

      // The CoCreateInstance is called by

      // the outer component during its creation and 

      // it queries for the ISquare interface on the inner 

      // component, and if the calls succeeds it stores the pointer 

      // in its variable m_pISqaure.


    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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here