Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft EdgeMore info about Internet Explorer and Microsoft Edge
Table of contentsExit editor mode

Using ActiveX Controls to Automate Your Web Pages

Feedback

In this article

This article describes how a developer can add Microsoft ActiveX support to an existing OLE Automation control.

  • Introduction
  • Prerequisites
  • Document Roadmap
  • What Is an ActiveX Control?
    • Description of an OLE Control
    • Description of an ActiveX Control
  • Porting the Timer Control
    • Modifying the OnDraw Function
    • Adding Support for Color and Font Properties
    • Adding Support for the Alarm (Properties and Resource)
    • Adding Support for Safety
    • Adding Support for Run-Time Licensing
    • Reducing a Control's Footprint
    • Creating a Test Certificate for an ActiveX Control

Introduction

An ActiveX control is an object that supports a customizable, programmatic interface. Using the methods, events, and properties exposed by a control, Web authors can automate their HTML pages. Examples of ActiveX Controls include text boxes, command buttons, audio players, video players, stock tickers, and so on.

You can develop ActiveX Controls using Microsoft Visual Basic, Microsoft Visual C++, and Java. This article focuses on controls developed with Visual C++. (For more information about developing controls with Visual Basic, seeVisual Basic; for more information about developing controls with Java, seeJava SE Desktop Technologies (JavaBeans)).

Because ActiveX Controls are complex, Microsoft offers some tools that help a C++ developer create an ActiveX control. The following table describes these tools.

ToolDescriptionShips with
Microsoft Foundation Classes (MFC)A set of C++ classes that support Component Object Model (COM), OLE, and ActiveX (among other things). MFC provides the simplest means of creating ActiveX Controls.Visual C++ version 4.2 or later. (MFC ships with earlier versions of Visual C++; however, these versions do not support ActiveX.)
Microsoft ActiveX Template LibraryA set of C++ templates designed to create small and fast COM objects.Active Template Library (ATL) 2.1 ships with Visual C++ version 5.0. (ATL 2.0 is a Web release that relies on the Visual C++ 4.2 IDE.)

 

This tutorial focuses on controls developed with MFC. It demonstrates how you can convert an existing OLE control into an ActiveX control. The tutorial is based on the sample MFC timer control that ships with Visual C++ version 4.2 and later. The timer control supports a single event and a single property: It fires a Timer event at a user-specified interval (which corresponds to the control's Interval property). For the purpose of this tutorial, the timer control was converted into a digital alarm clock (which can be inserted into an HTML page using theobject object).

The modifications to the sample timer control included:

  • Overriding theCOleControl::OnDraw member function so that the control renders current time.
  • Adding support for stock font, foreground color, and background color properties.
  • Adding support for the alarm's hour and minute properties.
  • Identifying the control as safe for scripting.
  • Adding support for run-time licensing.
  • Packaging the control for efficient download on the Internet.

Prerequisites

To benefit from this tutorial, you should be familiar with COM (the OLE Component Object Model) and OLE Automation; in addition, you should know C++ and be familiar with the MFC.

Document Roadmap

This document contains the following sections.

SectionDescription
What Is an ActiveX Control?Reviews the parts of an OLE control; in addition, it describes the new features that differentiate an ActiveX control from an OLE control. This section includes descriptions of topics such as security, run-time licensing, and packaging.
Porting the Timer ControlDescribes the work required to convert the sample MFC timer control into a digital clock (with alarm functionality). It includes information about modifying the MFC OnDraw function, adding support for stock properties, adding support for custom properties, adding support for security, and so on.

 

What Is an ActiveX Control?

An ActiveX control is an OLE control that supports additional ActiveX features. This section reviews the architecture of an OLE control and then describes those additional ActiveX features (such as safety, run-time licensing, digital certificates, packaging the control, and so on).

Description of an OLE Control

At a minimum, an OLE control is a COM object that supports theIUnknown andIClassFactory (orIClassFactory2) interfaces. As stated, support for these interfaces is the minimal requirement for an OLE control; however, in order to do any meaningful work, an OLE control also supports a number of other interfaces that provide features such as writing persistent data to disk, supporting automation (methods, events, and properties), and supporting a user interface for the control.

The following table describes the interfaces that can be supported by an OLE control.

InterfaceDescription
IOleObjectSupports communication between container and the control.
IOleInPlaceObjectSupports in-place activation.
IOleControlSupports mnemonics and ambient properties.
IDataObjectSupports data transfer (emulates DDE and the Windows clipboard).
IViewObject2Supports functions that a container uses to render the control.
IDispatchSupports a control's methods and properties.
IConnectionPointContainerSupports a control's events or property-change notifications.
IProvideClassInfo2Supports functions that a container calls in order to obtain a pointer to the control's type information.
ISpecifyPropertyPagesSupports functions that a container calls to determine the extent of a control's property-page support.
IPerPropertyBrowsingSupports functions that allow containers to retrieve individual control properties.
IPersist* ObjectsIncludes six interfaces that support functions that enable a control to read or write its persistent data to storage, stream, or file.
IOleCache2Supports the functions that a container calls in order to cache a control's data.
IExternalConnectionSupports functions that a control uses to track external connections.
IRunnableObjectSupports functions that a container uses to determine whether a control differentiates between a "loaded" and a "running" state.

 

As the previous list notes, theIPersist* interfaces support functions that allow a control to read or write its persistent data to storage, stream, or file. The following list identifies these interfaces and their purpose.

InterfacePurpose
IPersistMemorySaves and loads the control's data into a fixed-length sequential byte array (in memory).
IPersistStorageSaves and loads the control's data into anIStorage instance. Controls that want to be marked "Insertable" as other compound document objects (for insertion into non-control-aware containers)must support this interface.
IPersistPropertyBagSaves and loads the control's data as individual properties written toIPropertyBag, which the container implements. This is used for "Save As Text" functionality in some containers.
IPersistMonikerSaves and loads the control's data to a location named by a moniker. The control callsIMoniker::BindToStorage to retrieve the storage interface it requires, such asIStorage,IStream,ILockBytes,IDataObject, and so on.

 

For more information about any of these interfaces, refer to the OLE core documentation that ships with the Windows Software Development Kit (SDK).

Description of an ActiveX Control

An ActiveX control is an OLE control that supports several additional features. These features include:

  • Initialization security
  • Scripting security
  • Run-time licensing
  • Reduced footprint (for quick downloading)
  • Digital certification

Initialization security

When a control is initialized, it can receive data from an arbitraryIPersist* interface (from either a local or a remote URL) for initializing its state. This is a potential security hazard because the data could come from an untrusted source. Controls that guarantee no security breach regardless of the data source are considered safe for initialization.

There are two methods for indicating that your control is safe for initialization. The first method uses the Component Categories Manager to create the appropriate entries in the system registry. Windows Internet Explorer examines the registry prior to loading your control to determine whether these entries appear. The second method implements an interface namedIObjectSafety on your control. If Internet Explorer determines that your control supportsIObjectSafety, it calls theIObjectSafety::SetInterfaceSafetyOptions method prior to loading your control in order to determine whether your control is safe for initialization.

For more information about identifying a control as safe for initialization using the Component Categories Manager, seeUsing the Component Categories Manager. For more information about identifying a control as safe for initialization usingIObjectSafety, seeSupporting the IObjectSafety Interface.

Scripting security

Code signing can guarantee a user that code is trusted. However, allowing ActiveX Controls to be accessed from scripts raises several new security issues. Even if a control is known to be safe in the hands of a user, it is not necessarily safe when automated by an untrusted script. For example, Microsoft Word is a trusted tool from a reputable source, but a malicious script can use its automation model to delete files on the user's computer, install macro viruses, and worse.

Just as there are two methods for indicating that your control is safe for initialization, so there are two methods for identifying a control as safe for scripting. The first method uses the Component Categories Manager to create the appropriate entries in the system registry (when your control is loaded). Microsoft Internet Explorer 3.0 and later examines the registry prior to loading your control to determine whether these entries appear. The second method implements theIObjectSafety interface on your control. If Internet Explorer determines that your control supportsIObjectSafety, it calls theIObjectSafety::SetInterfaceSafetyOptions method prior to loading your control in order to determine whether your control is safe for scripting.

For more information about identifying a control as safe for scripting using the Component Categories Manager, seeUsing the Component Categories Manager. For more information about identifying a control as safe for scripting usingIObjectSafety, seeSupporting the IObjectSafety Interface.

Run-time licensing

Most ActiveX Controls should support design-time licensing and run-time licensing. (The exception is the control that is distributed free of charge.) Design-time licensing ensures that a developer is building his or her application or Web page with a legally purchased control; run-time licensing ensures that a user is running an application or displaying a Web page that contains a legally purchased control.

Design-time licensing is verified by control containers such as Visual Basic, Microsoft Access, or Microsoft Visual InterDev. Before these containers allow a developer to place a control on a form or Web page, they first verify that the control is licensed by the developer or content creator. These containers verify that a control is licensed by calling certain functions in the control: If the license is verified, the developer can add it.

Run-time licensing is also an issue for these containers (which are sometimes bundled as part of the final application); the containers again call functions in the control to validate the license that was embedded at design time.

Microsoft Internet Explorer is another type of container. Like the other containers, Microsoft Internet Explorer 4.0 and later also calls functions in the control to verify that it is licensed. However, unlike Visual Basic or Microsoft Access, which embed ActiveX Controls within the binary code of their application's executable files, Internet Explorer 4.0 and later uses a different model. This unique model is a necessary response due to:

  • The openness of the browser (the ability to view the HTML source file).
  • Client/server issues imposed by the Internet (or corporate intranet).

Because any user with Internet Explorer 4.0 or later can view the HTML source code for a given Web page, and because an ActiveX control is copied to a user's computer before it is displayed, a level of indirection is required to "hide" the license for an ActiveX control from the user. This prevents users from pirating and reusing controls that they did not purchase. Microsoft addresses these run-time licensing issues with a new file called the license package file (or LPK file). This file can be included in any HTML page by using theobject object. For more information about .lpk files, their use, and their format, seeThe License Package File.

The functions that support run-time licensing are members of theIClassFactory2 interface. Any ActiveX control that supports run-time licensing must support and implement this interface (an extension of theIClassFactory interface).

For more detailed information about implementing run-time licensing for an ActiveX control, seeRun-Time Licensing.

Reduced footprint

Microsoft supports a data-compression technology and associated toolset that you can use to package your ActiveX control for faster, more efficient downloading over the Internet or an intranet. (This same technology and toolset can be applied to Microsoft Win32 applications, Java classes, and Java libraries.)

Cabinet files

For a number of years, Microsoft used cabinet (.cab) files to compress software that was distributed on disks. Originally, these files were used to minimize the number of disks shipped with packaged product; today, .cab files are used to reduce the file size and the associated download time for Web content that is found on the Internet or corporate intranet servers.

The cabinet file format

The .cab file format is a nonproprietary compression format, also known as MSZIP, that is based on the Lempel-Ziv data-compression algorithm.

For information about Cabarc.exe, the tool you can use to compress your ActiveX control, seeCabarc.exe.

For a description of the HTML tags required to access a control contained in a cabinet file, seeAccessing Controls Stored in Cabinet Files.

Digital certification

When a user's security setting is at the default level for Internet Explorer 4.0 and later, any object identified by theobject object on an HTML page must be digitally signed. Digital signatures are created usingSigning Code with Microsoft Authenticode Technology, a set of developer tools that can be downloaded from MSDN Online. A digital signature associates a software vendor's name and a unique public key with a file that contains an ActiveX object (ensuring some sort of accountability on the part of the object's developer).

Before you purchase a certificate for your control's .cab file from a vendor, you can use the test certificate provided by Microsoft for verification purposes.

For a description of how you can add a digital certificate to your control's .cab file, seeSigning Code with Microsoft Authenticode Technology.

Porting the Timer Control

The original OLE timer control was an ideal starting point for a digital clock example: existing code was already in place to handle the firing of an event at a user-specified interval. The remaining work required the addition of:

  • Drawing code (which retrieves the current system time and then renders it within the control's real estate).
  • Stock properties (which let the user customize the appearance of the clock's output).
  • Custom properties (which let the user set the clock's alarm).
  • Support for safety (required by Internet Explorer).
  • Support for run-time licensing (required by Internet Explorer).
  • Packaging into a .cab file (required for efficient downloads).

The following sections describe how clock-like features were added to the timer control.

Modifying the OnDraw Function

As noted earlier in the Description of an OLE Control section,IViewObject is one of the interfaces commonly supported by an OLE control. This interface supports functions that a container calls to draw a control, to retrieve the logical palette used by a control, and to temporarily freeze the current view of the control.

MFC wraps theIViewObject functions in theCOleControl class. These functions are defined in the Ctlview.cpp file. TheCOleControl::XViewObject::Draw function contains most of the functionality typically found in an implementation ofIViewObject::Draw; this functionality includes:

  • Checking for optimized drawing.
  • Saving the DC state.
  • Switching to MM_TEXT mode.
  • Drawing the control.

To draw the control,COleControl::XViewObject::Draw calls theCOleControl::DrawContent member function, which is found in Ctlcore.cpp. This function maps logical coordinates into device coordinates. Once the coordinates are converted,COleControl::DrawContent callsCOleControl::OnDraw to actually render the control.COleControl::OnDraw must be overridden by the control developer in the source code, which is generated by the OLE Control wizard. The default version ofCOleControl::OnDraw simply draws an ellipse. The following excerpt shows the code provided by the OLE Control wizard.

/////////////////////////////////////////////////////////////////////////////// CTimeCtrl::OnDraw - Drawing functionvoid CTimeCtrl::OnDraw(            CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid){    // TODO: Replace the following code with your own drawing code.    pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));    pdc->Ellipse(rcBounds);}

Most of the code required to convert the sample timer control into a digital clock is actually found in theCOleControl::OnDraw function. This includes code that:

  • Sets the user's requested foreground color, background color, and font.
  • Sets the background mode to transparent for drawing.
  • Paints the control's background using the background color.
  • Retrieves the current system time.
  • Compares the current time to the user's requested alarm time.
  • Sounds the alarm if necessary.
  • Packages the time for rendering.
  • Renders the string containing the time.
  • Reselects the original font and brush into the device context.

The entireCOleControl::OnDraw function from the Timectl.cpp file appears below.

#include <strsafe.h> // StringCbPrintf/////////////////////////////////////////////////////////////////////////////// CTimeCtrl::OnDraw - Drawing functionvoid CTimeCtrl::OnDraw(            CDC* pdc, const CRect& rcBounds, const CRect&){    // Variables required for text output.    CFont* pOldFont;    TEXTMETRIC tm;    char tText[30];    CString strTime;    CRect rc = rcBounds;    CBrush bkBrush(TranslateColor(GetBackColor()));    // Variables required for alarm handler.    time_t aclock;     // elapsed time in seconds since 00:00:00 on                       // 1/1/1970    struct tm *now;    // time structure (contains useful time data)    BOOL bTest;    // Set the foreground color property and the transparent bk mode.    pdc->SetTextColor(TranslateColor(GetForeColor()));    pdc->SetBkMode(TRANSPARENT);    // Paint the background using the BackColor property.    pdc->FillRect(rcBounds, &bkBrush);    // Retrieve the time, check the alarm, package a string    // specifying the time.    time(&aclock);    // retrieve elapsed time in seconds    now = localtime(&aclock);    // convert elapsed seconds to                                 // meaningful data    // Examine the alarm properties.    if ((now->tm_hour == m_alarmHour) && (now->tm_min == m_alarmMinute))        if (bAlarm == TRUE)    {        bTest = PlaySound((const char*)IDR_WAVE3, AfxGetResourceHandle(),                SND_RESOURCE);        bAlarm = FALSE;    }    // Package time for rendering.    StringCbPrintf (tText, sizeof(tText), "Current Time: %.8s\n", asctime(now)+11);    strTime = tText;    // Draw the time string.    pOldFont = SelectStockFont(pdc);    pdc->GetTextMetrics(&tm);    pdc->SetTextAlign(TA_CENTER | TA_TOP);    pdc->ExtTextOut((rc.left + rc.right) / 2, (rc.top + rc.bottom -        tm.tmHeight) / 2, ETO_CLIPPED, rc, strTime, (strTime.GetLength())        -1, NULL);    // Reselect the old font and brush.    pdc->SelectObject(pOldFont);}

Adding Support for Color and Font Properties

As noted in the previous section that describes theCOleControl::OnDraw function, the current time is rendered by using the background color, foreground color, and font properties that the user selected. These properties are three of the nine stock properties supported by theCOleControl class. The following list identifies the nine supported stock properties:

  • Appearance
  • BackColor
  • BorderStyle
  • Caption
  • Enabled
  • Font
  • ForeColor
  • hWnd
  • Text

Support for the three stock properties was added using the ClassWizard. For example, support for the background color property was added through the following steps when the control's project was open in Visual C++:

  1. Select ClassWizard from the View menu.
  2. Select the OLE Automation tab.
  3. Select the control's class from the Class Name box.
  4. Select the Add Property button.
  5. Select the BackColor property in the External Name box.
  6. Select the OK button to close the Add Property dialog box.
  7. Select the OK button to confirm choices and close the ClassWizard.

When support for a stock property is added using the ClassWizard, the control's implementation file (Timectl.cpp in the case of the sample) and the control's object description (.odl) file (Timectl.odl) are updated. In the case of the sample control's implementation file, the following line was added to the control's dispatch map:

DISP_STOCKPROP_BACKCOLOR()

In the case of the sample control's object description file, the following line was added:

[id(DISPID_BACKCOLOR), bindable, requestedit] OLE_COLOR BackColor;

After support for the stock properties was added to the sample control, these properties were available for drawing operations. The background color was retrieved by calling theGetBackColor function and applying the result to a background brush variable (bkBrush) as follows:

CBrush bkBrush(TranslateColor(GetBackColor()));

Adding Support for the Alarm (Properties and Resource)

As noted in the Adding Support for Color and Font Properties section,COleControl supports nine stock properties that are common to most ActiveX Controls. However, invariably a control requires custom properties such as the Alarm properties that are supported by the digital clock control.

There are four types of custom properties: member variables, member variables with notification, get/set methods, and parameterized. The sample digital-clock control supports three custom properties (each of which falls into the category of member variables with notification). These properties are the AlarmHour, AlarmMinute, and Interval. The AlarmHour property specifies the hour at which the alarm should trigger; the AlarmMinute property specifies the minute at which the alarm should trigger; and the Interval property specifies the interval (in milliseconds) for updating the clock that's displayed by the control. When the user resets any of these custom properties, a notification function is called to handle any work necessitated by those changes.

The MFC framework supportsCOleControl::DoPropExchange to load or store persistent data for a control. This function normally makes calls to thePX_ family of functions to load or store specific user-defined properties. The sample control takes advantage of the MFC property-exchange functionality to save and load the custom interval and alarm properties.

/////////////////////////////////////////////////////////////////////////////// CTimeCtrl::DoPropExchange - Persistence supportvoid CTimeCtrl::DoPropExchange(CPropExchange* pPX){    ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));    COleControl::DoPropExchange(pPX);    long nInterval = m_interval;    short sAlarmHr = m_alarmHour;    short sAlarmMin = m_alarmMinute;    PX_Long(pPX, _T("Interval"), m_interval, DEFAULT_INTERVAL);    if (pPX->IsLoading())    {        if (nInterval != m_interval)            OnIntervalChanged();    }    PX_Short(pPX, _T("AlarmHour"), m_alarmHour, DEFAULT_HOUR);    if (pPX->IsLoading())    {        if (sAlarmHr != m_alarmHour)            OnAlarmHourChanged();    }    PX_Short(pPX, _T("AlarmMinute"), m_alarmMinute, DEFAULT_MINUTE);    if (pPX->IsLoading())    {        if (sAlarmMin != m_alarmMinute)            OnAlarmMinuteChanged();    }}

In the case of the AlarmHour property, when theOnAlarmHourChanged function is called, a global Boolean variable (bAlarm) is set to TRUE, and theCOleControl::SetModifiedFlag function is called to notify the container that the control's persistent state has changed.

void CTimeCtrl::OnAlarmHourChanged(){    bAlarm = TRUE;    SetModifiedFlag(TRUE);}

The global variable (bAlarm) is examined within theCOleControl::OnDraw function to ensure that the alarm should be triggered at the specified time.

// Examine the alarm properties.    if ((now->tm_hour == m_alarmHour) && (now->tm_min ==        m_alarmMinute))        if (bAlarm == TRUE)    {        bTest = PlaySound((const char*)IDR_WAVE3, AfxGetResourceHandle(),            SND_RESOURCE);        bAlarm = FALSE;    }

As this code excerpt shows, the alarm sounds when thePlaySound function is called. This function takes three arguments: the first (IDR_WAVE3) is a resource identifier; the second is an instance handle for the control's resources; and the third is a flag specifying that the sound is a resource.

The current alarm sound (a ringing phone) was inserted into the sample control's DLL to ensure that the alarm would activate on any system that supported audio output. This .wav file was inserted using the Insert Resource dialog box.

Adding Support for Safety

The sample control uses the Component Categories Manager to identify its safety features. For sample code demonstrating how this support was implemented, seeRegistering a Control as Safe.

Adding Support for Run-Time Licensing

The sample control supports run-time licensing for most containers through theIClassFactory2 interface. However, when Internet Explorer is the container, it's also necessary to generate an .lpk file as part of the run-time licensing scheme.

For more information about run-time licensing and .lpk files, seeRun-Time Licensing for Internet Explorer 4.0 and later.

For more information about using LPKTool.exe to create an .lpk file for a control, seeAppendix A: Generating a License Package with LPK_TOOL.

Reducing a Control's Footprint

The sample control was compressed using Cabarc.exe, a tool provided as part of the Internet Client SDK. For a description of this process, seePackaging the Sample ActiveX Control.

Creating a Test Certificate for an ActiveX Control

For testing purposes, a digital certificate was added to the sample control's .cab file using several tools shipped with the Internet Client SDK. For a description of this process, seeSigning the .cab File.


  • Last updated on

In this article