Click here to Skip to main content
15,884,353 members
Articles / Programming Languages / C++
Article

COM from scratch - PART THREE

Rate me:
Please Sign up or sign in to vote.
4.73/5 (37 votes)
17 Apr 20043 min read 110.3K   4.2K   94   20
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


Written By
Software Developer
Denmark Denmark
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralLInks are not working Pin
sandeepkavade4-May-08 23:39
sandeepkavade4-May-08 23:39 
GeneralRe: LInks are not working Pin
Aria Ansari5-May-08 7:18
Aria Ansari5-May-08 7:18 
GeneralCOM Interop with .NET Pin
w2tj25-Jul-07 21:31
w2tj25-Jul-07 21:31 
GeneralRe: COM Interop with .NET Pin
Pdaus27-Oct-10 0:50
Pdaus27-Oct-10 0:50 
GeneralworkCom classes Pin
LUV U5-Sep-05 4:44
LUV U5-Sep-05 4:44 
GeneralCoCreateInstance CLASS NOT REGISTERED ERROR Pin
tacbse200110-Jan-05 9:47
tacbse200110-Jan-05 9:47 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
Aria Ansari11-Jan-05 2:56
Aria Ansari11-Jan-05 2:56 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
tacbse200111-Jan-05 3:46
tacbse200111-Jan-05 3:46 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
tacbse200111-Jan-05 5:41
tacbse200111-Jan-05 5:41 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
Aria Ansari12-Jan-05 11:50
Aria Ansari12-Jan-05 11:50 
Hi,

If your persistent component inherits from IPersistStream , then
the implementation of IPersist’s method (GetClassID) and IPersistStream’ methods (load,save,GetSizeMax,IsDirty) are required. Because IPersistStream interface is derived from IPersist.You may choose a GUID for your class for example:

-----------------------------------------------------------------------------
// {49BF12F2-5041-48da-9B44-AA2FAA63AEFB}
const GUID CLSID_PersistComponent =
{ 0x49bf12f2, 0x5041, 0x48da, { 0x9b, 0x44, 0xaa, 0x2f, 0xaa, 0x63, 0xae, 0xfb } };

--------------------------------------------------------------------------

For persistants objects COM requires the component’s class id in order to associate it with persistent data and this can be achieved by implementation of the GetClassID method:

///////////////////////////////////////////////////////////
HRESULT YourPersistComponent::GetClassID(CLSID *pClassID) {
*pClassID = CLSID_PersistComponent;
return S_OK;
}

and for creation of an instance of your persistent component you can pass the component's clsid to the CoCreateInstance method:

CoCreateInstance(CLSID_PersistComponent,NULL,CLSCTX_ALL,IID_IPersistStream,(LPVOID*)&pPersistStream);

and it's also required that your component is registered in windows registrey if the client is going to use it via COM library.
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
tacbse200113-Jan-05 5:20
tacbse200113-Jan-05 5:20 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
Aria Ansari14-Jan-05 8:27
Aria Ansari14-Jan-05 8:27 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
Anonymous14-Jan-05 5:39
Anonymous14-Jan-05 5:39 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
tacbse200114-Jan-05 5:40
tacbse200114-Jan-05 5:40 
GeneralRe: CoCreateInstance CLASS NOT REGISTERED ERROR Pin
Aria Ansari14-Jan-05 9:18
Aria Ansari14-Jan-05 9:18 
GeneralSmall error Pin
Kza Wah27-Dec-04 23:03
Kza Wah27-Dec-04 23:03 
GeneralRe: Small error Pin
Aria Ansari28-Dec-04 3:51
Aria Ansari28-Dec-04 3:51 
QuestionHow to avoid shipping the interface.h file Pin
tpndtbk17-Nov-04 22:48
tpndtbk17-Nov-04 22:48 
AnswerRe: How to avoid shipping the interface.h file Pin
Aria Ansari19-Nov-04 3:02
Aria Ansari19-Nov-04 3:02 
GeneralQuestions concerning vector of string for IDL file Pin
tpndtbk17-Nov-04 22:31
tpndtbk17-Nov-04 22:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.