| Tenouk C & C++ | MFC Home | Another ATL Tutorial 1 | ATL Attributes 1 | Download | Site Index |


 

 

 

 

 

Another ATL Tutorial Using Visual C++ .NET part 2

 

 

 

 

 

Program examples compiled using Visual Studio/C++ .Net 2003 compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. The supplementary note for this tutorial is the .NET early stage development.

 

Step 5: Adding an Event

 

In this step, you will add a ClickIn and a ClickOut event to your ATL control. You will fire the ClickIn event if the user clicks within the polygon and fire ClickOut if the user clicks outside. The tasks to add an event are as follows:

  • Adding the ClickIn and ClickOut Methods.

  • Generating the Type Library.

  • Implementing the Connection Point Interfaces.

Adding the ClickIn and ClickOut Methods

 

When you created the ATL control in step 2, you selected the Connection points check box. This created the _IPolyCtlEvents interface in the Polygon.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 note that selecting Connection points added the following line in the Polygon.idl file to indicate that _IPolyCtlEvents is the default source interface:

 

[default, source] dispinterface _IPolyCtlEvents;

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 11.

 

The source attribute indicates that the control is the source of the notifications, so it will call this interface on the container.

Now add the ClickIn and ClickOut methods to the _IPolyCtlEvents interface.

 

 

To add the ClickIn and ClickOut methods

 

  1. In Class View, expand Polygon and PolygonLib to display _IPolyCtlEvents.
  2. Right-click _IPolyCtlEvents. On the shortcut menu, click Add, and then click Add Method.

 

Figure 20: Adding methods to Polygon.

 

Figure 20: Adding methods to Polygon.

 

  1. Select a Return Type of void.
  2. Enter ClickIn in the Method name box.
  3. Under Parameter attributes, select the in box.
  4. Select a Parameter type of LONG.
  5. Type x as the Parameter name, and click Add.

 

Figure 21: Adding ClickIn method through Add Method Wizard.

 

Figure 21: Adding ClickIn method through Add Method Wizard.

 

  1. Next, repeat step 5 to 8, select the in box, enter a Parameter type LONG and Parameter name of y, and click the Add button.
  2. Click Finish.
  3. Repeat the steps above to define a ClickOut method with the same LONG parameters x and y, the same Parameter attributes and the same void return type.

 

Figure 22: Adding ClickOut method through Add Method Wizard.

 

Figure 22: Adding ClickOut method through Add Method Wizard.

 

Check the Polygon.idl file to see that the code was added to the _IPolyCtlEvents dispinterface. The _IPolyCtlEvents dispinterface in your Polygon.idl file should now look like this:

dispinterface _IPolyCtlEvents

{

   properties:

   methods:

   [id(1), helpstring("method ClickIn")] void ClickIn([in]LONG x, [in] LONG y);

   [id(2), helpstring("method ClickOut")] void ClickOut([in] LONG x, [in] LONG y);

};

 

 

 

 

 

 

 

 

 

 

 

 

 

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

 

 

Listing 12.

 

The ClickIn and ClickOut methods take the x and y coordinates of the clicked point as parameters.

 

Generating the Type Library

 

Generate the type library at this point, because the Connection Point Wizard will use it to obtain the information it needs to construct a connection point interface and a connection point container interface for your control.

 

To generate the type library

 

Figure 23: Compiling the IDL file.

 

Figure 23: Compiling the IDL file.

 

This will create the Polygon.tlb file, which is your type library. The Polygon.tlb file is not visible from Solution Explorer, because it is a binary file and cannot be viewed or edited directly.

 

Figure 24: The generated TLB (type library) file.

 

Figure 24: The generated TLB (type library) file.

 

Implementing the Connection Point Interfaces

 

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. Because 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, you will use the Implement 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 use the Implement Connection Point Wizard

 

  1. In Class View, right-click your control's implementation class CPolyCtl.
  2. On the shortcut menu, click Add, and then click Add Connection Point.

 

Figure 25: Adding Connection Point to ATL control.

 

Figure 25: Adding Connection Point to ATL control.

 

  1. Select _IPolyCtlEvents from the Source Interfaces list and double-click it to add it to the Implement connection points column. Click Finish. A proxy class for the connection point will be generated, in this case, CProxy_IPolyCtlEvents.

 

Figure 26: Selecting and adding a Connection Point.

 

Figure 26: Selecting and adding a Connection Point.

 

If you look at the generated _IPolyCtlEvents_CP.h file in Solution Explorer, you will see that it has a class called CProxy_IPolyCtlEvents that derives from IConnectionPointImpl. _IPolyCtlEvents_CP.h also defines the two methods Fire_ClickIn() and Fire_ClickOut(), which take the two coordinate parameters. You call these methods when you want to fire an event from your control.

The wizard also added CProxy_PolyEvents 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 ClickIn or ClickOut event when the user clicks the left mouse button in the control. To find out when the user clicks the button, add a handler for the WM_LBUTTONDOWN message.

 

To add a handler for the WM_LBUTTONDOWN message

 

  1. In Class View, right-click the CPolyCtl class and click Properties on the shortcut menu.
  2. In the Properties window, click the Messages icon and then click WM_LBUTTONDOWN from the list on the left.

 

Figure 27: Adding WM_LBUTTONDOWN, a Windows message handler through Properties window.

 

Figure 27: Adding WM_LBUTTONDOWN, a Windows message handler through Properties window. 

 

  1. From the drop-down list that appears, click <Add> OnLButtonDown. The OnLButtonDown() handler declaration will be added to PolyCtl.h, and the handler implementation will be added to PolyCtl.cpp.

 

Figure 28: Selecting and adding WM_LBUTTONDOWN.

 

Figure 28: Selecting and adding WM_LBUTTONDOWN.

 

Next, modify the handler.

 

To modify the OnLButtonDown() method

 

Change the code which comprises the OnLButtonDown() method in PolyCtl.cpp (deleting any code placed by the wizard) so that it looks like this:

 

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

{

   HRGN hRgn;

   WORD xPos = LOWORD(lParam);  // horizontal position of cursor

   WORD yPos = HIWORD(lParam);  // vertical position of cursor

 

   CalcPoints(m_rcPos);

 

   // Create a region from our list of points

   hRgn = CreatePolygonRgn(&m_arrPoint[0], m_nSides, WINDING);

 

   // If the clicked point is in our polygon then fire the ClickIn

   //  event otherwise we fire the ClickOut event

   if (PtInRegion(hRgn, xPos, yPos))

      Fire_ClickIn(xPos, yPos);

   else

      Fire_ClickOut(xPos, yPos);

 

   // Delete the region that we created

   DeleteObject(hRgn);

   return 0;

}

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 13.

 

This code makes use of the points calculated in the OnDraw() function to create a region that detects the user's mouse clicks with the call to PtInRegion().

The uMsg parameter is the ID of the Windows message being handled. This allows you to have one function that handles a range of messages. The wParam and the lParam parameters are the standard values for the message being handled. The parameter bHandled allows you to specify whether the function handled the message or not. By default, the value is set to TRUE to indicate that the function handled the message, but you can set it to FALSE. This will cause ATL to continue looking for another message handler function to send the message to.

 

Building and Testing the Control

 

Now try out your events. Build the control and start the ActiveX Control Test Container again. This time, view the event log window. To route events to the output window, click Logging from the Options menu and select Log to output window.

 

Figure 29: Setting the Logging Options of the ActiveX Control Test Container.

 

Figure 29: Setting the Logging Options of the ActiveX Control Test Container.

 

Insert the control and try clicking in the window. Note that ClickIn is fired if you click within the filled polygon, and ClickOut is fired when you click outside of it.

 

Figure 30: Polygon in action.

 

Figure 30: Polygon in action.

 

Next, you will add a property page.

 

Step 6: Adding a Property Page

 

Property pages are implemented as separate COM objects, which allow them to be shared if required. In this step, you will do the following tasks to add a property page to the control:

Creating the Property Page Resource

 

To add a property page to your control, use the ATL Add Class Wizard.

 

To add a Property Page

 

  1. In Solution Explorer, right-click Polygon.
  2. On the shortcut menu, click Add, and then click Add Class.

 

Figure 31: Adding a new class for ATL control’s property page.

 

Figure 31: Adding a new class for ATL control’s property page.

 

  1. From the list of templates, select ATL Property Page and click Open.

 

Figure 32: Adding an ATL Property Page.

 

Figure 32: Adding an ATL Property Page.

 

  1. When the ATL Property Page Wizard appears, enter PolyProp as the Short name:

 

Figure 33: ATL Property Page Wizard, Names page.

 

Figure 33: ATL Property Page Wizard, Names page.

 

  1. Click Strings to open the Strings page and enter &Polygon as the Title:

 

Figure 34: ATL Property Page Wizard, Strings page.

 

Figure 34: ATL Property Page Wizard, Strings page.

 

The Title of the property page is the string that appears in the tab for that page. The Doc string is a description that a property frame uses to put in a status line or tool tip. Note that the standard property frame currently does not use this string, so you can leave it with the default contents. You will not going to generate a Help file at the moment, so delete the entry in that text box.

 

  1. Click Finish, and the property page object will be created.

 

The following three files are created:

 

File

Description

PolyProp.h

Contains the C++ class CPolyProp, which implements the property page.

PolyProp.cpp

Includes the PolyProp.h file.

PolyProp.rgs

The registry script that registers the property page object.

 

Table 4.

 

The following code changes are also made:

  1. The new property page is added to the object entry map in Polygon.cpp.

  2. The PolyProp class is added to the Polygon.idl file.

  3. The new registry script file PolyProp.rgs is added to the project resource.

  4. A dialog box template is added to the project resource for the property page.

  5. The property strings that you specified are added to the resource string table.

 

Now add the fields that you want to appear on the property page.

 

To add fields to the Property Page

 

  1. In Solution Explorer, double-click the Polygon.rc resource file. This will open Resource View.
  2. In Resource View, expand the Dialog node and double-click IDD_POLYPROP. Note that the dialog box that appears is empty except for a label that tells you to insert your controls here.
  3. Select that label and change it to read Sides: by altering the Caption text in the Properties window and resizing the label box:

 

Figure 35: Modifying the Static control property.  

 

Figure 35: Modifying the Static control property.

 

  1. Drag an Edit control from the Toolbox to the right of the label:

 

Figure 36: Property page template, adding an Edit control.

 

Figure 36: Property page template, adding an Edit control.

 

  1. Finally, change the ID of the Edit control to IDC_SIDES using the Properties window.

 

Figure 37: Modifying the Edit control property.

 

Figure 37: Modifying the Edit control property.

 

This completes the process of creating the property page resource.

 

Adding Code to Create and Manage the Property Page

 

Now that you have created the property page resource, you need to write the implementation code. First, enable the CPolyProp class to set the number of sides in your object when the Apply button is pressed.

 

To modify the Apply function to set the number of sides

 

Change the Apply() function in PolyProp.h as follows:

 

STDMETHOD(Apply)(void)

{

   USES_CONVERSION;

   ATLTRACE(_T("CPolyProp::Apply\n"));

   for (UINT i = 0; i < m_nObjects; i++)

   {

      CComQIPtr<IPolyCtl, &IID_IPolyCtl> pPoly(m_ppUnk[i]);

      short nSides = (short)GetDlgItemInt(IDC_SIDES);

      if FAILED(pPoly->put_Sides(nSides))

      {

         CComPtr<IErrorInfo> pError;

         CComBSTR strError;

         GetErrorInfo(0, &pError);

         pError->GetDescription(&strError);

         MessageBox(OLE2T(strError), _T("Error"), MB_ICONEXCLAMATION);

         return E_FAIL;

      }

   }

   m_bDirty = FALSE;

   return S_OK;

}

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 14.

 

A property page can have more than one client attached to it at a time, so the Apply() function loops around and calls put_Sides() on each client with the value retrieved from the edit box. You are using the CComQIPtr class, which performs the QueryInterface() on each object to obtain the IPolyCtl interface from the IUnknown interface (stored in the m_ppUnk array).

The code now checks that setting the Sides property actually worked. If it fails, the code displays a message box displaying error details from the IErrorInfo interface. Typically, a container asks an object for the ISupportErrorInfo interface and calls InterfaceSupportsErrorInfo() first, to determine whether the object supports setting error information. You can skip this task.

CComPtr helps you by automatically handling the reference counting, so you do not need to call Release() on the interface. CComBSTR helps you with BSTR processing, so you do not have to perform the final SysFreeString() call. You also use one of the various string conversion classes, so you can convert the BSTR if necessary (this is why the USES_CONVERSION macro is at the start of the function).

You also need to set the property page's dirty flag to indicate that the Apply button should be enabled. This occurs when the user changes the value in the Sides edit box.

 

To handle the Apply button

 

  1. In Class View, right-click CPolyProp and click Properties on the shortcut menu.
  2. In the Properties window, click the Events icon.

 

Figure 38: An event Properties page.

 

Figure 38: An event Properties page.

 

  1. Expand the IDC_SIDES node in the event list.
  2. Select EN_CHANGE, and from the drop-down menu to the right, click <Add> OnEnChangeSides. The OnEnChangeSides() handler declaration will be added to Polyprop.h, and the handler implementation to Polyprop.cpp.

 

Figure 39:  Adding an event to CPolyProp class.

 

Figure 39:  Adding an event to CPolyProp class.

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 15.

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 16.

 

Next, you will modify the handler.

 

To modify the OnEnChangeSides() method

 

Add the following code in Polyprop.cpp to the OnEnChangeSides() method (deleting any code that the wizard put there):

 

LRESULT CPolyProp::OnEnChangeSides(WORD /*wNotifyCode*/,

WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)

{

   SetDirty(TRUE);

   return 0;

}

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 17.

 

OnEnChangeSides() will be called when a WM_COMMAND message is sent with the EN_CHANGE notification for the IDC_SIDES control. OnEnChangeSides() then calls SetDirty() and passes TRUE to indicate the property page is now dirty and the Apply button should be enabled.

 

Adding the Property Page to the Control

 

The ATL Add Class Wizard and the ATL Property Page Wizard do not add the property page to your control for you automatically, because there could be multiple controls in your project. You will need to add an entry to the control's property map.

 

To add the property page

 

Open PolyCtl.h and add this line to the property map:

 

PROP_ENTRY("Sides", 1, CLSID_PolyProp)

 

The control's property map now looks like this:

 

BEGIN_PROP_MAP(CPolyCtl)

   PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)

   PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)

   PROP_ENTRY("FillColor", DISPID_FILLCOLOR, CLSID_StockColorPage)

   PROP_ENTRY("Sides", 1, CLSID_PolyProp)

   // Example entries

   // PROP_ENTRY("Property Description", dispid, clsid)

   // PROP_PAGE(CLSID_StockColorPage)

END_PROP_MAP()

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 18.

 

You could have added a PROP_PAGE macro with the CLSID of your property page, but if you use the PROP_ENTRY macro as shown, the Sides property value is also saved when the control is saved.

The three parameters to the macro are the property description, the DISPID of the property, and the CLSID of the property page that has the property on it. This is useful if, for example, you load the control into Visual Basic and set the number of Sides at design time. Because the number of Sides is saved, when you reload your Visual Basic project, the number of Sides will be restored.

 

Building and Testing the Control

 

Now build that control and insert it into ActiveX Control Test Container. In Test Container, on the Edit menu, click PolyCtl Class Object. The property page appears; click the Polygon tab. (If you clean your solution before re-building, you need to re-insert the PolyCtl Class object using Edit Insert New Control menu).

The Apply button is initially disabled. Start typing a value in the Sides box and the Apply button will become enabled. After you have finished entering the value, click the Apply button. The control display changes, and the Apply button is again disabled. Try entering an invalid value. You will see a message box containing the error description that you set from the put_Sides function.

 

Figure 40: Invoking Polygon’s property page in ActiveX Control Test Container.

 

Figure 40: Invoking Polygon’s property page in ActiveX Control Test Container.

 

Figure 41: Changing Sides’s value through property page.

 

Figure 41: Changing Sides’s value through property page.

 

Next, you will put your control on a Web page.

 

Step 7: Putting the Control on a Web Page

 

Your control is now finished. To see your control work in a real-world situation, put it on a Web page. An HTML file that contains the control was created when you defined your control. Open the PolyCtl.htm file from Solution Explorer and you can see your control on a Web page.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Figure 42: Polygon, an ATL control in web page.

 

Figure 42: Polygon, an ATL control in web page.

 

In this step, you will script the Web page to respond to events. You will also modify the control to let Internet Explorer know that the control is safe for scripting.

 

Scripting the Web Page

 

The control does not do anything yet, so change the Web page to respond to the events that you send.

 

To script the Web page

 

Open PolyCtl.htm and select HTML view. Add the lines in bold to the HTML code that makes up the page.

 

Figure 43: The HTML view.

Figure 43: The HTML view.

 

<HTML>

<HEAD>

<TITLE>ATL 3.0 test page for object PolyCtl</TITLE>

</HEAD>

<BODY>

<OBJECT ID="PolyCtl" <CLASSID="CLSID:4CBBC676-507F-11D0-B98B-000000000000">>

</OBJECT>

<SCRIPT LANGUAGE="VBScript">

<!--

Sub PolyCtl_ClickIn(x, y)

   PolyCtl.Sides = PolyCtl.Sides + 1

End Sub

Sub PolyCtl_ClickOut(x, y)

   PolyCtl.Sides = PolyCtl.Sides - 1

End Sub

-->

</SCRIPT>

</BODY>

</HTML>

 

Figure 44: Adding VBScript codes to the HTML.

 

Figure 44: Adding VBScript codes to the HTML.

 

You have added some VBScript code that gets the Sides property from the control and increases the number of sides by one if you click inside the control. If you click outside the control, you reduce the number of sides by one.

 

Indicating that the Control Is Safe for Scripting

 

You can view the Web page with the control in Internet Explorer or, more conveniently, use the Web browser view built into Visual C++ .NET. To see your control in the Web browser view, right-click PolyCtl.htm, and click View in Browser.

 

Figure 45: Viewing the ATL control in Browser.

 

Figure 45: Viewing the ATL control in Browser.

 

Figure 46: Polygon, an ATL control seen in Browser.

 

Figure 46: Polygon, an ATL control seen in Browser.

 

Based on your current Internet Explorer security settings, you may receive a Security Alert dialog box stating that the control may not be safe to script and could potentially do damage. You can try single click inside the green polygon. The following Security alert dialog box will be displayed.

 

Figure 47: Internet Explorer Security Alert dialog.

 

Figure 47: Internet Explorer Security Alert dialog.

 

Just click Yes. Then click again inside the green polygon. Finally, click outside the polygon (inside the circle). Can you see the action?

 

Other typical example, if you had a control that displayed a file but also had a Delete method that deleted a file, it would be safe if you just viewed it on a page. It would be not safe to script, however, because someone could call the Delete method.

Security Note:   For this tutorial, you can change your security settings in Internet Explorer to run ActiveX controls that are not marked as safe. In Control Panel, click Internet Properties and click Security to change the appropriate settings. When you have completed the tutorial, change your security settings back to their original state.

You can programmatically alert Internet Explorer that it does not need to display the Security Alert dialog box for this particular control. You can do this with the IObjectSafety interface, and ATL supplies an implementation of this interface in the class IObjectSafetyImpl. To add the interface to your control, add IObjectSafetyImpl to your list of inherited classes and add an entry for it in your COM map.

 

To add IObjectSafetyImpl to the control

 

  1. Add the following line to the end of the list of inherited classes in PolyCtl.h and add a comma to the previous line:

 

public IObjectSafetyImpl<CPolyCtl, INTERFACESAFE_FOR_UNTRUSTED_CALLER>

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 19.

 

  1. Add the following line to the COM map in PolyCtl.h:

 

COM_INTERFACE_ENTRY(IObjectSafety)

 

MFC C++ code snippet - ATL Tutorial Using Visual C++ .NET

 

Listing 20.

 

Building and Testing the Control

 

Build the control. Once the build has finished, open PolyCtl.htm in browser view again. This time, the Web page should be displayed directly without the Safety Alert dialog box. Click inside the polygon; the number of sides increases by one. Click outside the polygon (in the circle) to reduce the number of sides. If you try to reduce the number of sides below three, you will see the error message that you set. Here is the control after one click:

 

Figure 48: Polygon in IE Browser without the Security Alert dialog prompt.

 

Figure 48: Polygon in IE Browser without the Security Alert dialog prompt.

 

If you fail to build or re-build the control with the following error, close your Visual Studio/C++ .Net and delete the Debug directory in the polygon project directory. Then re-open Polygon and re-build.

 

cannot open file 'Debug/Polygon.dll'

 

This concludes the ATL tutorial.

 

---------------------End--------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  1. Further reading and digging:

    1. MSDN MFC 7.0 class library online documentation.

    2. MSDN MFC 9.0 class library online documentation - latest version.

    3. Porting & Migrating your older programs.

    4. MSDN Library

    5. DCOM at MSDN.

    6. COM+ at MSDN.

    7. COM at MSDN.

    8. Windows data type.

    9. Win32 programming Tutorial.

    10. The best of C/C++, MFC, Windows and other related books.

    11. Unicode and Multibyte character set: Story and program examples.

 

 

 


 

| Tenouk C & C++ | MFC Home | Another ATL Tutorial 1 | ATL Attributes 1 | Download | Site Index |