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

COM from scratch - PART THREE

0.00/5 (No votes)
17 Apr 2004 1  
An article about COM Containment.

Component three from the demo application

Introduction

In part one, some background information regarding to the COM technology was explained, and a COM component was made by a simple example. In part two, the code in the example was optimized, such that the component was no longer bound to its client, and it could be created via COM Library. This part is about the containment mechanism, and I'll guide the reader to reuse the component made in part two in another component.

Part three - COM Containment

COM provides two mechanisms for code reuse. These mechanisms are called containment or delegation and aggregation. In the containment mechanism, one object (the �outer� object) becomes the client of another, internally using the second object (the �inner� object) as a provider of services that the outer object finds useful in its own implementation. COM containment is similar to C++ containment; however, it is at the interface level. The outer component contains pointers to interfaces on the inner component. The outer component implements its own interfaces using the interfaces of the inner component and it can also re-implement the inner component's interfaces by forwarding calls to the inner component and adding code before and after the code for the inner component. Containment reuses the implementation of the interfaces belonging to the inner components. One of the components (CComponent3) from the demo application (illustrated in the above figure), uses this mechanism. Until now, a component has been created and its interface supports the Print method. Now, in order to use the containment mechanism, we can use this interface in a new component. In the following, a new component will be created, which reuses the previous component�s interface in order to build its own interface. This new interface will support two new mathematical functions and will be called IMath interface.

Step 1: Definition of the Component and implementation of its Server

Using the same method in part two, create en empty project in order to make the component's server(Component2.dll). The steps for creation of the DLL are explained in details in part two.

  • Definition of the new interface:

    As you may know, every COM interface should be derived directly or indirectly from the IUnknown interface, so the new interface may be derived form the interface which was implemented in the pervious parts, and that interface was derived directly from the IUnknown interface:

    interface IMath: IComponent; 
    {
      // desired functionality for the new component      
    
    
            
      virtual int  __stdcall Sum(int a,int b)=0;
      virtual int  __stdcall Subtract(int a,int b)=0;
    
    };
  • Definition of the component's class:

    As you may know, the component's class can be defined by deriving a class from the newly created COM interface, and because the containment mechanism will be used, there is a need for a method to create the inner component (which can be called from the class factory), and a pointer to the inner interface:

    class CComponent2:public IMath
    {
    private:
    long m_cRef;// Referance count
    
    IComponent* m_pInnerInterface; 
    // pointer to the interface of the inner component
    
    
    public:
    //IUnknown
    
    virtual HRESULT __stdcall QueryInterface(const IID & iid,void** ppv);
    virtual ULONG __stdcall AddRef();
    virtual ULONG __stdcall Release();
    //IComponent (interface of the inner component)
    
    virtual void __stdcall Print (const char* msg);   
    //IMath
    
    virtual int __stdcall Sum(int a,int b){return(a+b);}
    virtual int __stdcall Subtract(int a,int b){return(a-b);} 
    // A method to create the inner component
    
    HRESULT __stdcall CreateInnerComponenet();
    //Constructor
    
    CComponent2();
    //Destructor
    
    ~CComponent2();
    
    };
  • Implementation of CComponent2::CreateInnerComponent:

    Implementation of CreateInnerComponent

  • Implementation of CFactory::CreateInstance():
    ////////////////////////////////////////////////////////////////////////
    
    HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnkOuter, 
                                               const IID& iid,void** ppv)
    
    {
        // Cannot aggregate
    
        if (pUnkOuter != NULL)
              return CLASS_E_NOAGGREGATION ;
    
        // Create component2(the outer component).
    
             CComponent2* pComponent2 = new CComponent2 ;
    
        if (pComponent2 == NULL)
            return E_OUTOFMEMORY ;
    
        // Create the inner component. 
    
        HRESULT hr = pComponent2->CreateInnerComponent() ;
        if (FAILED(hr))
        {
            pComponent2->Release() ;
            return hr ;
        }
    
        // Get the requested interface.
    
            hr = pComponent2->QueryInterface(iid,(void**) ppv) ;
        //if Query faild the component will delete itself
    
      
        if (FAILED(hr))
           pComponent2->Release() ;
    
      return hr ;   
    }
  • Forwarding calls to the inner component:
    ///////////////////////////////////
    
    void __stdcall CComponent2::Print(const char* msg)
    {
      m_pInnerInterface->Print(msg);
    }
  • Providing GUID for the component's class and interface identifier for IMath interface, using GUIDGEN.EXE:
    //CLSID for component2's class
    
    // {0CDD7D97-DD93-450a-BBCF-6B22894FAFF5}
    
    extern "C" const GUID CLSID_Component2 = 
    { 0xcdd7d97, 0xdd93, 0x450a, 
      { 0xbb, 0xcf, 0x6b, 0x22, 0x89, 0x4f, 0xaf, 0xf5 } };
    
    //IID for component2's IMath interface
    
    // {C8ACE8CC-0480-4f1a-8A62-89717E1D5705}
    
    extern "C" const IID IID_IMath = 
    { 0xc8ace8cc, 0x480, 0x4f1a, 
      { 0x8a, 0x62, 0x89, 0x71, 0x7e, 0x1d, 0x57, 0x5 } };

Step 2: the Client

The Client

The following figure shows the output window of the client program:

The output window

One of the uses of containment is to extend an interface by adding code to an existing interface. As an example, the inner Component2 from demo application has a window with yellow color, although Component2's background color is initially white. The other change is that the butterfly starts flying after creation of the inner component, although initially it does not fly. These changes are made through the containment mechanism. The following piece of code and figure show how one of the methods of Component2 has been specialized.

/////////////////////////////////////////////////////////////////////

void __stdcall CComponent3::ShowWndBackground_Com2(COLORREF bgcolor) 
// default background color is white

{
//--------------------------------------------------------------------------------//

//The outer component (Component3) can reimplement an interface supported

//by the inner component by forwarding calls to the inner component.The outer

//component can specialize the interface by adding code before and after the code

//for the inner component. As an example the background color of component2's window

//will be changed to yellow 

//--------------------------------------------------------------------------------//

    bgcolor=RGB(255,255,0);  //bgcolor is changed to yellow color

//--------------------------------------------------------------------------------//

    m_pIComponent2->StartFlying();//The butterfly should fly 

    m_pIComponent2->ShowWndBackground_Com2(bgcolor);
    
}

Extending an interface by COM Containment

The demo application's code is very similar to the example explained in the article and only a window is used to visualize each component, and each window's menu acts as the component's interface. I hope that these articles were useful for you and could be a guide to begin with "COM" from scratch.

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