Chapter 30

Creating an ActiveX Control To Activate a Web Page

by Rob McGregor


CONTENTS

ActiveX controls are the new standard for OLE Control Extensions (OCXs) that use the OLE Control 96 specification. Because ActiveX controls are changing the face of the World Wide Web, they are having an enormous impact on the world of Windows communications programming. This chapter shows you how to create a new ActiveX control and add it to a Web page to help activate the Internet.

Overview of ActiveX Controls

An ActiveX control is an OLE control with some special features. The control must be a COM object and must export the functions DLLRegisterServer() and DLLUnRegisterServer(). The control must also implement the IUnknown interface. MFC makes this all very easy by providing convenient wrapper classes; the Visual C++ OLE Control Wizard makes it easy to create a skeleton control. The Visual C++ 4.2 Control Wizard has been updated to support advanced features of ActiveX controls. The most important new feature is support for asynchronous code and property downloading. This feature allows control code and properties to load in the background, preventing application blocking.

OLE controls are programmable software components that can be used in a variety of OLE-enabled containers, including COM-aware (ActiveX-enabled) Web browsers on the Internet. The most exciting thing about an ActiveX control is that it is inherently an Internet control. This means that an ActiveX control can be used within an ActiveX document or it can be a direct part of a Web page!

NOTE
ActiveX controls aren't limited to use on the Web. They can also be used in any ActiveX-enabled container (if the control supports the COM interfaces required by that container).

The OLE Controls 96 Specification

ActiveX controls are special because they use the OLE Controls 96 specification. Like standard OLE controls, ActiveX controls are self-contained, plug-in software components. The OLE Controls 96 specification provides some advanced features for OLE controls not available previously, including these:

NOTE
Because the primary purpose of ActiveX technology at this time is to provide useful components over the Internet, it's desirable to keep the code size small for an ActiveX control. To help ensure this, Microsoft Visual C++ 4.x doesn't allow static linkage for ActiveX controls. The controls must use the shared MFC DLLs at runtime, which means that these DLLs must be present on the user's system.

A Sample ActiveX Control: JIGGLER.OCX

Let's now implement a basic-but useful and fun-ActiveX control. This control was inspired by the Java applet "Nervous Text," which was written by Daniel Wyszynski from the Center for Applied Large-Scale Computing (CALC).

NOTE
The source code for the JIGGLER ActiveX control has no relation whatsoever to the original "Nervous Text" Java applet source code; I started from scratch. (Thanks for the idea though, Dan!)

The basic idea for the control is that a text string, supplied by the user of the control, becomes visually hyperactive in the container, whether it be an ActiveX-enabled Web page or an OLE container window. Each character in the text string must be offset from the next by a random amount on both the x and y axes. This random offset is controlled by a window timer internal to the control; the randomly offset characters are redrawn with each WM_TIMER message received by the control. This causes the text string to appear to dance, or jiggle, on the display (thus the name JIGGLER).

NOTE
JIGGLER.OCX, along with all its source files, can be found in the SOURCE\CHAP30\JIGGLER folder on this book's companion CD-ROM.

To test the completed JIGGLER.OCX control, you use the Test Container application that ships with Visual C++ and create a simple Web page for Microsoft's ActiveX-enabled Web browser, Internet Explorer 3 (IE3), to test the control in a typical active Web setting.

Creating an OLE Control Skeleton

The first step in creating the JIGGLER ActiveX control is to create an ActiveX-compliant control skeleton. The easiest and most effective way to do this is to use the Visual C++ 4.2 OLE Control Wizard, which provides new ActiveX features in step 2 of the Wizard (see Figure 30.1).

Figure 30.1 : Creating a new ActiveX control project with the Visual C++ 4.2 OLE Control Wizard.

The OLE Control Wizard walks you through the two simple steps needed to create a robust ActiveX control skeleton. Then it's just a matter of adding the methods needed to implement the control's desired functionality (in this case, the jiggling of some text).

NOTE
The Visual C++ OLE Control Wizard enables you to select advanced ActiveX options for your control. The most important of these is asynchronous download. For this skeleton control, it's the only option you need to check.

Testing the Skeleton Control

After you select the Finish button in the Control Wizard dialog box, the Control Wizard generates a set of header, source, and support files for the new control. The resulting classes for the JIGGLER control from a run of the Control Wizard are shown in Figure 30.2, as displayed in the Visual C++ ClassView pane.

Figure 30.2 : The classes and methods generated automatically by the OLE Control Wizard.

The skeleton control can be compiled at this point. Visual C++ automatically creates a type library and registers the new control with the system registry at this time. If you insert the control into the Test Container application (included with Visual C++), the default visual representation of the control simply draws an ellipse. You can modify the visual aspects of the control by adding the desired code.

Adding Functionality to the Skeleton

Now that you have an ActiveX skeleton to build on, let's add the functionality needed to create an OCX that has some useful purpose-in this case, jiggling text. Your JIGGLER.OCX control will have the following features:

Customizing the Project Resources

The default resources for the skeleton control are a good starting point, but they need to be customized for the professional look users have come to expect. For this control, you change the default bitmap used to identify the OCX in client programming system tool palettes (such as the Visual Basic tool palette) to that shown in Figure 30.3.

Figure 30.3 : The new customized bitmap for the JIGGLER control under con-struction in the Developer Studio resource editor.

After recompiling, the OCX uses the new bitmap to help users differentiate a JIGGLER control from other controls in a tool palette.

The Property Page Dialog Resource

To enable users to control the JIGGLER.OCX properties at design time, you add controls to the control's property page dialog box resource (IDD_PROPAGE_JIGGLER) to represent the state or value of each property exposed by the control. After adding the appropriate controls for every property (as discussed next), the dialog resource looks like the one shown in Figure 30.4.

Figure 30.4 : The customized property page dialog resource in the Developer Studio resource editor.

The dialog box resource has only four controls: two static text boxes and two edit controls. The edit controls allow the user to specify a text string (for the jiggling text), and the interval (in milliseconds) at which the jiggling occurs. The entire dialog resource script should look something like that shown in Listing 30.1.


Listing 30.1. The IDD_PROPPAGE_JIGGLER dialog resource.
IDD_PROPPAGE_JIGGLER DIALOG DISCARDABLE  0, 0, 250, 62

STYLE WS_CHILD

FONT 8, "MS Sans Serif"

BEGIN

    LTEXT           "Jiggle Text:",IDC_STATIC,5,7,45,10

    EDITTEXT        IDC_EDIT_CAPTION,55,5,180,12,ES_AUTOHSCROLL

    LTEXT           "Interval:",IDC_STATIC,5,22,45,10

    EDITTEXT        IDC_EDIT_INTERVAL,55,20,40,12,ES_AUTOHSCROLL

END


The CJigglerCtrl Class

To fill out the new JIGGLER.OCX CJigglerCtrl class and make it fully functional, perform the following steps:

  1. Use the Class Wizard to add OLE Automation properties to the class; alternatively, add the dispatch map code manually.
  2. Implement the code to provide the jiggling functionality you need.

Here are the properties you'll add:

The methods you'll add are listed in Table 30.1.

Table 30.1. The additional methods used by the JIGGLE.OCX control.

MethodDescription
GetDefaultFont()Gets the custom font stored in the m_fontDefault data member.
JiggleText()The method that implements the text-jiggling functionality.
OnCreate()Starts the internal timer when the control window is created.
OnDestroy()Kills the internal timer when the control window is destroyed.
OnIntervalChanged()Called by MFC when the timer interval changes and resets the internal timer to the new value.
OnTimer()Called by MFC in response to the WM_TIMER message. This method calls JiggleText() for each WM_TIMER message in the control's message queue.
SetDefaultFont()Sets the custom font stored in the m_fontDefault data member.

The CJigglerCtrl Class Interface (JIGGLERC.H)

The CJigglerCtrl class declaration for the JIGGLER.OCX control is given in Listing 30.2.


Listing 30.2. The interface for the CJigglerCtrl class (JIGGLERC.H).
///////////////////////////////////////////////////////////////////

//  Module  : JIGGLERC.H

//

//  Purpose : Interface for the CJigglerCtrl OLE control class.



#define IDC_TIMER1  100



///////////////////////////////////////////////////////////////////

// The CJigglerCtrl class



class CJigglerCtrl : public COleControl

{

   DECLARE_DYNCREATE(CJigglerCtrl)



// Constructor

public:

   CJigglerCtrl();

   virtual void DoPropExchange(CPropExchange* pPX);



protected:

   CRect        m_rcBounds;     // Control bounding rect

   CFontHolder  m_fontDefault;  // Custom OLE font



   void JiggleText();



   DECLARE_OLECREATE_EX(CJigglerCtrl)  // Class factory and guid

   DECLARE_OLETYPELIB(CJigglerCtrl)    // GetTypeInfo

   DECLARE_PROPPAGEIDS(CJigglerCtrl)   // Property page IDs

   DECLARE_OLECTLTYPE(CJigglerCtrl)    // Type name and misc status



   // Message map entries

   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

   afx_msg void OnDestroy();

   afx_msg void OnTimer(UINT nIDEvent);

   afx_msg void AboutBox();



   DECLARE_MESSAGE_MAP()



   // Event maps

   DECLARE_EVENT_MAP()



   // Dispatch maps

   short m_nInterval;

   afx_msg void OnIntervalChanged();

   afx_msg LPFONTDISP GetDefaultFont();

   afx_msg void SetDefaultFont(LPFONTDISP newValue);



   DECLARE_DISPATCH_MAP()



public:

   // Dispatch and event IDs

   enum {

      dispidInterval    = 1L,

      dispidDefaultFont = 2L,

   };

};



///////////////////////////////////////////////////////////////////


Implementing the CJigglerCtrl Class

The CJigglerCtrl class is the heart of your ActiveX control, providing all the functionality the control needs. The helper methods and dispatch map methods listed in the class declaration in Listing 30.2 must, of course, be implemented. The default font is described by the FONTDESC type, which is used by OLE to create the font (in this case, 24-point Arial). This is shown in the following code:

// Default OLE font for the control

static const FONTDESC _fontdescDefault =

{

   sizeof(FONTDESC), OLESTR("Arial"), FONTSIZE(24),

   FW_NORMAL, ANSI_CHARSET, FALSE, FALSE, FALSE

};

The class data member m_fontDefault is used to hold the resulting CFontHolder object. The CFontHolder class encapsulates the functionality of a Windows font object and the OLE IFont interface. You use this in the Jiggler control to implement the custom DefaultFont property.

The IFontDispatch Interface

The CFontHolder class implements an OLE font object that uses the IFontDispatch OLE interface. This interface exposes a font object's properties through OLE automation. Table 30.2 describes the dispatch IDs for the various font properties.

Table 30.2. The dispIDs for various OLE font properties.

Symbol
Value
DISPID_FONT_NAME
0
DISPID_FONT_SIZE
2
DISPID_FONT_BOLD
3
DISPID_FONT_ITALIC
4
DISPID_FONT_UNDER
5
DISPID_FONT_STRIKE
6
DISPID_FONT_WEIGHT
7
DISPID_FONT_CHARSET
8

The properties in the IFontDisp interface support both read and write access, and they are listed in Table 30.3. These properties will become very useful later in this chapter when you want to manipulate font characteristics for your JIGGLER control with a scripting language in your sample Web page.

Table 30.3. The properties for the IFontDisp interface.

PropertyType Description
NameBSTR The face name of the font (for example, Arial)
SizeCY The point size of the font
BoldBOOL Indicates if the font is bold
ItalicBOOL Indicates if the font is italic
UnderlineBOOL Indicates if the font is underlined
StrikethroughBOOL Indicates if the font is strikethrough
Weightshort The boldness of the font
Charsetshort The character set used for the font

The CJigglerCtrl Constructor

The CJigglerCtrl constructor simply calls the COleControl method InitializeIIDs() with the appropriate parameters, as you can see here:

CJigglerCtrl::CJigglerCtrl() : m_fontDefault(&m_xFontNotification)

{

   InitializeIIDs(&IID_DJiggler, &IID_DJigglerEvents);

m_lReadyState = READYSTATE_LOADING;

}

The COleControl member m_lReadyState is set to the value READYSTATE_LOADING to indicate that the control is currently loading its properties.

The Jiggler Control Properties

The dispatch map for the CJigglerCtrl class is used to set up the connection between class data members and property values. Listing 30.3 shows the dispatch map for the CJigglerCtrl class.


Listing 30.3. The dispatch map for the CJigglerCtrl class.
///////////////////////////////////////////////////////////////////

// Dispatch map



BEGIN_DISPATCH_MAP(CJigglerCtrl, COleControl)

   DISP_PROPERTY_NOTIFY(CJigglerCtrl, "Interval", m_nInterval,

      OnIntervalChanged, VT_I2)



   DISP_PROPERTY_EX(CJigglerCtrl, "DefaultFont", GetDefaultFont,

      SetDefaultFont, VT_FONT)



   DISP_DEFVALUE(CJigglerCtrl, "Caption")

   DISP_STOCKPROP_CAPTION()

   DISP_STOCKFUNC_REFRESH()

   DISP_STOCKPROP_READYSTATE()

   DISP_STOCKPROP_BACKCOLOR()

   DISP_STOCKPROP_FORECOLOR()

   DISP_FUNCTION_ID(CJigglerCtrl, "AboutBox", DISPID_ABOUTBOX,

      AboutBox, VT_EMPTY, VTS_NONE)

END_DISPATCH_MAP()


NOTE
The JIGGLER control uses the stock property Caption, which is the default property for the control. This is evident from the use of the DISP_STOCKPROP_CAPTION and DISP_DEFVALUE macros.

There are three property pages for this control: one custom page (the General page) and two predefined pages (Colors and Font), as specified with this code:

///////////////////////////////////////////////////////////////////

// Property pages



BEGIN_PROPPAGEIDS(CJigglerCtrl, 3)

   PROPPAGEID(CJigglerPropPage::guid)

   PROPPAGEID(CLSID_CColorPropPage)

   PROPPAGEID(CLSID_CFontPropPage)

END_PROPPAGEIDS(CJigglerCtrl)

At runtime, The General property page (defined by your customized dialog template resource) looks like the one in Figure 30.5.

Figure 30.5 : The customized General property page (CJigglerPropPage::guid).

The identifiers CLSID_CColorPropPage and CLSID_CFontPropPage are predefined values that MFC uses to add the Colors and Font property pages to the control's property sheet. Figure 30.6 shows the predefined Colors property page with the two stock properties BackColor and ForeColor present.

Figure 30.6 : The predefined Colors property page (CLSID_CColorPropPage).

Figure 30.7 shows the predefined Font property page with the default Arial font selected.

Figure 30.7 : The predefined Font property page (CLSID_CFontPropPage).

Object Persistence with DoPropExchange()

To make sure that your OLE control "remembers" settings applied to properties at design time when it executes at runtime, you must implement object persistence for the control. By doing this, you can ensure that property settings made with a property editor will remain valid at runtime without writing additional code on the client side. This is achieved by using the PX_* group of persistence support functions provided by MFC.

The CJigglerCtrl::DoPropExchange() method makes calls to MFC's predefined persistence methods to provide property persistence for the JIGGLER control. The method is given in Listing 30.4.


Listing 30.4. The DoPropExchange() method.
///////////////////////////////////////////////////////////////////

// CJigglerCtrl::DoPropExchange - Persistence support



void CJigglerCtrl::DoPropExchange(CPropExchange* pPX)

{

   ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));



   // Set a default caption

   if (InternalGetText() == "")

      SetText(_T("ActiveX Jiggler!!"));



   COleControl::DoPropExchange(pPX);



//

// Call PX_* functions for each persistent custom property

//

   // Property exchange for default font

   PX_Font(pPX, _T("DefaultFont"), m_fontDefault,

      &_fontdescDefault);



   PX_Short(pPX, _T("Interval"), m_nInterval, 100);

}


Miscellaneous OLE Control Housekeeping

When the CJigglerCtrl class was generated by the OLE Control Wizard, it generated some very important OLE housekeeping functions in the JIGGLERC.CPP source file. These functions perform the following tasks:

Listing 30.5 shows the code for these tasks.


Listing 30.5. OLE housekeeping functions for the CJigglerCtrl class.
///////////////////////////////////////////////////////////////////

// Initialize class factory and guid



IMPLEMENT_OLECREATE_EX(CJigglerCtrl, "JIGGLER.JigglerCtrl.1",

   0x5cd2fc83, 0xe7bd, 0x11cf, 0xa3, 0xbc, 0x44, 0x45, 0x53,

   0x54, 0, 0)



///////////////////////////////////////////////////////////////////

// Type library ID and version



IMPLEMENT_OLETYPELIB(CJigglerCtrl, _tlid, _wVerMajor, _wVerMinor)



///////////////////////////////////////////////////////////////////

// Interface IDs



const IID BASED_CODE IID_DJiggler =

{

   0x5cd2fc81, 0xe7bd, 0x11cf,

   {

      0xa3, 0xbc, 0x44, 0x45, 0x53, 0x54, 0, 0

   }

};



const IID BASED_CODE IID_DJigglerEvents =

{

   0x5cd2fc82, 0xe7bd, 0x11cf,

   {

      0xa3, 0xbc, 0x44, 0x45, 0x53, 0x54, 0, 0

   }

};



///////////////////////////////////////////////////////////////////

// Control type information



static const DWORD BASED_CODE _dwJigglerOleMisc =

   OLEMISC_ACTIVATEWHENVISIBLE |

   OLEMISC_SETCLIENTSITEFIRST |

   OLEMISC_INSIDEOUT |

   OLEMISC_CANTLINKINSIDE |

   OLEMISC_RECOMPOSEONRESIZE;



IMPLEMENT_OLECTLTYPE(CJigglerCtrl, IDS_JIGGLER, _dwJigglerOleMisc)



///////////////////////////////////////////////////////////////////

// CJigglerCtrl::CJigglerCtrlFactory::UpdateRegistry -

// Adds or removes system registry entries for CJigglerCtrl



BOOL CJigglerCtrl::CJigglerCtrlFactory::UpdateRegistry(

   BOOL bRegister)

{

   if (bRegister)

      return AfxOleRegisterControlClass(

         AfxGetInstanceHandle(),

         m_clsid,

         m_lpszProgID,

         IDS_JIGGLER,

         IDB_JIGGLER,

         afxRegInsertable | afxRegApartmentThreading,

         _dwJigglerOleMisc,

         _tlid,

         _wVerMajor,

         _wVerMinor);

   else

      return AfxOleUnregisterClass(m_clsid, m_lpszProgID);

}


The JiggleText() Method

Typically, MFC calls the OnDraw() method whenever a control needs to be redrawn. In this case, OnDraw() simply isn't needed because the control is continually redrawn in response to the internal timer firing WM_TIMER messages. Instead, the drawing code all takes place within the JiggleText() method, shown in Listing 30.6.


Listing 30.6. The JiggleText() method.
///////////////////////////////////////////////////////////////////

// CJigglerCtrl::JiggleText() - The meat of the control!



void CJigglerCtrl::JiggleText()

{

   // Get the client DC

   CClientDC dc(this);



   // Prepare a memory DC for holding a memory bitmap

   CDC dcMem;

   dcMem.CreateCompatibleDC(&dc);



   // Get the current caption text and text length

   const CString& strText = InternalGetText();

   int nMsgLen = strText.GetLength();



   // Select font and set transparent text mode, forecolor

   CFont* pFontOld = SelectFontObject(&dcMem, m_fontDefault);

   int nModeOld = dcMem.SetBkMode(TRANSPARENT);

   int crTextColorOldMem = dcMem.SetTextColor(

      TranslateColor(GetForeColor()));



   // Get the text char size for current font

   TEXTMETRIC tm;

   dcMem.GetTextMetrics(&tm);



   int nTextHeight = tm.tmHeight + tm.tmExternalLeading * 2;

   int nAveCharWidth = (int)(tm.tmAveCharWidth * 1.5);



   // Get the bounding rect for the entire string (current font)

   int nCtrlWidth = nAveCharWidth * nMsgLen + (nAveCharWidth / 2);

   int nCtrlHeight = nTextHeight + (nTextHeight / 4);



   // Autosize the control to the text (if needed)

   int nWidth, nHeight;

   GetControlSize(&nWidth, &nHeight);

   CRect rcOld(0, 0, nCtrlWidth, nCtrlHeight);

   CRect rcCurrent(0, 0, nWidth, nHeight);



   if (rcCurrent != rcOld)

      SetControlSize(nCtrlWidth, nCtrlHeight);



   // Prepare a memory bitmap

   CBitmap bmp;

   bmp.CreateCompatibleBitmap(&dc, nCtrlWidth, nCtrlHeight);



   // Select the bitmap

   CBitmap* pbmpOld = dcMem.SelectObject(&bmp);



   // Fill with current background color

   CRect rc(0, 0, nCtrlWidth, nCtrlHeight);

   CBrush brBack(TranslateColor(GetBackColor()));

   dcMem.FillRect(&rc, &brBack);



   // Create a random number generating object

   CRand rand;



   // Jiggle the chars

   for (int i = 0; i < nMsgLen; i++)

   {

      CRect rc;

      UINT cx = rand.MapRand(nAveCharWidth) / 2;

      UINT cy = rand.MapRand(nTextHeight) / 4;



      rc.left   = (nAveCharWidth * i) + cx;

      rc.right  = rc.left + nAveCharWidth + cx;

      rc.top    = cy;

      rc.bottom = rc.top + nTextHeight + cy;



      // Draw next character on the bitmap

      dcMem.DrawText((CString)strText[i], rc, DT_CENTER);

   }



   // Blast bitmapped text to the window

   dc.BitBlt(0, 0, nCtrlWidth, nCtrlHeight, &dcMem, 0, 0, SRCCOPY);



   // Clean up

   dcMem.SelectObject(pbmpOld);

   dcMem.SelectObject(pFontOld);

   dcMem.SetTextColor(crTextColorOldMem);

   dcMem.SetBkMode(TranslateColor(nModeOld));

   bmp.DeleteObject();

}


This code might look somewhat tricky at first glance, but the idea is really quite simple. To make the text jiggle, you get the bounding rectangle of the text using the text metrics of the current font. Then you subdivide this rectangle into smaller rectangles, one for each character in the string. You then draw the text, one character at a time (within a loop) on a memory bitmap, with each character offset by a random amount in the x and y axes.

The memory bitmap is then blasted to the control's client area using the CDC::BitBlt() method. Using BitBlt() instead of drawing directly in the control prevents flashing and makes the jiggling look much smoother. The control's client area is resized automatically to the maximum size of the jiggled text string. This provides the smallest area needed for the BitBlt() and optimizes the speed of the bit block transfer operation.

CAUTION
It's very important to clean up by calling the DeleteObject() method for the GDI bitmap object. If this isn't done, the control will allocate a new bitmap every time the timer fires a WM_TIMER message, and the GDI heap will be quickly exhausted. This will cause unpredictable results, always resulting in the text no longer jiggling, but it could also easily cause a system crash under Windows 95!

The CJigglerPropPage Class

The CJigglerPropPage class provides the user interface needed to control JIGGLER.OCX properties at design time from development environments such as the Visual C++ Developer Studio. The interface for the CJigglerPropPage class is given in Listing 30.7.


Listing 30.7. The interface for the CJigglerPropPage class.
///////////////////////////////////////////////////////////////////

//  Module  : JIGGLERC.CPP

//

//  Purpose : Interface for the CJigglerPropPage property page

//            class.



///////////////////////////////////////////////////////////////////

// The CJigglerPropPage class



class CJigglerPropPage : public COlePropertyPage

{

   DECLARE_DYNCREATE(CJigglerPropPage)

   DECLARE_OLECREATE_EX(CJigglerPropPage)



public:

   CJigglerPropPage();   // Constructor



   // Dialog Data

   enum { IDD = IDD_PROPPAGE_JIGGLER };



   CString   m_Caption;

   int       m_nInterval;



protected:

   // DDX/DDV support

   virtual void DoDataExchange(CDataExchange* pDX);



// Message map

DECLARE_MESSAGE_MAP()

};



///////////////////////////////////////////////////////////////////


This class uses two data members, m_Caption and m_nInterval, to handle the data exchange to the Caption and Interval properties. The complete implementation source code for this class is given in Listing 30.8.


Listing 30.8. The implementation of the CJigglerPropPage class.
///////////////////////////////////////////////////////////////////

//  Module  : JIGGLERP.CPP

//

//  Purpose : Implementation of the CJigglerPropPage property

//            page class.



#include "stdafx.h"

#include "Jiggler.h"

#include "JigglerP.h"



IMPLEMENT_DYNCREATE(CJigglerPropPage, COlePropertyPage)



///////////////////////////////////////////////////////////////////

// Initialize class factory and guid



IMPLEMENT_OLECREATE_EX(CJigglerPropPage,

   "JIGGLER.JigglerPropPage.1",

   0x5cd2fc84, 0xe7bd, 0x11cf, 0xa3, 0xbc, 0x44, 0x45, 0x53,

   0x54, 0, 0)



///////////////////////////////////////////////////////////////////

// CJigglerPropPage::CJigglerPropPageFactory::UpdateRegistry -

// Adds or removes system registry entries for CJigglerPropPage



BOOL CJigglerPropPage::CJigglerPropPageFactory::UpdateRegistry(

   BOOL bRegister)

{

   if (bRegister)

      return AfxOleRegisterPropertyPageClass(

         AfxGetInstanceHandle(), m_clsid, IDS_JIGGLER_PPG);

   else

      return AfxOleUnregisterClass(m_clsid, NULL);

}



///////////////////////////////////////////////////////////////////

// CJigglerPropPage::CJigglerPropPage - Constructor



CJigglerPropPage::CJigglerPropPage() :

   COlePropertyPage(IDD, IDS_JIGGLER_PPG_CAPTION)

{

   m_Caption   = _T("");

   m_nInterval = 100;

}



///////////////////////////////////////////////////////////////////

// CJigglerPropPage::DoDataExchange - Moves data between page

//  and properties



void CJigglerPropPage::DoDataExchange(CDataExchange* pDX)

{

   DDP_Text(pDX, IDC_EDIT_CAPTION, m_Caption, _T("Caption"));

   DDX_Text(pDX, IDC_EDIT_CAPTION, m_Caption);

   DDV_MaxChars(pDX, m_Caption, 50);



   DDP_Text(pDX, IDC_EDIT_INTERVAL, m_nInterval, _T("Interval"));

   DDX_Text(pDX, IDC_EDIT_INTERVAL, m_nInterval);

   DDV_MinMaxInt(pDX, m_nInterval, 10, 1000);



   DDP_PostProcessing(pDX);

}



///////////////////////////////////////////////////////////////////


Testing the Control in the OLE Control Test Container

To test the new functionality of the JIGGLER control, you first drop it into the Test Container application to verify that all the properties are functioning properly and that the behavior of the control is what you expect. The result of this test is shown in Figure 30.8.

Figure 30.8 : The fully implemented JIGGLER OLE control in the Test Container application.

Just to verify that everything works properly at design time in an actual Windows development environment, you dropped JIGGLER.OCX into a new Visual Basic 4 form. The result of this small test can be seen in Figure 30.9.

Figure 30.9 : The JIGGLER ActiveX control in a generic Visual Basic 4 form.

Using the New Control on a Sample Web Page

ActiveX controls are the hot new way to activate the World Wide Web, bringing previously static Web pages to life. Now that the control has been successfully tested in both the Test Container application and a generic VB form, it's time to write a Web page of your own to fully test the control in a Web browser. To "activate" this sample Web page, you'll use three instances of the JIGGLER control and programmatically control them with OLE Automation by using VBScript, Microsoft's trimmed-down version of Visual Basic for the Internet.

NOTE
The source for the sample Web page can be found in the SOURCE\CHAP30\JIGGLER folder on this book's companion CD-ROM as the file JIGGLER3.htm.

Creating a Simple HTML Web Page

Before you can add a control to a Web page, you must first create an HTML document that defines the page. There are several HTML generating utilities and applications available, but you'll start your sample page by using one that comes free with every version of Windows: Notepad. Yes, with the venerable Notepad and a little knowledge of HTML syntax, you can create a simple Web page in no time. The source code for a basic Web page contains just a few simple HTML tags, as shown in Listing 30.9.


Listing 30.9. The HTML source for a minimal Web page.
<HTML>

<HEAD>

<TITLE>Some Title</TITLE>

</HEAD>

<BODY>



</BODY>

</HTML>


Of course, this code is pretty useless, because nothing appears in the browser! You must customize the page by adding some text that describes the new JIGGLER ActiveX control, as shown in Listing 30.10.


Listing 30.10. The modified HTML source for describing the JIGGLER ActiveX control.
<HTML>

<HEAD>

<TITLE>Rob's Jiggler ActiveX Control</TITLE>

</HEAD>

<BODY BGCOLOR="#FFFFFF">



<H2><I>Rob's ActiveX Jiggler Control</I></H2>

<HR>

This is my new JIGGLER ActiveX control!! Pretty cool, eh?

 It was inspired by the "Nervous Text" Java applet, but this was totally

 redesigned from scratch and written in C++ for ActiveX! It supports many

 background and text colors, and it's easily extensible, so go for it!<p>



</BODY>

</HTML>


Note that you've set the BODY tag to include a background color set to white:

<BODY BGCOLOR="#FFFFFF">

When opened in Microsoft's ActiveX-enabled Web browser Internet Explorer 3 (IE3), this code results in a Web page that resembles the one in Figure 30.10.

Figure 30.10 : The basic Web page in Internet Explorer 3.0.

Adding the JIGGLER Control to the Web Page

Now that the basic HTML file is ready, how do you add the ActiveX control to the page to activate it with some exciting jiggling text? The easiest way to add the JIGGLER.OCX to the Web page is with a very useful tool: Microsoft's ActiveX Control Pad. The Control Pad is a free utility designed to insert ActiveX controls into Web pages. This utility also generates basic VBScript and JavaScript source code for the controls on your pages.

NOTE
You can download the ActiveX Control Pad utility from the Web at this URL:
http://www.microsoft.com/workshop/author/cpad/
If Microsoft has moved the utility by the time you read this, just go to www.microsoft.com and search for Control Pad.

To add the JIGGLER control to the Web page, just open the JIGGLER3.htm file in Control Pad and then select the Edit | Insert ActiveX Control menu command. This action activates the Insert ActiveX Controls dialog box (see Figure 30.11).

Figure 30.11 : Inserting the JIGGLER.OCX into an HTML document using the ActiveX Control Pad.

Select JIGGLER ActiveX Control from the list; the Control Pad opens an editing window similar to the one found in Visual Basic (see Figure 30.12). By setting the Caption, ForeColor, and BackColor properties in the Properties dialog box, you can visually set these stock OLE control properties.

Figure 30.12 : The ActiveX Control Pad Properties and Edit ActiveX Control windows.


NOTE
If the JIGGLER control isn't on the Insert Control list, you'll need to manually register the control. You can do so in the Test Container application by choosing the File | Register Controls menu command

Set the Caption property to ActiveX using the property editor. Next, visually set the BackColor property to the color white by double-clicking the BackColor property in the Properties window and choosing the white square from the resulting color-selection dialog box (see Figure 30.13). Set the ForeColor property to the color red using the same technique. The changes are immediately reflected in the Edit ActiveX Control window.

Figure 30.13 : Changing the BackColor property with the ActiveX Control Pad property editor.

When you're finished, close the Properties and Edit ActiveX Control windows. At this point, the Control Pad generates and inserts the code for the JIGGLER control into the HTML document. The code added by Control Pad is shown in Listing 30.11.


Listing 30.11. The basic JIGGLER control source code generated by the Control Pad.
<OBJECT ID="Jiggler1" WIDTH=157 HEIGHT=47

 CLASSID="CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000">

    <PARAM NAME="_Version" VALUE="65536">

    <PARAM NAME="_ExtentX" VALUE="4154">

    <PARAM NAME="_ExtentY" VALUE="1244">

    <PARAM NAME="_StockProps" VALUE="11">

    <PARAM NAME="Caption" VALUE="ActiveX">

    <PARAM NAME="ForeColor" VALUE="255">

    <PARAM NAME="BackColor" VALUE="50331647">

</OBJECT>


This code uses the World Wide Web Consortium (W3C) <OBJECT> HTML tag to describe the ActiveX control. The CLASSID on line 2 gives the JIGGLER control's CLSID, which Control Pad automatically looked up in the registry for you. This is a very convenient feature! Notice that the second line ends the <OBJECT> tag with a closing bracket (>) after the CLSID number. You'll add a new line inside the <OBJECT> tag that specifies the actual URL of the JIGGLER ActiveX control on your Web server.

The CODEBASE keyword lets IE3 find and download the control to a user's machine if the control doesn't exist on that machine already. For your purposes, assume that the control exists in a subdirectory of the directory in which your JIGGLER3.htm file lives. Let's call this directory ocx. This gives you the following relative CODEBASE:

CODEBASE = "./ocx/Jiggler.ocx">

The preceding CODEBASE line now ends the <OBJECT> tag, which gives IE3 everything it needs to know about where to find the control. The <OBJECT> tag is followed by several PARAM NAME lines that define the values for control properties. This change yields the following code, which fully describes the JIGGLER ActiveX object, named Jiggler1, in HTML:

<OBJECT ID="Jiggler1" WIDTH=157 HEIGHT=47

 CLASSID="CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000"

 CODEBASE="./ocx/Jiggler.ocx">

    <PARAM NAME="_Version" VALUE="65536">

    <PARAM NAME="_ExtentX" VALUE="4154">

    <PARAM NAME="_ExtentY" VALUE="1244">

    <PARAM NAME="_StockProps" VALUE="11">

    <PARAM NAME="Caption" VALUE="ActiveX">

    <PARAM NAME="ForeColor" VALUE="255">

    <PARAM NAME="BackColor" VALUE="50331647">

</OBJECT>

By simply copying this object definition and pasting two more copies into the document, you create a total of three JIGGLER controls on the page. Each control must have a unique name, so you must change the names of the pasted controls. Change the name of the second control from Jiggler1 to Jiggler2, and change the name of the third control from Jiggler1 to Jiggler3.

The Control Pad editor window displays buttons in the left margin that you can click to bring up the edit and property windows for each control (see Figure 30.14). Use these buttons to change the Caption and ForeColor properties of the second and third instances of the JIGGLER control. Set the Caption property for Jiggler2 to Text and the ForeColor to the color green (value 65280). Set the Caption property for Jiggler3 to Jiggling and the ForeColor to the color blue (value 16711680). There! A nice red, green, and blue trio of JIGGLER objects.

Figure 30.14 : The Control Pad editor provides edit buttons for each ActiveX control in an HTML document.

The Control Pad makes creating these definitions of your JIGGLER objects easy and convenient, but the objects aren't very flexible on their own. After all, an ActiveX control is, at its core, an OLE Automation server. To take programmatic control of the object in a Web page, you must use a scripting language. The next section uses VBScript for this example.

Programming the JIGGLER Control with VBScript

Now that the HTML code is in place for the control, you can write some simple VBScript code to set JIGGLER property values when the document is loaded into the IE3 browser window. Much more can be done with VBScript, but let's keep it simple.

NOTE
For the full documentation on VBScript, visit the Microsoft Web site. While you're there, check out the cool VBScript code samples. You can find this stuff at the following URL:
http://www.microsoft.com/vbscrip

To begin, you want to change the Interval property for each control, and you also want to change the DefaultFont properties for each control. This should be done when the page is first loaded into a browser. By selecting the Tools | ScriptWizard menu command in the Control Pad, you can generate a default script with the proper syntax already in place. To get an idea of how this tool works, follow these steps:

  1. Select the onLoad event for the window object in the left pane, highlighting it in the tree control.
  2. Select the Interval property for the Jiggler1 control in the right pane, highlighting it in the tree control.
  3. Click the Insert Action button and enter the number 50 in the resulting input dialog box.

These steps result in the Script Wizard associating the value of 50 with the Interval property of the Jiggler1 object when the page is loaded, as you can see in Figure 30.15.

Figure 30.15 : Defining a basic script in the Control Pad Script Wizard.

Click OK at this point to generate the following VBScript code:

<SCRIPT LANGUAGE="VBScript">

<!--

Sub window_onLoad()

   Jiggler1.Interval = 50

end sub

-->

</SCRIPT>

Because the DefaultFont property uses the IFontDispatch interface, you can use the OLE Automation properties presented earlier in Table 30.3 to programmatically set font properties. The font properties you'll set are all IFontDispatch interface subproperties of the DefaultFont property, including Name, Size, Italic, and Bold. The following is an example using these properties in VBScript code:

Jiggler1.DefaultFont.Name   = "Times New Roman"

Jiggler1.DefaultFont.Size   = 20

Jiggler1.DefaultFont.Italic = True

Jiggler1.DefaultFont.Bold   = True

As the final step, duplicate the VBScript code for the other two JIGGLER object instances, changing the names to Jiggler2 and Jiggler3 (of course). This gives you the final HTML source code found in Listing 30.12. You're finished! The JIGGLER3.htm Web page is ready to be loaded into IE3, and the three JIGGLER objects will jiggle away until the cows come home.


Listing 30.12. The complete source code for the final version of JIGGLER3.htm.
<HTML>

<HEAD>

<TITLE>Rob's Jiggler ActiveX Control</TITLE>

</HEAD>



<BODY BGCOLOR="#FFFFFF">



<H2><I>Rob's Jiggler ActiveX Control</I></H2>

<HR>

This is my new JIGGLER ActiveX control!! Pretty cool, eh?

 It was inspired by the "Nervous Text" Java applet, but this was totally

 redesigned from scratch and written in C++ for ActiveX! It supports many

 background and text colors, and it's easily extensible, so go for it!

<P>



<!-- Create 3 Jiggler ActiveX controls -->



<OBJECT

   ID       = "Jiggler1"

   WIDTH    = 0

   HEIGHT   = 0

   CLASSID  = "CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000"

   CODEBASE = "./ocx/Jiggler.ocx">



   <PARAM NAME = "_Version"    VALUE = "65536">

   <PARAM NAME = "_ExtentX"    VALUE = "11933">

   <PARAM NAME = "_ExtentY"    VALUE = "1244">

   <PARAM NAME = "_StockProps" VALUE = "11">

   <PARAM NAME = "Caption"     VALUE = "ActiveX">

   <PARAM NAME = "ForeColor"   VALUE = "255">

   <PARAM NAME = "BackColor"   VALUE = "16777215">

</OBJECT>



<OBJECT

   ID       = "Jiggler2"

   WIDTH    = 0

   HEIGHT   = 0

   CLASSID  = "CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000"

   CODEBASE = "./ocx/Jiggler.ocx">



   <PARAM NAME = "_Version"    VALUE = "65536">

   <PARAM NAME = "_ExtentX"    VALUE = "11933">

   <PARAM NAME = "_ExtentY"    VALUE = "1244">

   <PARAM NAME = "_StockProps" VALUE = "11">

   <PARAM NAME = "Caption"     VALUE = "Jiggling">

   <PARAM NAME = "ForeColor"   VALUE = "65280">

   <PARAM NAME = "BackColor"   VALUE = "16777215">

</OBJECT>



<OBJECT

   ID       = "Jiggler3"

   WIDTH    = 0

   HEIGHT   = 0

   CLASSID  = "CLSID:5CD2FC83-E7BD-11CF-A3BC-444553540000"

   CODEBASE = "./ocx/Jiggler.ocx">



   <PARAM NAME = "_Version"    VALUE = "65536">

   <PARAM NAME = "_ExtentX"    VALUE = "11933">

   <PARAM NAME = "_ExtentY"    VALUE = "1244">

   <PARAM NAME = "_StockProps" VALUE = "11">

   <PARAM NAME = "Caption"     VALUE = "Text">

   <PARAM NAME = "ForeColor"   VALUE = "16711680">

   <PARAM NAME = "BackColor"   VALUE = "16777215">

</OBJECT>



<P>



<!-- Program the controls with OLE Automation via VBScript -->



<SCRIPT LANGUAGE="VBScript">

<!--

Sub window_onLoad()



   rem Set the properties for Jiggler1



   Jiggler1.Interval           = 100

   Jiggler1.DefaultFont.Name   = "Times New Roman"

   Jiggler1.DefaultFont.Size   = 20

   Jiggler1.DefaultFont.Italic = True

   Jiggler1.DefaultFont.Bold   = True



   rem Set the properties for Jiggler2



   Jiggler2.Interval           = 75

   Jiggler2.DefaultFont.Name   = "Arial"

   Jiggler2.DefaultFont.Size   = 24

   Jiggler2.DefaultFont.Italic = True

   Jiggler2.DefaultFont.Bold   = True



   rem Set the properties for Jiggler3



   Jiggler3.Interval           = 50

   Jiggler3.DefaultFont.Name   = "Courier New"

   Jiggler3.DefaultFont.Size   = 30

   Jiggler3.DefaultFont.Italic = True

   Jiggler3.DefaultFont.Bold   = True



end sub

-->

</SCRIPT>



</BODY>

</HTML>


Figure 30.16 shows the end result of all your hard work, with the JIGGLER3.htm file loaded in the IE3 browser window. (Take my word for it; the control text is red, green, and blue.)

Figure 30.16 : The final JIGGLER3.htm Web page, complete with three JIGGLE ActiveX controls using different fonts, colors, and jiggle intervals.

Summary

ActiveX controls are at the forefront of new technology that's activating the Internet. These new controls are fun and exciting and, they bring the Web to life. Visual C++ 4.2 fully supports ActiveX controls and makes creating them fairly easy, thanks to the updated OLE Control Wizard.

Here are some points to remember: