Tools Menu

Chapter 27 - COM and DHTML

Visual C++ 6: The Complete Reference
Chris H. Pappas and William H. Murray, III
  Copyright 1998 The McGraw-Hill Companies

Creating the ATL Polygon Project
ATL projects are created by using the ATL COM AppWizard. Use the following steps to create the basic ATL Polygon project:
  1. From within Visual C++, select the File | New menu item, then choose the Projects tab.
  2. Choose the ATL COM AppWizard.
  3. Name the project Polygon, as shown in Figure 27-1.
Figure 27-1: The New Projects dialog box for the ATL Polygon project
When you select OK, the ATL COM AppWizard dialog box will open, as shown in Figure 27-2.
Figure 27-2: The ATL COM AppWizard dialog box
Select the Dynamic Link Library (DLL) option, then select Finish to complete the process. The New Project Information dialog box, as shown in Figure 27-3, will open and display the information for the ATL project.
Figure 27-3: The New Project information dialog box
Select OK to generate the basic files for the ATL Polygon project.
In order to make this project functional, a control will be added to the basic code. Use the Insert | New ATL Object… menu item to open the ATL Object Wizard dialog box, as shown in Figure 27-4.
Figure 27-4: The ATL Object Wizard dialog box is used to add a control to the project
Select the Full Control option and select the Next button. You will now be allowed to set various configurations for the control using the property pages of the ATL Object Wizard Properties dialog box, as shown in Figure 27-5.
Figure 27-5: The ATL Object Wizard Properties dialog box will permit various control configurations to be set
Using the Names tab in this dialog box, enter the name PolyCtl in the Short Name edit box. All other entries will be completed automatically. See Figure 27-5 once again.
Next select the Attributes tab and check both support checkboxes as shown in Figure 27-6.
Figure 27-6: The Attributes tab is used to select IsupportErrorInfo and Connection Points
Now, select the Stock Properties tab and enable support for Fill Color as shown in Figure 27-7.
Figure 27-7: The Stock Properties tab allows specific stock properties to be supported by the project
Select OK and return to the developer’s screen. If you now open the FileView window, as shown in Figure 27-8, you will see the list of files generated for this project.
Figure 27-8: The list of the project’s files is available in the FileView window
There now exists enough information to build the ATL project. From the menu, select the Build | Rebuild All option and compile and link the project. To test the initial control, select the Tools | ActiveX Control Test Container menu item.
Use the Test Container’s Edit | Insert New Control menu item to open the Insert Control dialog box. Scroll down the list of available controls until you locate the PolyCtl Class control. Select this control and it will be placed in the editor, as shown in Figure 27-9.
Figure 27-9: The basis ATL project control is shown in the ActiveX Control Test Container
This control will not really be functional until we add various properties, events, and property pages to the control.
Adding and Modifying the Project’s Code
In this section, we’ll make several modifications to various files in the ATL Polygon project that will make it functional.
Adding a Property
IPolyCtl is used to contain custom methods and properties for the project. To add a new property, use the Visual C++ ClassView to select the IPolyCtl class, then right-click the mouse to open a menu box. From the menu box, choose the Add Property… item. This selection will open the Add Property to Interface dialog box, as shown in Figure 27-10.
Figure 27-10: The Add Property to interface dialog box allows the “Sides” property to be added to the control
Select the property type, short, from the drop-down list of property types. Then use the name Sides as the Property Name. Select OK to add the new property.
The MIDL (a program that builds files with .idl file extensions) defines a get method and a put method that retrieve and set the Sides property.
In addition, get and put function prototypes are added to the POLYCTL.H header file and a skeleton implementation of each to the POLYCTL.CPP file. You’ll see this implementation when we view the completed file in the next section.
Drawing a Better Control
In this section, we’ll examine all of the changes that we have made to the basic ATL program code to make it more functional. Most of these changes have been made to the POLYCTL.H and POLYCTL.CPP files.
This is the code for the header file, POLYCTL.H. Additions and changes to the basic code are shown in a bold font.
// PolyCtl.h : Declaration of the CPolyCtl

#include <math.h>
#include “resource.h”       // main symbols
#include “CPPolygon.h”
//////////////////////////////////////////////////////////////
// CPolyCtl
class CPolyCtl :
 public CComObjectRoot,
 public CComCoClass<CPolyCtl, &CLSID_PolyCtl>,
 public CComControl<CPolyCtl>,
 public CStockPropImpl<CPolyCtl, IPolyCtl,
                       &IID_IPolyCtl,
                       &LIBID_POLYGONLib>,
 
public IProvideClassInfo2Impl<&CLSID_PolyCtl,
  &DIID__PolyEvents,
  &LIBID_POLYGONLib>,
 public IPersistStreamInitImpl<CPolyCtl>,
 public IPersistStorageImpl<CPolyCtl>,
 public IQuickActivateImpl<CPolyCtl>,
 public IOleControlImpl<CPolyCtl>,
 public IOleObjectImpl<CPolyCtl>,
 public IOleInPlaceActiveObjectImpl<CPolyCtl>,
 public IViewObjectExImpl<CPolyCtl>,
 public IOleInPlaceObjectWindowlessImpl<CPolyCtl>,
 public IDataObjectImpl<CPolyCtl>,
 public IPersistPropertyBagImpl<CPolyCtl>,
 public ISupportErrorInfo,
 public ISpecifyPropertyPagesImpl<CPolyCtl>,
 
public CProxy_PolyEvents<CPolyCtl>,
 
public IConnectionPointContainerImpl<CPolyCtl>,
 public IPropertyNotifySinkCP<CPolyCtl>,
 public IObjectSafetyImpl<CPolyCtl>
{
public:
   CPolyCtl()
   {
       m_nSides = 6;                        // initial hexagon  
       m_clrFillColor = RGB(0xFF, 0xFF, 0); // use yellow fill
   }

DECLARE_REGISTRY_RESOURCEID(IDR_PolyCtl)
BEGIN_COM_MAP(CPolyCtl)
 COM_INTERFACE_ENTRY(IPolyCtl)
 COM_INTERFACE_ENTRY(IDispatch)
 COM_INTERFACE_ENTRY_IMPL(IViewObjectEx)
 COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2,
                              IViewObjectEx)
 COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject,
                              IViewObjectEx)
 COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless)
 COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject,
                              IOleInPlaceObjectWindowless)
 COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow,
                              IOleInPlaceObjectWindowless)
 COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject)
 COM_INTERFACE_ENTRY_IMPL(IOleControl)
 COM_INTERFACE_ENTRY_IMPL(IOleObject)
 COM_INTERFACE_ENTRY_IMPL(IQuickActivate)
 COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
 COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit)
 COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag)
 COM_INTERFACE_ENTRY_IMPL(ISpecifyPropertyPages)
 COM_INTERFACE_ENTRY_IMPL(IDataObject)
 COM_INTERFACE_ENTRY(IProvideClassInfo)
 COM_INTERFACE_ENTRY(IProvideClassInfo2)
 COM_INTERFACE_ENTRY(ISupportErrorInfo)
 
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
 COM_INTERFACE_ENTRY(IObjectSafety)
END_COM_MAP()
BEGIN_CONNECTION_POINT_MAP(CPolyCtl)
   CONNECTION_POINT_ENTRY(DIID__PolyEvents)
   CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink)
END_CONNECTION_POINT_MAP()

BEGIN_PROPERTY_MAP(CPolyCtl)
 PROP_ENTRY(“Sides”, 1, CLSID_PolyProp)
 PROP_PAGE(CLSID_CColorPropPage)
END_PROPERTY_MAP()

BEGIN_MSG_MAP(CPolyCtl)
 MESSAGE_HANDLER(WM_PAINT, OnPaint)
 MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
 MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
   MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
END_MSG_MAP()
// ISupportsErrorInfo
 STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IViewObjectEx
 STDMETHOD(GetViewStatus)(DWORD* pdwStatus)
 {
   ATLTRACE(_T(“IViewObjectExImpl::GetViewStatus\n”));
   *pdwStatus = VIEWSTATUS_OPAQUE;
   return S_OK;
 }

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

 HRESULT OnDraw(ATL_DRAWINFO& di);

   LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam,
                                               LPARAM lParam, BOOL& bHandled);
   
   short m_nSides;
   OLE_COLOR m_clrFillColor;
   POINT m_arrPoint[10];
};
As you examine the listing, notice the get and put function prototypes mentioned earlier. Also, notice in the constructor that the initial number of sides for the polygon is set to six and the initial fill color to yellow. This project will allow polygons with 3 to 10 sides to be drawn.
  Note Microsoft’s Polygon project set the limit to 100.
A connection point interface and a connection point container interface are also needed for this project. A COM object can have multiple connection points, so the COM object also implements a connection point container interface. The IConnectionPoint interface implements the connection point. The IConnectionPointContainer interface is used to implement a connection point container.
An ATL proxy generator is used to create the IConnectionPoint interface by reading the type library and creating a function for each event that can be fired. A type library must be generated before using the proxy generator. To build a type library, right-click on the POLYGON.IDL file while in FileView and generate the POLYGON.TLB file by selecting Compile POLYGON.IDL. This file is the type library.
Now, right-click on CpolyCtl and select the Implement Connect Point menu item. The CPPOLYGON.H file has a class called CProxy_PolyEvents that is derived from IConnectionPointImpl and two methods, Fire_ClickIn and Fire_ClickOut. These methods are used to fire control events.
IConnectionPointContainer is exposed through the QueryInterface( ) function when it is added to the COM map in POLYCTL.H.
The IConnectionPointContainer is notified of available points by using a connection point map. Here is a small portion of the POLYCTL.H file:
BEGIN_CONNECTION_POINT_MAP(CPolyCtl)
 CONNECTION_POINT_ENTRY(DIID__PolyEvents)
 CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink)
END_CONNECTION_POINT_MAP()
A WM_LBUTTONDOWN event handler is added to detect when a user clicks the left mouse button. The prototype is included in this file and the implementation in the POLYCTL.CPP file.
The following listing shows the POLYCTL.CPP file containing all of the additions for the project. The code additions are shown in a bold font.
// PolyCtl.cpp : Implementation of CPolyCtl
#include “stdafx.h”
#include “Polygon.h”
#include “PolyCtl.h”
#include <time.h>
#include <string.h>


//////////////////////////////////////////////////////////////
// CPolyCtl

STDMETHODIMP CPolyCtl::InterfaceSupportsErrorInfo(REFIID riid)
{
 static const IID* arr[] =
 {
   &IID_IPolyCtl,
 };
 for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
 {
   if (InlineIsEqualGUID(*arr[i],riid))
     return S_OK;
 }
 return S_FALSE;
}
HRESULT CPolyCtl::OnDraw(ATL_DRAWINFO& di)
{
struct tm *date_time;
time_t timer;
static TEXTMETRIC tm;

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);
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;
double  dblDiff  = 2 * pi / m_nSides;
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;
}
Ellipse(hdc, rc.left, rc.top, rc.right, rc.bottom);

// Create/select brush used to fill the polygon
hBrush = CreateSolidBrush(colFore);
SelectObject(hdc, hBrush);
Polygon(hdc, &m_arrPoint[0], m_nSides);

// Print date and time
time(&timer);
date_time=localtime(&timer);

const char* strtime;
   
strtime  = asctime(date_time);

SetBkMode(hdc,TRANSPARENT);

SetTextAlign(hdc, TA_CENTER | TA_TOP);
ExtTextOut(hdc, (rc.left + rc.right)/2,
          (rc.top + rc.bottom - tm.tmHeight)/2,
          ETO_CLIPPED, &rc, strtime,
          strlen(strtime)-1, NULL);

// Select old pen and brush and delete created brush
SelectObject(hdc, hOldPen);
SelectObject(hdc, hOldBrush);
DeleteObject(hBrush);

return S_OK;
}

LRESULT CPolyCtl::OnLButtonDown(UINT uMsg, WPARAM wParam,
                               LPARAM lParam, BOOL& bHandled)
{
HRGN hRgn;
WORD xPos = LOWORD(lParam);  // horiz. position of cursor
WORD yPos = HIWORD(lParam);  // vertical position of cursor

// Create a region from our list of points
hRgn = CreatePolygonRgn(&m_arrPoint[0], m_nSides, WINDING);
// If the clicked point is in polygon, 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;
}

STDMETHODIMP CPolyCtl::get_Sides(short *pVal)
{
  *pVal = m_nSides;
  return S_OK;
}

STDMETHODIMP CPolyCtl::put_Sides(short newVal)
{
 if (newVal > 2 && newVal < 11)
 {
   m_nSides = newVal;
   FireViewChange();
   return S_OK;
 }
 else
   return Error(_T(“Must have between 3 and 10 sides”));
}
When Microsoft developed the polygon project, they used the sin( ) and cos( ) functions from MATH.H to calculate the polygon points. See if you can find that portion of code in the previous listing. Our project modifies the original polygon project by limiting the number of sides to 10 instead of 100 and includes the code necessary to print the local date and time within the control. As a matter of fact, the date and time will be updated each time a “hit” occurs within the polygon shape. We used code similar to this in the ActiveX control developed in Chapter 26.
The put_Sides method (near the end of the listing) is changed in order to force a call via the FireViewChange( ) function, which in turn calls the InvalidateRect( ) function, forcing a repainting of the control. If this isn’t done, the figure will not update when “hits” occur.
In order to fire events, a small portion of code must be added to the POLYGON.IDL file. The additions to this file are shown in a bold font.
#include <olectl.h>
// Polygon.idl : IDL source for Polygon.dll
//

// This file will be processed by the MIDL tool to
// make the type library (Polygon.tlb) and marshalling code.
import “oaidl.idl”;
import “ocidl.idl”;

 [
   object,
   uuid(4CBBC675-507F-11D0-B98B-000000000000),
   dual,
   helpstring(“IPolyCtl Interface”),
   pointer_default(unique)
 ]
 interface IPolyCtl : IDispatch
 {
   [propput, id(DISPID_FILLCOLOR)]
   HRESULT FillColor([in]OLE_COLOR clr);
   [propget, id(DISPID_FILLCOLOR)]
   HRESULT FillColor([out, retval]OLE_COLOR* pclr);
   [propget, id(1), helpstring(“property Sides”)] \
   HRESULT Sides([out, retval] short *newVal);
   [propput, id(1), helpstring(“property Sides”)] \
   HRESULT Sides([in] short newVal);
 };
[
 uuid(4CBBC673-507F-11D0-B98B-000000000000),
 version(1.0),
 helpstring(“Polygon 1.0 Type Library”)
]
library POLYGONLib
{
 importlib(“stdole32.tlb”);

 [
   uuid(4CBBC677-507F-11D0-B98B-000000000000),
   helpstring(“Event interface for PolyCtl”)
 ]
 dispinterface _PolyEvents
 {
   properties:
   methods:
   [id(1)] void ClickIn([in]long x, [in] long y);
   [id(2)] void ClickOut([in]long x, [in] long y);
 };

 [
   uuid(4CBBC676-507F-11D0-B98B-000000000000),
   helpstring(“PolyCtl Class”)
 ]
 coclass PolyCtl
 {
   [default] interface IPolyCtl;
   
[default, source] dispinterface _PolyEvents;
 };
 

 [
   uuid(A3121F30-516B-11D0-B98C-000000000000),
   helpstring(“PolyProp Class”)
 ]
 coclass PolyProp
 {
   interface IUnknown;
 };
};
The ClickIn( ) and ClickOut( ) methods use the x and y coordinates of the clicked point as parameters.
A property page can be added to the control with the use of the ATL Object Wizard. Use the Insert | New ATL Object… menu item to open the ATL Object Wizard dialog box, as shown in Figure 27-11.
Figure 27-11: The ATL Object Wizard dialog box is used to add a property page control to the project
Select the Property Page option and select the Next button. You will now be allowed to set various configurations for the control using the property pages of the ATL Object Wizard Properties dialog box, as shown in Figure 27-12.
Figure 27-12: The ATL Object Wizard Properties dialog box will permit various control configurations to be set
Using the Names tab in this dialog box, enter the name PolyProp in the Short Name edit box. All other entries will be completed automatically. See Figure 27-12 once again.
Next, select the Strings tab and enter the Title and Doc String as shown in Figure 27-13.
Figure 27-13: The Strings tab is used to set the Title and Doc String for the property page
When you select OK the POLYPROP.H, POLYPROP.CPP and POLYPROP.RGS files will be generated. In addition, a new property page is added to the object entry map.
Use the ResourceView, while in Visual C++, to open the IDD_POLYPROP dialog box. Change the label to Sides: and add an edit box control with the IDC_SIDES ID value.
Make the changes, shown here in bold, to the POLYPROP.H file
// PolyProp.h : Declaration of the CPolyProp

#include “resource.h”       // main symbols
#include “Polygon.h”

EXTERN_C const CLSID CLSID_PolyProp;

//////////////////////////////////////////////////////////////
// CPolyProp
class CPolyProp :
 public CComObjectRoot,
 public CComCoClass<CPolyProp, &CLSID_PolyProp>,
 public IPropertyPageImpl<CPolyProp>,
 public CDialogImpl<CPolyProp>
{
public:
 CPolyProp()
 {
   m_dwTitleID = IDS_TITLEPolyProp;
   m_dwHelpFileID = IDS_HELPFILEPolyProp;
   m_dwDocStringID = IDS_DOCSTRINGPolyProp;
 }
 enum {IDD = IDD_PolyProp};

DECLARE_REGISTRY_RESOURCEID(IDR_PolyProp)

BEGIN_COM_MAP(CPolyProp)
 COM_INTERFACE_ENTRY_IMPL(IPropertyPage)
END_COM_MAP()

BEGIN_MSG_MAP(CPolyProp)
 COMMAND_HANDLER(IDC_SIDES, EN_CHANGE, OnSidesChange)
 CHAIN_MSG_MAP(IPropertyPageImpl<CPolyProp>)
END_MSG_MAP()

 
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;
 }

 LRESULT OnSidesChange(WORD wNotify, WORD wID, HWND hWnd, BOOL&
         bHandled)
 {
   SetDirty(TRUE);
   return 0;
 }
};
A property page could have more than one client attached to it at a time, so the Apply( ) function is used to loop around and call put_Sides on each client with the value obtained from the edit box.
The property page is now added to the control by adding the following line to the POLYCTL.H header file:
PROP_ENTRY(“Sides”, 1, CLSID_PolyProp)
Now, we’re ready to test the control on an actual Web page.
Testing the ATL Control on a Web Page
The ATL Object Wizard creates the initial control along with an HTML file that contains the control. This file is named PolyCtl.htm and can be opened in Microsoft’s Internet Explorer. Here, you will be able to view and test the ATL control on an actual Web page.
Add the following changes, shown in a bold font, to the POLYCTL.HTM while in the Visual C++ environment:
<HTML>
<HEAD>
<TITLE>ATL 2.0 test page for object PolyCtl</TITLE>
</HEAD>
<BODY>
<OBJECT <
CLASS>
>
</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>
Now, start the Microsoft Internet Explorer and open the POLYCTL.HTM file. Your screen should initially appear like Figure 27-14.
Figure 27-14: The ATL control as it initially appears on a web page
Continue to test the control by clicking the left mouse button within and without the control’s figure. Figure 27-15 shows the control after two additional clicks (hits) within the control.
Figure 27-15: The mouse is clicked twice within the control
Notice that the number of sides to the polygon increased.

Books24x7.com, Inc 2000 –  


Visual C++ 6(c) The Complete Reference
Visual Studio 6: The Complete Reference
ISBN: B00007FYGA
EAN: N/A
Year: 1998
Pages: 207

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net