ATL

17
ATL Tutorial With ATL, you can create efficient, flexible, lightweight controls. This tutorial leads you through the creation of a control and demonstrates some ATL fundamentals in the process. The ATL control that you create in this seven-step tutorial draws a circle and a filled polygon inside the circle. You will add a control to your project, add a Sides property to indicate how many sides the polygon will have, and add drawing code to display your control when the property changes. Then, you will make your control respond to click events, add a property page to your control, and put your control on a Web page. The tutorial is divided into seven steps. Do them in order because later steps depend on tasks you have completed in earlier steps. Step 1: Creating the Project First you will create the initial ATL project using the ATL COM AppWizard. 1. In the Visual C++ environment, click New on the File menu, then choose the Projects tab. 2. Select the ATL COM AppWizard. 3. Type Polygon as the project name. Your dialog box should look like this: Click OK and the ATL COM AppWizard presents a dialog box offering several choices to configure the initial ATL project.

description

ATL

Transcript of ATL

  • ATL Tutorial With ATL, you can create efficient, flexible, lightweight controls. This tutorial leads you through the creation of a control and demonstrates some ATL fundamentals in the process. The ATL control that you create in this seven-step tutorial draws a circle and a filled polygon inside the circle. You will add a control to your project, add a Sides property to indicate how many sides the polygon will have, and add drawing code to display your control when the property changes. Then, you will make your control respond to click events, add a property page to your control, and put your control on a Web page. The tutorial is divided into seven steps. Do them in order because later steps depend on tasks you have completed in earlier steps.

    Step 1: Creating the Project First you will create the initial ATL project using the ATL COM AppWizard.

    1. In the Visual C++ environment, click New on the File menu, then choose the Projects tab. 2. Select the ATL COM AppWizard. 3. Type Polygon as the project name.

    Your dialog box should look like this:

    Click OK and the ATL COM AppWizard presents a dialog box offering several choices to configure the initial ATL project.

  • Because you are creating a control leave the Server Type as a DLL, since a control must be an in-process server. All the default options are fine, so click Finish. A dialog box appears that lists the main files that will be created. These files are listed below, along with a description of each file that the ATL COM AppWizard generates.

    File Description

    Polygon.cpp Contains the implementation of DllMain, DllCanUnloadNow, DllGetClassObject, DllRegisterServer and DllUnregisterServer. Also contains the object map, which is a list of the ATL objects in your project. This is initially blank, since you haven't created an object yet.

    Polygon.def The standard Windows module definition file for the DLL.

    Polygon.dsw The project workspace.

    Polygon.dsp The file that contains the project settings.

    Polygon.idl The interface definition language file, which describes the interfaces specific to your objects.

    Polygon.rc The resource file, which initially contains the version information and a string containing the project name.

    Resource.h The header file for the resource file.

    Polygonps.mk The make file that can be used to build a proxy/stub DLL. You will not need to use this.

    Polygonps.def The module definition file for the proxy/stub DLL.

    StdAfx.cpp The file that will #include the ATL implementation files.

  • StdAfx.h The file that will #include the ATL header files.

    To make the Polygon DLL useful, you need to add a control, using the ATL Object Wizard.

    Step 2: 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. Some of the options you can select are a basic COM object, a control tailored to work in Internet Explorer, and a property page. In this tutorial, you will create a standard control, so set 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 "PolyCtl" as the short name. The other fields are automatically completed.

  • The Class field shows the C++ class name created to implement the control. The .H File and .CPP File fields show the files created to contain the definition of the C++ class. The CoClass is the name of the component class for this control, and Interface is the name of the interface on which your control will implement its custom methods and properties. The Type is a description for the control, and the ProgID is the readable name that can be used to look up the CLSID of the control. Now enable support for rich error information and connection points for your control:

    1. Click on the Attributes tab. 2. Click the Support ISupportErrorInfo check box. 3. Click the Support Connection Points check box. This will create support for an outgoing

    interface in the IDL file. You will color in the polygon when you draw it, so add a Fill Color stock property:

    1. Click on the Stock Properties tab. You see a list box with all the possible stock properties you can enter.

    2. 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. The following files were created:

    File Description

    PolyCtl.h Contains most of the implementation of the C++ class CPolyCtl.

    PolyCtl.cpp Contains the remaining parts of CPolyCtl.

    PolyCtl.rgs A text file that contains the registry script used to register the control.

    PolyCtl.htm An HTML file that contains the source of a Web page that contains a reference to the newly created control, so that you can try it out in Internet Explorer immediately.

    The Wizard also performed the following code changes:

    z A #include was added to the StdAfx.h and StdAfx.cpp files to include the ATL files necessary for controls.

    z The registry script PolyCtl.rgs was added to the project resource. z Polygon.idl was changed to include details of the new control. z The new control was added to the object map in Polygon.cpp.

    The file PolyCtl.h is the most interesting because it contains the main code that implements your control. You are now ready to build your control:

  • 1. On the Build menu click Build Polygon.dll. 2. Once your control has finished building, click ActiveX Control Test Container on the Tools

    menu. The Test Container is launched. 3. In Test Container, choose Insert New Control from the Edit menu. The Insert Control

    dialog box appears. 4. From the list of available controls in the Insert Control dialog box, choose PolyCtl class.

    You should see a rectangle with the text "ATL 3.0 : PolyCtl" in the middle. 5. Close Test Container.

    Next, you will add a custom property to the control. Step 3: Adding a Property to the Control IPolyCtl is the interface that contains your custom methods and properties. The easiest way to add a property to this interface is to right-click it in ClassView and select Add Property.

    The Add Property to Interface dialog box appears, allowing you to enter the details of the property you want to add:

    1. On the drop-down list of property types, select short. 2. Type "Sides" as the Property Name. As you edit the Property Name field, the

    Implementation box shows the lines that will be added to your IDL file. 3. Click OK to finish adding the property.

  • MIDL (the program that compiles .idl files) defines a Get method that retrieves the property and a Put method that sets the property. When MIDL compiles the file, it automatically defines those two methods in the interface by prepending put_ and get_ to the property name. Along with adding the necessary lines to the .idl file, the Add Property to Interface dialog box also adds the Get and Put function prototypes to the class definition in PolyCtl.h and adds an empty implementation to PolyCtl.cpp. To set and retrieve the property you need a place to store it. From FileView, open PolyCtl.h and add the following line at the end of the class definition after m_clrFillColor is defined: short m_nSides; Now you can implement the Get and Put methods. The get_Sides and put_Sides function definitions have been added to PolyCtl.h. You need to add code in PolyCtl.cpp to match the following: STDMETHODIMP CPolyCtl::get_Sides(short *pVal) { *pVal = m_nSides; return S_OK; } STDMETHODIMP CPolyCtl::put_Sides(short newVal) { if (newVal > 2 && newVal < 101) { m_nSides = newVal; return S_OK; } else return Error(_T("Shape must have between 3 and 100 sides")); } The get_Sides function simply returns the current value of the Sides property through the pVal pointer. In the put_Sides method, you make sure the user is setting the Sides property to an acceptable value. You need more than 2 sides, and since you will be storing an array of points for each side later on, 100 is a reasonable limit for a maximum value. If an invalid value is passed you use the ATL Error function to set the details in the IErrorInfo interface. This is useful if your container needs more information about the error than the returned HRESULT. The last thing you need to do for the property is initialize m_nSides. Make a triangle the default shape by adding a line to the constructor in PolyCtl.h: CPolyCtl() { m_nSides = 3; } You now have a property called Sides. It's not much use until you do something with it, so next you will change the drawing code to use it.

    Step 4: Changing the Drawing Code In the drawing code you will use sin and cos functions to calculate the polygon points, so add include math.h at the top of PolyCtl.h: #include #include "resource.h" // main symbols Note for Release builds only When the ATL COM AppWizard generates the default project, it defines the macro _ATL_MIN_CRT. This macro is defined so that you don't bring the C Run-Time Library into your code if you don't need it. The polygon control needs the C Run-Time Library start-up code to initialize the floating-point functions. Therefore, you need to remove the _ATL_MIN_CRT macro if you

  • want to build a Release version. To remove the macro, click Settings on the Project menu. In the Settings For: drop-down list, choose Multiple Configurations. In the Select project configuration(s) to modify dialog box that appears, click the check boxes for all four Release versions, then click OK. On the C/C++ tab, choose the General category, then remove _ATL_MIN_CRT from the Preprocessor definitions edit box. Once the polygon points are calculated, you store the points by adding an array of type POINT to the end of the class definition in PolyCtl.h: OLE_COLOR m_clrFillColor; short m_nSides;

    POINT m_arrPoint[100];

    Now change the OnDraw function in PolyCtl.h to match the one below. Note that you remove the calls to the Rectangle and DrawText functions. You also explicitly get and select a black pen and white brush. You need to do this in case your control is running windowless. If you don't have your own window, you can't make assumptions about the device context you'll be drawing in. The completed OnDraw looks like this: HRESULT CPolyCtl::OnDraw(ATL_DRAWINFO& di) { RECT& rc = *(RECT*)di.prcBounds; HDC hdc = di.hdcDraw; COLORREF colFore; HBRUSH hOldBrush, hBrush; HPEN hOldPen, hPen; // Translate m_colFore into a COLORREF type OleTranslateColor(m_clrFillColor, NULL, &colFore); // Create and select the colors to draw the circle hPen = (HPEN)GetStockObject(BLACK_PEN); hOldPen = (HPEN)SelectObject(hdc, hPen); hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH); hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom); // Create and select the brush that will be used to fill the polygon hBrush = CreateSolidBrush(colFore); SelectObject(hdc, hBrush); CalcPoints(rc); Polygon(hdc, &m_arrPoint[0], m_nSides); // Select back the old pen and brush and delete the brush we created SelectObject(hdc, hOldPen); SelectObject(hdc, hOldBrush); DeleteObject(hBrush); return S_OK; } You now need to add a function, called CalcPoints, that will calculate the coordinates of line intersections. These calculations will be based on the RECT variable that is passed into the function. First you should add the definition of CalcPoints to the public section for the CPolyCtl class in PolyCtl.h: void CalcPoints(const RECT& rc); The public section of the CPolyCtl class should now look like this:

  • // IPolyCtl public: STDMETHOD(get_Sides)(/*[out, retval]*/ short *newVal); STDMETHOD(put_Sides)(/*[in]*/ short newVal);

    void CalcPoints(const RECT& rc);

    Next, add the implementation of the CalcPoints function to the end of the PolyCtl.cpp file: void CPolyCtl::CalcPoints(const RECT& rc) { const double pi = 3.14159265358979; POINT ptCenter; double dblRadiusx = (rc.right - rc.left) / 2; double dblRadiusy = (rc.bottom - rc.top) / 2; double dblAngle = 3 * pi / 2; // Start at the top double dblDiff = 2 * pi / m_nSides; // Angle each side will make ptCenter.x = (rc.left + rc.right) / 2; ptCenter.y = (rc.top + rc.bottom) / 2; // Calculate the points for each side for (int i = 0; i < m_nSides; i++) { m_arrPoint[i].x = (long)(dblRadiusx * cos(dblAngle) + ptCenter.x + 0.5); m_arrPoint[i].y = (long)(dblRadiusy * sin(dblAngle) + ptCenter.y + 0.5); dblAngle += dblDiff; } } Now, initialize m_clrFillColor. Choose green as the default color and add this line to the CPolyCtl constructor in PolyCtl.h:

    m_clrFillColor = RGB(0, 0xFF, 0);

    The constructor now looks like this: CPolyCtl() { m_nSides = 3; m_clrFillColor = RGB(0, 0xFF, 0); } Now rebuild the control and try it again. Open ActiveX Control Test Container and insert the control. You should see a green triangle within a circle. Try changing the number of sides by following the steps below. To modify properties on a dual interface from within Test Container, use Invoke Methods:

    1. In Test Container, click Invoke Methods on the Control menu. The Invoke Method dialog box is displayed.

    2. Click the PropPut version of the Sides property from the Method Name. 3. Type 5 in the Parameter Value edit box, click Set Value and click Invoke.

  • Notice that the control doesn't change. What is wrong? Although you changed the number of sides internally by setting the m_nSides variable, you didn't cause the control to repaint. If you switch to another application and then switch back to Test Container you will find that the control is repainted and now has the correct number of sides. To correct this problem, you need to add a call to the FireViewChange function, which is defined in IViewObjectExImpl, after you set the number of sides. If the control is running in its own window, FireViewChange will call the InvalidateRect API directly. If the control is running windowless, the InvalidateRect method will be called on the container's site interface. This forces the control to repaint itself. The new put_Sides method is as follows: STDMETHODIMP CPolyCtl::put_Sides(short newVal) { if (newVal > 2 && newVal < 101) { m_nSides = newVal; FireViewChange(); return S_OK; } else return Error(_T("Shape must have between 3 and 100 sides")); } After you've added FireViewChange, rebuild and try the control again. This time when you change the number of sides and click Invoke, you should see the control change immediately. Next, you will add an event to the control.

    Step 5: Adding an Event Now 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. When you created the full control in Step 2, you selected the Support Connection Points check box. This created the IPolyCtlEvents 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 IPolyCtlEvents 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. Now you should add the ClickIn and ClickOut methods to the IPolyCtlEvents interface:

    1. Right click on IPolyCtlEvents in ClassView and selecting Add Method from the popup menu.

    2. Select a Return Type of void. 3. Type ClickIn in the Method Name box. 4. Enter [in] long x, [in] long y in the Parameters box. 5. Click OK.

    Check the .idl file to see that the code was added as a method to the IPolyCtlEvents dispinterface. Then use the same procedure to define a ClickOut method with the same parameters and return type. The IPolyCtlEvents dispinterface in your .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); }; The ClickIn and ClickOut 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 Polygon.idl. This will create the Polygon.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 CPolyCtl. In the shortcut menu, select Implement Connection Point. Select _PolyEvents from the Interfaces list then click OK and a proxy class for the connection point will be generated, in this case, CProxy_IPolyCtlEvents.

  • If you look at the generated PolygonCP.h file in FileView, you see it has a class called CProxy_PolyCtlEvents that derives from IConnectionPointImpl. PolygonCP.h also defines the two methods Fire_ClickIn and Fire_ClickOut, 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_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, first add a handler for the WM_LBUTTONDOWN message. In ClassView, right click on the CPolyCtl 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 PolyCtl.h (deleting any code placed by the wizard) so that OnLButtonDown now 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; } Since you have already calculated the points of the polygon in the OnDraw function, use them in OnLButtonDown to create a region. Then, use the PtInRegion API function to determine whether the clicked point is inside the polygon or not. 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 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. Doing so will cause ATL to continue looking for another message handler function to which to send the message. Now try out your events. Build the control and start ActiveX Control Test Container again. 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 ClickIn is fired if you click within the filled polygon and ClickOut is fired when you click outside of it. Next you will add a property page.

    Step 6: Adding a Property Page Property pages are implemented as separate COM objects, which allow property pages to be shared if

  • required. To add a property page to your control you 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. You again get the dialog box allowing you to enter the name of the new object. Call the object PolyProp 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 &Polygon as the title. 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 doesn't use this string, but you can set it anyway. You're not going to generate a Helpfile at the moment, so erase the entry in that text box. Click OK 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.

    The following code changes are also made:

    z The new property page is added to the object entry map in Polygon.cpp. z The PolyProp class is added to the Polygon.idl file. z The new registry script file PolyProp.rgs is added to the project resource. z A dialog box template is added to the project resource for the property page. z The property strings you specified are added to the resource string table.

    Now add the fields that you want to appear on the property page. Notice that in Polygon.rc the dialog is empty except for a label that tells you to insert your controls here. Delete that label and add one that contains the text "Sides:". Next to the label add an edit box and give it an ID of IDC_SIDES.

    Include Polygon.h at the top of the PolyProp.h file: #include "Polygon.h" // definition of IpolyCtl Now enable the CPolyProp class to set the number of sides in your object when the Apply button is pressed. 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 pPoly(m_ppUnk[i]); short nSides = (short)GetDlgItemInt(IDC_SIDES); if FAILED(pPoly->put_Sides(nSides)) { CComPtr 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; } A property page could 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 (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. But since it's your control, you can forego that check. CComPtr helps you by automatically handling the reference counting, so you don't need to call Release on the interface. CComBSTR helps you with BSTR processing, so you don't 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 we add the USES_CONVERSION macro at the start of the function). You also must 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. Right-click the property page class (CPolyProp) in ClassView and then select Add Windows Message Handler... from the shortcut menu. Select IDC_SIDES from the object box and then add the EN_CHANGE message. Now add the following code in Polyprop.h to the OnChangeSides function (deleting any code that the wizard put there): LRESULT OnChangeSides(WORD wNotify, WORD wID, HWND hWnd, BOOL& bHandled) { SetDirty(TRUE); return 0; } OnChangeSides will be called when a WM_COMMAND message is sent with the EN_CHANGE notification for the IDC_SIDES control. OnChangeSides then calls SetDirty and passes TRUE to indicate the property page is now dirty and the Apply button should be enabled. Now, add the property page to your control. The ATL Object Wizard doesn't do this for you automatically, since there could be multiple controls in your project. 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() 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. Since the number of Sides is saved, when you reload your Visual Basic project the number of Sides will be restored. Now build that control and insert it into ActiveX Control Test Container. Then, in Test Container, on the Edit menu click PolyCtl Class Object. The property page appears; chose the Polygon tab.

  • The Apply button is initially disabled. Start typing a value in the Sides edit 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 and you should see a message box containing the error description that you set from the put_Sides function. Next you'll 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. When the ATL Object Wizard creates the initial control it also creates an HTML file that contains the control. You can open up the PolyCtl.htm file in Internet Explorer and you see your control on a Web page. The control doesn't do anything yet, so change the Web page to respond to the events that you send. Open PolyCtl.htm in Visual C++ (if the file does not appear in FileView, select Open from the File menu to open the file) and add the lines in bold. ATL 3.0 test page for object PolyCtl >

  • PolyCtl.Sides = PolyCtl.Sides - 1 End Sub --> 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. Start up Internet Explorer and make sure your Security settings are set to Medium:

    1. Click Internet Options on the View menu. 2. Select the Security tab and set the security to Medium, if necessary, then click OK.

    Now open PolyCtl.htm in Internet Explorer. After you click on the control, a Security Alert dialog box informs you that Internet Explorer doesn't know if the control is safe to script. What does this mean? Imagine if you had a control that, for example, displayed a file, but also had a Delete method that deleted a file. The control would be safe if you just viewed it on a page, but wouldn't be safe to script since someone could call the Delete method. This message is Internet Explorer's way of saying that it doesn't know if someone could do damage with this control, so it is asking the user. You know your control is safe, so click Yes. Now click inside the polygon; the number of sides increases by one. Click outside the polygon 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. The following figure shows the control running in Internet Explorer after you have clicked inside the polygon twice.

  • Since you know your control is always safe to script, it would be good to let Internet Explorer know, so that it doesn't need to show the Security Alert dialog box. You can do this through the IObjectSafety interface. ATL supplies an implementation of this interface in the class IObjectSafetyImpl. To add the interface to your control, just add IObjectSafetyImpl to your list of inherited classes and add an entry for it in your COM map. Add the following line to the end of the list of inherited classes in PolyCtl.h, remembering to add a comma to the previous line:

    public IObjectSafetyImpl

    Then add the following line to the COM map in PolyCtl.h:

    COM_INTERFACE_ENTRY(IObjectSafety)

    Now build the control. Once the build has finished, open PolyCtl.htm in Internet Explorer again. This time the Web page should be displayed directly without the Safety Violation dialog box. Click inside and outside the polygon to confirm that the scripting is working.