3/30/09

Reusability In COM

COM promotes language-independent reuse. But so far we�ve only looked at clients using COM objects. Once the COM component is built it can be used by a client written in any language: VC, VB, Java, etc. Even scripting languages can use the component. But that�s only part of the reuse story. At times we may want to reuse an existing component while building another component. This way we can take advantage of the existing features of a component, and add to its functionality without being required to recompile the existing component. COM doesn�t support inheritance in the same sense as C++ does. This means we can�t derive one COM component from another the way we derive one C++ class from another.

COM offers two primary mechanisms for reusing existing components to build other components. These are containment and aggregation. In this episode we would look at the first of these mechanisms, i.e., containment.

In containment we can have one component as a member variable of another. The outer component makes use of the inner component, but does not directly expose the inner component to its clients. This is shown in the following figure.

Figure 1.

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 inner component�s exposed interfaces. These wrapper methods can either call the inner component�s methods directly, or they can perform some additional tasks thereby extending the inner component�s interface.

The outer component manages the lifetime of the inner component. It typically creates the inner component with CoCreateInstance( ) when it initializes itself (in the FinalConstruct( ) function), and releases the inner component�s interface pointer when it uninitializes itself (FinalRelease( )).

Let us now understand containment 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 the inner component through the interface provided by the outer component.

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:

  • Select �New� from the �File� menu.

  • Select �ATL COM AppWizard� as the project. Type �Inner� as the project name and click �OK� to continue.

  • Select type of module as �Dynamic Link Library�, click �Finish�.

(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:

  • Select �Insert | New ATL Object� menu item. This would display the �ATL Object Wizard�

  • Select �Simple Object� from the various object categories and click on �Next�.

  • A �ATL Object Wizard Properties� dialog is displayed.

  • Enter the �Short Name� as �Mouse�. As soon as you do this all other edit controls would be filled automatically. Click on OK.

(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.

  • Switch to class view tab. Select the interface �IMouse� and click the right mouse button. From the menu that pops up, select �Add Method�.

  • In the �Add Method to Interface� dialog specify the method name as �SwapButton� and leave the parameters edit control empty.

  • Click on �OK�.

  • Adding the SwapButton( ) method creates a function definition in �Mouse.cpp� as shown below:

STDMETHODIMP CMouse::SwapButton( )

{

// TODO: Add your implementation code here return S_OK ;

}

  • Add the following code to the SwapButton( ) method by double clicking on this method from the Class view tab:

mousestatus = !mousestatus ;

SystemParametersInfo( SPI_SETMOUSEBUTTONSWAP, mousestatus,NULL,NULL) ;

  • From the class view tab add a private variable mousestatus of type BOOL to the CMouse class. Initialize this variable to FALSE in the constructor of CMouse class.

  • On similar lines add another method called SetDoubleclicktime( ). Its code is given below:

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, except for a few minor variations. First create a module called �Outer�. Insert a component called �MouseTrails� in it and then add three methods called SwapButton( ), SetDoubleclicktime( ) and SetMouseTrails( ) to it.

These methods are shown below:

STDMETHODIMP CMouseTrails::SwapButton()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())

inner -> SwapButton() ;

//TODO: Add your implementation code here

return S_OK;

}

STDMETHODIMP CMouseTrails::Setdoubleclicktime(long time)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState( ) )

// TODO: Add your implementation code here

inner -> SetDoubleclicktime ( time ) ;

return S_OK ;

}

STDMETHODIMP CMouseTrails::SetMouseTrails ( int trails )

{

AFX_MANAGE_STATE(AfxGetStaticModuleState( ) )

// TODO: Add your implementation code here

SystemParametersInfo ( SPI_SETMOUSETRAILS, trails,NULL,NULL ) ;

return S_OK ;

}

  • From the class view tab add a private variable inner of type IMouse * to the CMouseTrails class.

  • In the �stdafx.h� file of the outer component add the following statements:

#import "..\inner\inner.tlb"

using namespace INNERLib ;

  • Add the following two function to the �MouseTrails.h� file.

HRESULT FinalConstruct( )

{

HRESULT hr ;

CLSID clsid ;

CoInitialize ( NULL ) ;

hr = CLSIDFromProgID ( OLESTR ( "inner.Mouse" ), &clsid ) ;

if ( FAILED ( hr ) )

::MessageBox ( 0, "", "CLSIDFromProgID failed", 0 ) ;

hr = CoCreateInstance ( clsid, NULL, CLSCTX_INPROC,

__uuidof ( IMouse ), ( void ** ) &inner ) ;

if ( FAILED ( hr ) )

::MessageBox ( 0, "", "CoCreate failed",0) ;

return S_OK ;

}

HRESULT FinalRelease( )

{

inner->Release( ) ;

}

Creating A Client

The steps to create a COM Client using MFC are as under:

  • Create a dialog-based project using AppWizard (EXE).

  • Add two edit boxes and three buttons to the dialog as shown in Figure 2.


Figure 2.

  • Add two variables m_trails and m_dctime of type integer for the two edit controls in the dialog box.

  • Import the server�s Type Library into the client. This is done by adding the following two statement to the file �StdAfx.h�.

# import "..\Outer\Outer.tlb"

using namespace OUTERLib ;

// Add the regulation code in the OnInitDialog( ) function as shown below:

BOOL CContClientDlg::OnInitDialog( )

{

// AppWizard generated code

// TODO: Add extra initialization here

CLSID c ;

HRESULT h ;

CoInitialize ( NULL ) ;

h = CLSIDFromProgID ( OLESTR ( "Outer.MouseTrails" ), &c ) ;

if ( FAILED ( h ) )

MessageBox ( "CLSIDFromProgID Failed" ) ;

h = CoCreateInstance ( c, NULL, CLSCTX_ALL,

__uuidof ( IMouseTrails ), ( void ** ) &outer ) ;

if ( FAILED ( h ) )

MessageBox ( " Not able to create instance of an object ") ;

return TRUE; // return TRUE unless you set the focus to a control

}

  • From the class view tab add a private variable outer of type IMouseTrails * to the CClientDlg class.

  • Use the COM Object. Now that we have obtained the interface pointer, the client application can call the methods of the COM server object as shown below:

void CClientDlg::OnSwap( )

{

// TODO: Add your control notification handler code here

outer -> SwapButton( ) ;

}

void CClientDlg::OnMouseTrails( )

{

// TODO: Add your control notification handler code here

UpdateData ( TRUE ) ;

outer -> SetMouseTrails ( m_trails ) ;

}

void CClientDlg::OnDoubleClick( )

{

// TODO: Add your control notification handler code here

UpdateData ( TRUE ) ;

outer -> Setdoubleclicktime ( m_dctime ) ;

}

  • Uninitialize the COM library by the function CoUninitialize( ) at the end of InitInstance( ) function as shown below:

BOOL CContClientApp::InitInstance( )

{

// wizard generate code

CoUninitialize( ) ;

return FALSE;

}

With that we are through with the creation of the client. Now you can compile and execute the client and check out whether it is able to interact with the methods in the outer component.

No comments: