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;
{
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;
IComponent* m_pInnerInterface;
public:
virtual HRESULT __stdcall QueryInterface(const IID & iid,void** ppv);
virtual ULONG __stdcall AddRef();
virtual ULONG __stdcall Release();
virtual void __stdcall Print (const char* msg);
virtual int __stdcall Sum(int a,int b){return(a+b);}
virtual int __stdcall Subtract(int a,int b){return(a-b);}
HRESULT __stdcall CreateInnerComponenet();
CComponent2();
~CComponent2();
};
- Implementation of
CComponent2::CreateInnerComponent
:
- Implementation of
CFactory::CreateInstance()
:
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnkOuter,
const IID& iid,void** ppv)
{
if (pUnkOuter != NULL)
return CLASS_E_NOAGGREGATION ;
CComponent2* pComponent2 = new CComponent2 ;
if (pComponent2 == NULL)
return E_OUTOFMEMORY ;
HRESULT hr = pComponent2->CreateInnerComponent() ;
if (FAILED(hr))
{
pComponent2->Release() ;
return hr ;
}
hr = pComponent2->QueryInterface(iid,(void**) ppv) ;
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:
extern "C" const GUID CLSID_Component2 =
{ 0xcdd7d97, 0xdd93, 0x450a,
{ 0xbb, 0xcf, 0x6b, 0x22, 0x89, 0x4f, 0xaf, 0xf5 } };
extern "C" const IID IID_IMath =
{ 0xc8ace8cc, 0x480, 0x4f1a,
{ 0x8a, 0x62, 0x89, 0x71, 0x7e, 0x1d, 0x57, 0x5 } };
Step 2: the Client
The following figure shows the output window of the client program:
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)
{
bgcolor=RGB(255,255,0);
m_pIComponent2->StartFlying();
m_pIComponent2->ShowWndBackground_Com2(bgcolor);
}
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.