Sunday, August 24, 2008

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:

  • 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. 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 the file ‘inner.h’ in ‘mousetrail.h’ as shown below:

# include "..\inner\inner.h"

  • Add a private member variable called m_agg in the outer class from the class view tab:

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

  • Add the following macro to the file ‘MouseTrails.h’

DECLARE_GET_CONTROLLING_UNKNOWN( )

  • Add the following 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, GetControllingUnknown( ), CLSCTX_INPROC, IID_IUnknown, ( void ** ) &m_agg ) ;

if ( FAILED ( hr ) )

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

return S_OK ;

}

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.

  • Add the following function to the ‘MouseTrails.h’ file.

HRESULT FinalRelease( )
{
inner->Release( ) ;
}

  • Add a COM_INTERFACE_ENTRY_AGGREGATE( ) macro for the IMouse interface to the com map.

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

  • Update the file outer.idl file as shown below:

library OUTERLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
importlib("..\inner\inner.tlb");
[
uuid(CB45BCB0-8A4A-11BD-A251-E0EAC39E8615),
helpstring("MouseTrails Class")
]
coclass MouseTrails
{
[default] interface IMouseTrails;
interface IMouse ;
};
};

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

Figure 1.

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

  • Import the Type Libraries into the client. This is done by adding the following statements to the file ‘StdAfx.h’.

# import "..\Outer\Outer.tlb"
using namespace OUTERLib ;
# import "..\Inner\Inner.tlb"
using namespace INNERLib ;

  • 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 ") ;

hr = outer -> QueryInterface ( __uuidof ( IMouse ), (void **) &inner );
if ( FAILED ( hr ) )

MessageBox ( "Unable to query the inner interface" ) ;

inner -> SwapButtons = FALSE ;

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

}

  • From the class view tab add private variables outer, inner of types IMouseTrails * and IMouse * respectively 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 he
inner -> SwapButton( ) ;
}

void CClientDlg::OnMouseTrails( )
{
// TODO: Add your control notification handler code here
UpdateData ( TRUE ) ;
inner -> 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;
}

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.

Multiple Controls

This Article is used to bulid a full fledged ATL COM explorer Control. This article is divided into six steps.

Step 1: Creating the Project
Step 2: Adding a Control to Project
Step 3. Changing Control's Drawing Code
Step 4: Adding a Methods to Control
Step 5: Adding an Event to the Control
Step 6: Adding a Property Page to Your Control

Creating the project :

First we have to create the initial ATL project using the ATL COM AppWizard. click New on the File menu, then choose the Projects tab. Select the ATL COM AppWizard. Type 'MultiControls' as the project name. In step2 check 'Support MFC' check box and click on Finish.

Adding a Control :

To add an object to an ATL project, you use the ATL Object Wizard. Click New ATL Object on the Insert menu, and the ATL Object Wizard appears. In the first ATL Object Wizard dialog box, select the category of object you want to add to your current ATL project. select the category as Controls on the left, then on the right select Full Control. Finally, click Next. A set of property pages is displayed that allows you to configure the control you are inserting into your project. Type "Explorer" as the short name. The other fields are automatically completed. Now check support for rich error information and connection points for your control. On the Attributes tab,Click the Support Connection Points check box. This will create support for an outgoing interface in the IDL file. Click on Miscellaneous tab, from Add Controls combo box select 'SysTreeView32' and in the others division check 'insertable' check button.You are finished selecting options for your control. Click OK. When you created your control, several code changes and additions were made. Changing Control's Code : Since we wish to display 'treectrl' (to display directory tree structure) on the left side of the control and 'listctrl' (to display the files present in the selected directory from the treectrl)on the right side. For initializing listctrl add the following code at the constructor m_ctllist(_T("SysListView32"),this,2) where m_ctlist is the member variable that we have added. we have added code in OnCreate( ) to cretae the listctrl manually.

m_ctllist.Create(m_hWnd, rc, NULL, WS_BORDER|WS_CHILD|WS_VISIBLE|LVS_SMALLICON|LVS_SHOWSELALWAYS ) ;
We have added OnSize( ) handler to resize both the controls. Important thing is we must add the following line
ALT_MSG_MAP(2)
between
BEGIN_MSG_MAP ( CExplorer )
END_MSG_MAP( )

Adding properties to the control : A property 'format' is added to the controls interface,to change the formats of the files to be displayed at runtime.

Adding Events to the control :

Two event GetPathFromTree( ) and GetPathFromList( ) are added to the control . For knowing how to adding Events in ATL COM refer to article ' Connection Points' in ATLCOM section of this website. GetPathFromTree( ) event is fired from onselchanged( ) handler to get the full directory path when we select a directory from the treectrl. GetPathFromList( ) is fired from onitemchanged( ) handler to get the full path of the filename that was selected from the listctrl.

Adding a property pages to the control :

Property pages are implemented as separate COM objects, which allow property pages to be shared if required. To add a property page to the control we can use the ATL Object Wizard.Start the ATL Object Wizard and select Controls as the category on the left. Select Property Page on the right, then click Next.we again get the dialog box allowing to enter the name of the new object. Call the object 'PropPage' and enter that name in the Short Name edit box. Notice that the Interface edit box is grayed out. This is because a property page doesn't need a custom interface. Click on the Strings tab to set the title of the property page. The title of the property page is the string that appears in the tab for that page. Type '&Format' as the title. Click OK and the property page object will be created. A dialog box template is added to the project resource for the property page. Now add the fields that we want to appear on the property page and write the appropriate code that we required in Apply( ) function . At Last Build the control,and test it usinig Activex Text Container.




Client

This program uses 'Explorer Control. Creation of these controls has been discussed in the previous article of the section 'ATL COM' in this same site. We have created the a dialog-based project called "ExplorerDemo'' using MFC AppWizard(exe). To use the ActiveX controls they have to be registered with the Windows registry. If you want to register these controls on your machine you will have to download "Explorer' control . Once registered these controls can be inserted in the dialog box. using the following procedure:

From the 'Project' menu select 'Add To Project' then select 'Components and Controls'. A dialog box will be popped up. Open 'Registered ActiveX Controls' folder. Search for the control to be inserted.(in our case 'Explorer' control). Double click on both the controls to insert them in the project. The bitmap of these controls would now be visible in the 'Controls' toolbar of the dialog editor. Pick it and paste on the dialog box. You can see the preview of both in the dialog box. The dialog that we created is shown in Figure . Run the application to see the effect. Combo box is added to the dialog for changing the files format at runtime. A handler OnSelchangeCombo1( ) is added to the dialog class which get called whenever we have changed the contents in the combo box. Inside this handler a call to SetFormat( ) will update the control present on the the dialog with appropriate File Format. Using ClassWizard a handler corresponding to the event GetPathFromList( ) is added and a inside the handler appropriate code is added such that when ever we clicked on the files present in the list control the file name with full path is displayed in an edit box.


Calendar ActiveX Control Using ATL

This Article is used to build a full fledged ATL COM calendar Control. This article is divided into six steps.

Step 1: Creating the Project

Step 2: Adding a Control to Project

Step 3. Adding a Methods to Control

Step 4: Changing Control's Drawing Code

Step 5: Adding an Event to the Control

Step 6: Adding a Property Page to Your Control

Creating the project

First we have to create the initial ATL project using the ATL COM AppWizard. Click New on the File menu, then choose the Projects tab.Select the ATL COM AppWizard. Type 'CalendarServer' as the project name. In step2 check 'Support MFC' check box and click on Finish.

Adding a Control

To add an object to an ATL project, you use the ATL Object Wizard. Click New ATL Object on the Insert menu, and the ATL Object Wizard appears. In the first ATL Object Wizard dialog box, select the category of object you want to add to your current ATL project. select the category as Controls on the left, then on the right select Full Control. Finally, click Next. A set of property pages is displayed that allows you to configure the control you are inserting into your project. Type "Calendar" as the short name. The other fields are automatically completed. Now check support for rich error information and connection points for your control: On the Attributes tab,Click the Support Connection Points check box. This will create support for an outgoing interface in the IDL file.Click on the Stock Properties tab. You see a list box with all the possible stock properties you can enter. Scroll down the list, then double-click Fill Color to move it to the Supported list.You are finished selecting options for your control. Click OK. When you created your control, several code changes and additions were made.

Adding Methods to the control

Add to Methods GetDate( ), and SetDate( ) to ICalendar interface and add the appropriate code to these methods.For knowing how to add methods in ATL COM refer to article 'Building Client and Servers' in ATLCOM section of this website.

Changing Control's Drawing Code

By Default OnDraw( ) handler is present in the CCalendar class. In this handler code is added to draw calendar.

Adding Events to the control

Add an event NewDay( ) to the control .For knowing how to adding Events in ATL COM refer to article ' Connection Points' in ATLCOM section of this website and event is fired from OnLButtonDown( ) handler.

Adding a property pages to the control

Property pages are implemented as separate COM objects, which allow property pages to be shared if required. To add a property page to the control we can use the ATL Object Wizard. Start the ATL Object Wizard and select Controls as the category on the left. Select Property Page on the right, then click Next.we again get the dialog box allowing to enter the name of the new object. Call the object PropPage and enter that name in the Short Name edit box. Notice that the Interface edit box is grayed out. This is because a property page doesn't need a custom interface. Click on the Strings tab to set the title of the property page. The title of the property page is the string that appears in the tab for that page. Type '&Date' as the title. Click OK and the property page object will be created. A dialog box template is added to the project resource for the property page.Now add the fields that we want to appear on the property page. and write the appropriate code that we required in Apply( ) function . At last Build the control,and test it usinig ActiveX Text Container.



Client

This program uses 'Calendar' Control. Creation of these controls has been discussed in the previous articles of the section 'ATL COM' in this same site. We have created the a dialog-based project called "CalendarClient'' using MFC AppWizard(exe). To use the ActiveX controls they have to be registered with the Windows registry. If you want to register these controls on your machine you will have to download "CalendarServer' control . Once registered these controls can be inserted in the dialog box. using the following procedure: From the 'Project' menu select 'Add To Project' then select 'Components and Controls'. A dialog box will be popped up. Open 'Registered ActiveX Controls' folder. Search for the control to be inserted; in our case 'Calendar' control Double click on both the controls to insert them in the project. The bitmaps of these controls would now be visible in the 'Controls' toolbar of the dialog editor. Pick it and paste on the dialog box. You can see the preview of both in the dialog box. The dialog that we created is shown in Figure. Run the application to see the effect. Two combo boxes are added to the dialog for months and years.a call to Initmonths( ) and Inityears( ) functions in OnInitDialog( ) handler fills the two comboboxe with months and years respectively.a handler OnCombosChanged( ) is added to the dialog class which get called whenever we have changed the contents in the combo box. Inside this handler a call to GetDate( ) and SetDate( ) methods will update the calendar control present on the the dialog with appropriate date. Using ClassWizard a handler corresponding to the event NewDay( ) is added and a inside the handler appropriate code is added such that when ever we clicked on the calendar control the date and day of the grid clicked get displayed in a message box .


Connection Points

Adding an Event to an ATL controls

Now we will add a and a event to your ATL control. You will fire the ClickFirstpane( ) event if the user clicks within the first pane of the control and fire ClickSecondPane( ) if the user clicks within second pane. Building a component in ATL is a four-step process:

(a) Creating a module
(b) Adding a component to the module
(c) Adding methods to the component
(d) Adding properties to the component.

We would create a module (usually a DLL) called 'conpoint'. Then we will create a component called mycon within this server. Let us carry out these steps now.

Creation Of Module

To create a module the Developer Studio provides an ATL COM AppWizard. This is the easiest of jobs. Carry out the following steps:

(a) Select ‘New’ from the ‘File menu. From the dialog that pops up select ‘ATL COM AppWizard’ as the project. Type ‘conpoint’ as the project name and click ‘OK’ to continue.
(b) Select type of module as ‘Dynamic Link Library’ and click ‘Finish’.
(c) Now a ‘New Project Information’ dialog is displayed which lists the name of files that the wizard would create. Click on ‘OK’. Now that the module stands created let us add a component to it.

Adding Component To The Module

To add component to the module we can use ‘ATL Object Wizard’. Carry out the following steps for adding the component named ‘Calculator’ using this wizard:

(a) Select ‘Insert | New ATL Object’ menu item. This would display the ‘ATL Object Wizard’ dialog.Select 'controls' from the categories List box and Select ‘Full control’ from the various object categories and click on ‘Next’.
(b) An ‘ATL Object Wizard Properties’ dialog is displayed
(c) The property sheet contains two tabs-Names and Attributes.

The Names tab is divided into two sections. The first section displays the ‘C++ names’ and the second section the ‘COM names’. Enter the ‘Short Name’ as mycon. As soon as you do this all other edit controls would be filled automatically . The names filled under the edit controls of ‘C++ names’ indicate that the class Cmycon will implement the object mycon in the files ‘mycon.h’ and ‘mycon.cpp’.The names filled under the edit controls of ‘COM names’ indicate that the CoClass name (component class) remains the same as the short name. The Interface name will be Imycon. The type is a description for the class. The ProgID will be 'compoint.mycon' and from the attributes tab, you selected the Support Connection Points check box. This created the ImyconEvents interface in your .idl file. Note that the interface name starts with an underscore. This is a convention to indicate that the interface is an internal interface. Thus, programs that allow you to browse COM objects can choose not to display the interface to the user. Also notice in the .idl file that Support Connection Points added a line to indicate that ImyconEvents is the default source interface. The source attribute indicates that the control is the source of the notifications, so it will call this interface on the container. When the ‘OK’ button is clicked, the class Cmycon and the interface Imycon are created. They can be viewed from the class view tab. Now you should add the ClickFirstPane( ) and ClickSecondPane( ) methods to the ImyconEvents interface: Right click on ImyconEvents in ClassView and selecting Add Method from the popup menu. From the Add Method dialog box, select a Return Type of void. Type ClickFirstPane in the Method Name box. Enter [in] long x, [in] long y in the Parameters box. Click OK. Check the '.idl' file to see that the code was added as a method to the ImyconEvents dispinterface. Then use the same procedure to define a ClickSecondPane method with the same parameters and return type. The ImyconEvents dispinterface in your .idl file should now look like this:

dispinterface _ImyconEvents
{

properties:
methods:
[id(1), helpstring("method ClickFirstPane")] void ClickFirstPane(int x,int y);
[id(2), helpstring("method ClickSecondPane")] void ClickSecondPane(int x, int y);

} ;

The ClickFirstPane( ) and ClickSeconePane( ) methods that take the x and y coordinates of the clicked point as parameters.Now generate your type library. To do this you can either rebuild your project or right-click the '.idl' file in FileView and click Compile 'conpiont.idl'. This will create the mycon.tlb file, which is your type library. Next, implement a connection point interface and a connection point container interface for your control. (In COM, events are implemented through the mechanism of connection points. To receive events from a COM object, a container establishes an advisory connection to the connection point that the COM object implements. Since a COM object can have multiple connection points, the COM object also implements a connection point container interface. Through this interface, the container can determine which connection points are supported.) The interface that implements a connection point is called IConnectionPoint and the interface that implements a connection point container is called IConnectionPointContainer. To help implement IConnectionPoint, use ClassView to access a connection point wizard. This wizard generates the IConnectionPoint interface by reading your type library and implementing a function for each event that can be fired. To run the wizard, follow these steps: Go to ClassView (on the View menu, click Workspace to see ClassView). Right click on your control's implementation class, in this case Cmycon. In the shortcut menu, select Implement Connection Point. Select _ImyconEvents from the Interfaces list then click OK and a proxy class for the connection point will be generated, in this case, CProxy_ImyconEvents. This is shown in below figure.



If you look at the generated 'conpointCP.h' file in FileView, you see it has a class called CProxy_ImyconEvents that derives from IConnectionPointImpl. 'conpointCP.h' also defines the two methods Fire_ClickFirstPane( ) and Fire_ClickSecondPane( ), which take the two coordinate parameters. These are the methods you call when you want to fire an event from your control. The wizard also added the CProxy_ImyconEvents and IConnectionPointContainerImpl to your control's multiple inheritance list. The wizard also exposed IConnectionPointContainer for you by adding appropriate entries to the COM map. You are finished implementing the code to support events. Now, add some code to fire the events at the appropriate moment. Remember, you are going to fire a ClickFirstPane or ClickSecondPane event when the user clicks the left mouse button in the control. To find out when the user clicks the button, first add a handler for the WM_LBUTTONDOWN message. In ClassView, right click on the Cmycon class and select 'Add Windows Message Handler' from the shortcut menu. Then select WM_LBUTTONDOWN from the list on the left and click the 'Add Handler' button. Click OK. Next, add new code to the OnLButtonDown( ) function in mycon.h (deleting any code placed by the wizard) so that OnLButtonDown( ) now looks like this:

LRESULT OnLButtonDown ( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{

// TODO : Add Code for message handler. Call DefWindowProc if necessary.

POINT pt ;

pt.x = LOWORD ( lParam ) ;

pt.y = HIWORD ( lParam ) ;

if ( PtInRect(&r1,pt ) )

Fire_ClickFirstPane(pt.x,pt.y) ;

else

Fire_ClickSecondPane(pt.x,pt.y) ;

return 0;

}

Since you have already calculated the points of the two panes in the OnDraw( ) function, use them in OnLButtonDown( ), use the PtInRect( ) API function to determine whether the clicked point is in first pane or not. Now try out your events. Build the control and start ActiveX Control Test Container . This time, view the event log window. To route events to the output window, select Logging from the Options menu and select Log to output window. Now insert the control and try clicking in the window. Notice that ClickFirstPane( ) is fired if you click within the first pane and ClickSecondPane( ) is fired when you click in second panet.