|
Reusability In COM-II (Aggregation) |
|
|
|
One way to reuse a component within another component is using containment. Another way to do so is using aggregation. In containment the outer component makes use of the inner component, but does not directly expose the inner component to its clients. The outer component can choose to use the inner component to implement some of its functionality. If the outer component decides to implement one or more of the interfaces of the inner component it must provide a wrapper for every method in the exposed interfaces. The method wrappers can simply call the inner component’s methods directly, or they can perform additional tasks such as validating parameters, recording usage, or otherwise extending the inner component’s interface. Sometimes, it’s more convenient to expose the interfaces of the inner object directly, without writing a collection of wrappers. This is aggregation. A client creates the outer object, and when it asks QueryInterface( ) for an interface supported by the inner object, it gets passed a pointer to the inner object’s interface. When we use containment or aggregation the client does not know that there are actually two separate components acting in unison. We may as well have more than two objects. The outer component can aggregate several different components. Similarly, the inner component can aggregate another component into itself. Aggregation has one distinct advantage over containment: it requires much less knowledge of the inner component on the part of the person implementing the outer component. Also, bypassing the wrapper layer makes the component that bit more efficient. The disadvantages of aggregation are that the inner (aggregated) object must be specially written so that it can be aggregated, and that the inner object can only be implemented in a DLL that is loaded directly into the same apartment as the outer object. There is no cross-apartment, cross-process or cross-machine aggregation. Let us now understand aggregation with the help of some concrete example. Suppose we have a component that knows how to swap mouse buttons and set the time interval between two clicks of the mouse button. We can develop another component which uses this existing component and additionally also manage mouse trails. The existing component becomes the inner component. Given below is the procedure for building these components and a client that accesses the functionality of both the components. Creating The Inner Component The process of creating components in ATL consists of three steps: (a) Creating Module: To create a module the Developer Studio provides an ATL COM AppWizard. Carry out the following steps:
(b) Adding Component To The Module To add component to the module we can use ‘ATL Object Wizard’. Carry out the following steps for adding a component using this wizard:
(c) Adding Methods To The Component The component that has been added does not contain any functionality. To provide functionality we should add two methods named SwapButton( ) and SetDoubleclicktime( ) as indicated below.
STDMETHODIMP
CMouse::SwapButton(
) // TODO: Add your implementation code here return S_OK ; }
mousestatus = !mousestatus ; SystemParametersInfo ( SPI_SETMOUSEBUTTONSWAP, mousestatus,NULL,NULL ) ;
STDMETHODIMP CMouse::SetDoubleclicktime ( long time ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( ) ) // TODO: Add your implementation code here SystemParametersInfo ( SPI_SETDOUBLECLICKTIME, time, NULL, NULL ) ; return S_OK ; } Creating The Outer Component The procedure for creating the outer component is same as that for creating the inner component. First create a module called ‘Outer’. Insert a component called ‘MouseTrails’ in it and then add a method called SetMouseTrails( ) to it. This methods is shown below: STDMETHODIMP CMouseTrails::SetMouseTrails ( int trails ) { AFX_MANAGE_STATE ( AfxGetStaticModuleState( ) ) // TODO: Add your implementation code here SystemParametersInfo ( SPI_SETMOUSETRAILS, trails, NULL, NULL ) ; return S_OK ; }
# include "..\inner\inner.h"
CComPtr<IUnknown> m_agg ; This variable would be used to hold the IUnknown pointer that we are going to get back from the aggregate when we create it.
DECLARE_GET_CONTROLLING_UNKNOWN( )
HRESULT FinalConstruct( ) {
} This function creates an instance of the inner object when the outer object first gets initialized. Here we are using CoCreateInstance( ) to create the instance of the CMouse coclass. The second parameter to CoCreateInstance( ) is the outer unknown of CMouseTrails object. We do not know that the current instance of the CMouse coclass is the controlling object, or whether it too has been aggregated. Hence we are using GetControllingUnknown( ) which will either return the controlling outer IUnknown if it is aggregated, or its own IUnknown if it is not. The fourth parameter is the interface we are asking for, which must be IUnknown when we are creating an object as part of an aggregate.
HRESULT FinalRelease(
)
COM_INTERFACE_ENTRY_AGGREGATE( IID_IMouse, m_agg.p ) This macro takes an interface identifier for the interface that we are exposing and an IUnknown * for the inner object that implements the interface. The macro itself hides the details that handle the implementation of QueryInterface( ).
library
OUTERLib Creating A Client The steps to create a COM Client using MFC are as under:
#
import
"..\Outer\Outer.tlb"
void CClientDlg::OnSwap( ) void CClientDlg::OnMouseTrails( )
void CClientDlg::OnDoubleClick( )
BOOL CContClientApp::InitInstance(
) |