Internet First Steps: ActiveX Controls

OverviewHow Do I

To be an ActiveX control, your control must be a COM object, export DLLRegisterServer and DLLUnRegisterServer, and implement the IUnknown interface.

ActiveX controls are not limited to the Internet. An ActiveX control can also be used in any container, as long as the control supports the interfaces required by that container. Topics included in this article that describe how to create ActiveX controls for the Internet are:

  • Making Your Existing Controls Internet-Friendly

  • How Do I Create a New ActiveX Control?

  • How Do I Update an Existing OLE Control to Use New ActiveX Control Features?

  • Displaying a Control on a Web Page

  • How Do I Decide Whether to Derive from CDataPathProperty or CCachedDataPathProperty?

Making Your Existing Controls Internet-Friendly

You can use your existing controls on the Internet. However, there are steps you should take to make your code size smaller and to make your control properties download asynchronously.

The following tips will make your applications run better on the Internet.

  • Be asynchronous; don't hold up other programs.

  • Download code and properties in the background.

  • Download data in small blocks.

  • Become user-interface active as quickly as possible.

  • Write efficient routines to keep code size and run time down.

For more information on increasing your control's performance, see ActiveX Controls: Optimization.

Creating a New ActiveX Control

When creating a new control using AppWizard, you can choose to enable support for asynchronous monikers as well as other optimizations. To add support to download control properties asynchronously, follow these steps:

To create your project using the MFC ActiveX ControlWizard

  1. Select New on the File menu.

  2. Click the Projects tab, select MFC ActiveX ControlWizard, and name your project.

  3. In Step 2 of the MFC ActiveX ControlWizard, click the Advanced button.

    In the Advanced ActiveX Features dialog box, select Loads Properties Asynchronously. Selecting this option sets up the ready state property and the ready state changed event for you.

    You can also select other optimizations, such as Windowless Activation, which is described in ActiveX Controls: Optimization.

  4. In the Advanced ActiveX Features dialog box, click OK.

  5. Choose Finish to create the project.

To create a class derived from CDataPathProperty

  1. Create a class derived from CDataPathProperty.

  2. In each of your source files that includes the header file for your control, add the header file for this class before it.

  3. In this class, override OnDataAvailable. This function is called whenever data is available for display. As data becomes available, you can handle it any way you choose, for example by progressively rendering it.

    The code excerpt below is a simple example of progressively displaying data in an edit control. Note the use of flag BSCF_FIRSTDATANOTIFICATION to clear the edit control.

    void CMyDataPathProperty::OnDataAvailable(DWORD dwSize, DWORD bscfFlag)
    {
            CListCtrl list_ctrl;
            CEdit* edit=list_ctrl.GetEditControl();
            if (bscfFlag & BSCF_FIRSTDATANOTIFICATION && edit->m_hWnd)
            {
                edit->SetSel(0, -1);
                edit->Clear();
            }
            if (!dwSize)
                return;
    
        CString string;
        LPTSTR str=string.GetBuffer(dwSize);
        UINT nBytesRead=Read(str, dwSize);
        if (!nBytesRead)
                return;
            string.ReleaseBuffer(nBytesRead);
            edit->SetSel(-1, -1);
            edit->ReplaceSel(string);
    }
    

    Note that you must include AFXCMN.H to use the CListCtrl class.

  4. When your control's overall state changes (for example, from loading to initialized or user interactive), call  COleControl::InternalSetReadyState. If your control has only one data path property, you can add code on BSCF_LASTDATANOTIFICATION to notify the container that your download is complete. For example:

    if (bscfFlag == BSCF_LASTDATANOTIFICATION)
    {
        GetControl()->InternalSetReadyState(READYSTATE_COMPLETE);
    }
    
  5. Override OnProgress. In OnProgress, you are passed a number showing the maximum range and a number showing how far along the current download is. You can use these numbers to display status such as percent complete to the user.

In the next procedure, you will be adding a property to your control to use the class you just derived.

To add a property using ClassWizard

  1. In the MFC ClassWizard dialog box, click the Automation tab and make sure your control class (the class derived from COleControl) is selected.

  2. Choose Add Property, and in the Add Property dialog box select Get/Set methods.

  3. Type a variable name in the External name box, for example, EditControlText, and select BSTR as its type.

    This adds the member variable, modifies the IDL, and adds the dispatch map and Get/Set functions.

  4. Declare a member variable of your CDataPathProperty derived class to your ActiveX control class.

    CMyDataPathProperty EditControlText;
    
  5. Implement the Get/Set member functions. For the Get function, return the string. For Set, load the property and call SetModifiedFlag.

    BSTR CDataPathCtrl::GetDataPath()
    {
        CString strResult;
        strResult = EditControlText.GetPath();
        return strResult.AllocSysString();
    }
    void CDataPathCtrl::SetDataPath(LPCTSTR lpszNewValue)
    {
        Load(lpszNewValue, EditControlText);
        SetModifiedFlag();
    }
    
  6. In , add the following line:

    PX_DataPath(pPX, _T("DataPath"), EditControlText);
    
  7. Override to notify the property to reset its control by adding this line:

    EditControlText.ResetData();
    

Deciding Whether to Derive from CDataPathProperty or CCachedDataPathProperty

The previous example describes steps for deriving your control's property from CDataPathProperty. This is a good choice if you are downloading real-time data that frequently changes, and for which you do not need to keep all the data, but only the current value. An example is a stock ticker control.

You can also derive from CCachedDataPathProperty. In this case, the downloaded data is cached in a memory file. This is a good choice if you need to keep all the downloaded data — for example, a control that progressively renders a bitmap. In this case, the class has a member variable containing your data:

    CMemFile m_Cache;

In your ActiveX control class, you can use this memory mapped file in OnDraw to display the data. In your ActiveX control CCachedDataPathProperty derived class, override the member function OnDataAvailable and invalidate the control, after calling the base class implementation.

COleControl OLEctrl;

void CMyCachedDataPathProperty::OnDataAvailable(DWORD dwSize, DWORD bscfFlag)
{
    CCachedDataPathProperty:OnDataAvailable(dwSize, bscfFlag);
    OLEctrl.InvalidateControl(NULL);
}

Downloading Data Asynchronously Using ActiveX Controls

Downloading data over a network should be done asynchronously. The advantage of doing so is that if a large amount of data is transferred or if the connection is slow, the download process will not block other processes on the client.

Asynchronous monikers provide a way to download data asynchronously over a network. A Read operation on an Asynchronous moniker returns immediately, even if the operation has not been completed.

For example, if only 10 bytes are available and Read is called asynchronously on a 1K file, Read does not block, but returns with the currently available 10 bytes.

You implement asynchronous monikers using the CAsyncMonikerFile class. However, ActiveX controls can use the CDataPathProperty class, which is derived from CAsyncMonikerFile, to help implement asynchronous control properties.

The ASYNDOWN sample demonstrates how to set up an asynchronous loop using timers to read the data. ASYNDOWN is described in detail in the KB article "FILE: AsyncDown Demonstrates Asynchronous Data Download" (006415), and is available for download from the Microsoft Software Library. (For more information about downloading files from the Microsoft Software Library, please see the article How to Obtain Microsoft Support Files from Online Services (Q119591) in the Microsoft Knowledge Base.)

The basic technique used in ASYNDOWN is to set a timer in CDataPathProperty::OnDataAvailable to indicate when data is available. When the timer message is received, the application reads in 128-byte blocks of data and fills an edit control. If data is not available when the timer message is handled, the timer is turned off. OnDataAvailable turns on the timer if more data arrives later.

Displaying a Control on a Web Page

Here is an example of an object tag and attributes for inserting a control on a Web page.

<OBJECT
CLASSID="clsid:FC25B780-75BE-11CF-8B01-444553540000"
CODEBASE="/ie/download/activex/iechart.ocx"
ID=chart1
WIDTH=400
HEIGHT=200
ALIGN=center
HSPACE=0
VSPACE=0
>
<PARAM NAME="BackColor" value="#ffffff">
<PARAM NAME="ForeColor" value="#0000ff">
<PARAM NAME="url" VALUE="/ie/controls/chart/mychart.txt">
</OBJECT>

A W3C Working Draft is currently under review for the object tag. See the draft for details of object tags and attributes. A list of current W3C working drafts, including this one, can be found at:

http://www.w3.org/pub/WWW/TR

See Also   Internet: Where Is..., ActiveX Controls on the Internet

Updating an Existing OLE Control to Use New ActiveX Control Features

If your OLE control was created with a version of Visual C++ prior to 4.2, there are steps you can take to improve its performance and enhance its functionality. For a detailed discussion of these changes, see ActiveX Controls: Optimization.

If you are adding asynchronous property support to an existing control, you will need to add the ready state property and the ReadyStateChange event yourself. ClassWizard provides stock implementation of these. In the constructor for your control, add:

m_lReadyState = READYSTATE_LOADING;

You will update the ready state as your code is downloaded by calling . One place you could call InternalSetReadyState is from the OnProgress override of CDataPathProperty derived class.

Then, follow the steps in How do I create a new ActiveX control?