Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

ActiveX Control Architecture

The beauty of ActiveX controls is that they are programmable and reusable. They expose themselves to the outside world and can be used in a variety of environments. ActiveX controls are similar to embedded object servers, in that they are embedded in a container and are responsible for providing a user interface. ActiveX controls also are expected to expose functionality through interfaces, which often are derived from IDispatch to take advantage of automation. ActiveX controls take advantage of the capability to send events to their container; this capability to send events separates ActiveX controls from other in-process OLE servers.

ActiveX controls communicate with the outside world in three ways:

Properties

Properties are named attributes or characteristics of an ActiveX control. Properties may be marked as read-only, but typically these properties can be set or queried. Some examples of properties are color, font, and number.

Usually, ActiveX controls provide access to their properties through property sheets. Property sheets are exposed via automation. Property sheets are not limited to design/compile time but can be displayed at runtime to allow the user to manipulate the control's properties, events, or methods. Property sheets are user-interface components that basically are tabbed dialog boxes. Automation provides the mechanism by which ActiveX controls communicate with their property sheets.

ActiveX controls have access to what are called stock properties. These are properties common to all ActiveX controls. MFC enables you to take advantage of these stock properties because they already are built in. Table 28.1 lists all the stock properties supported by MFC.

Table 28.1. Stock Properties for ActiveX Controls Supported by MFC

Stock Property Get/Set Stock Method Enables You To…
Appearance void SetAppearance (short sAppear) short GetAppearance( ) Set the appearance of an ActiveX control to flat or 3D.
BackColor OLE_COLOR GetBackColor ( ) void SetBackColor (OLE_COLOR dwBkColor) Set the background colors of the control.
Border Style short GetBorderStyle ( ) void SetBorderStyle(short sBorderStyle) Set the border style of the control to normal or none.
Caption BSTR GetText( ) void SetText(LPCTSTR pszText) Set the caption of the ActiveX control.
Enabled BOOL GetEnabled( ) void SetEnabled(BOOL bEnabled) Enable or disable the control.
Font LPFONTDISP GetFont( ) void SetFont(LPFONTDISP pFontDisp) Set the font properties used by the control.
Fore Color OLE_COLOR GetForeColor( ) void SetForeColor(OLE_COLOR dwForeColor) Set the forecolors of the control.
hWnd OLE_HANDLE GetHwnd( ) Hold the control's window handle.
Ready State long GetReadyState( ) Get or set the ready state of the control with the following values: READYSTATE_UNINITIALIZED READYSTATE_LOADING READYSTATE_LOADED READYSTATE_INTERACTIVE READYSTATE_COMPLETE
Text const CString& InternalGetText( ) Get and set the control's text. This property is the same as Caption.

You can include the stock properties in your ActiveX controls by using the ClassWizard. Choose View, ClassWizard or press Ctrl+W. After the ClassWizard appears, select the Automation tab (see Figure 28.1).

28fig01.gif

Figure 28.1 Using the ClassWizard to access stock properties.

Click the Add Property button to invoke the Add Property dialog box. If you use the External Name combo box, you can select which stock properties you want your control to have (see Figure 28.2).

28fig02.gif

Figure 28.2 You can select stock properties to support by using the External Name combo box.

MFC and Visual C++ 6.0 also provide stock property sheets to add to our user interface so that users can set stock properties. These are General (see Figure 28.3), Fonts (see Figure 28.4), Colors (see Figure 28.5), and Pictures properties.

28fig03.gif

Figure 28.3 The default General Properties page.

28fig04.gif

Figure 28.4 The stock Fonts Properties page.

28fig05.gif

Figure 28.5 The stock Colors Properties page.

The human-readable class IDs for these Properties pages follow:

When you use the MFC ActiveX ControlWizard to generate an ActiveX control, it generates a class derived from COleControl. In that class's .cpp file, the wizard includes support for a Properties page so that your control can have a user interface to set the control properties. This Properties page is derived from COlePropertyPage, and MFC has a macro for declaring the Properties pages in the control. Listing 28.1 shows the macros BEGIN_PROPPAGEIDS and END_PROPPAGEIDS. This code fragment is located in the .cpp file for the COleControl-derived class.

Example 28.1. Using the BEGIN_PROPPAGEIDS and END_PROPPAGEIDS to Declare the COlePropertyPage for Your Control

1: BEGIN_PROPPAGEIDS( CMySampleCtrl, 1 )
2:    PROPPAGEID( CMyPropPage::guid )
3: END_PROPPAGEIDS(CSampleCtrl)

Notice on line 2 that PROPPAGEID is passed the globally unique identifier (GUID— pronounced gwid, as in squid), and also calls a class ID (CLSID) of the Properties page. The GUID for the Properties page is declared in the .cpp of the class created by the ControlWizard derived from ColePropertyPage.

If you want to implement the stock Properties pages that MFC provides, you have to pass the CSLID of the stock Properties pages. Listing 28.2 demonstrates this process.

Example 28.2. Adding Declarations for the Stock Fonts Properties Page, Colors Properties Page, and Pictures Properties Page

1: BEGIN_PROPPAGEIDS( CMySampleCtrl, 4 )
2:    PROPPAGEID( CMyPropPage::guid )
3:    PROPPAGEID( CLSID_CFontPropPage )
4:    PROPPAGEID( CLSID_CColorPropPage )
5:    PROPPAGEID( CLSID_CPicturePropPage )
6: END_PROPPAGEIDS(CSampleCtrl)

Notice in line 1 that the second parameter is now 4. This second parameter represents the number of Properties pages.

ActiveX controls also have persistent properties. These properties are stored in the container and are set at design time or compile time. Controls also have the capability to save persistent information about their properties at runtime, and thus, in effect, can save their state. This means that the controls can load their persistent properties at initial load time.

Events

Events are notifications generated by the control to provide some sort of notification to the container. Usually, this is input by the user, such as a mouse click or keyboard input. However, it also may be generated entirely within the control, such as when a timer expires. The event then is communicated to the control's container by the control. This is done through a communications mechanism known as a Lightweight Remote Procedure Call (LRPC). LRPCs are the scaled-down little brothers of the Remote Procedure Call (RPC).

RPCs are an interprocess communications mechanism used to provide two-way communications between applications. These applications can be on the same computer or on computers across a network. RPC is the mechanism that Distributed COM (DCOM) uses to exchange objects across process and computer boundaries. RPC is much more than just a communications method. It allows a function in a process on one computer to evoke a function in a process on another computer. This computer can even be a computer across an enterprise-wide network or the Internet.

Unlike their big brother, the RPC, LRPCs are only for communications between processes or within processes on a single computer. LRPCs are the mechanism by which an ActiveX control dispatches control notifications to the container and from the container to the control. This communication is based on posting messages or events to window handles to transfer data between processes. It is known as marshaling.

The Microsoft Foundation Classes provide support for several stock events; Table 28.2 lists these events.

Table 28.2. Stock Events Supported in MFC and Visual C++ and Their Event Map Entries

Stock Event Function Event Map Entry
Click void FireClick() EVENT_STOCK_CLICK()
DblClick void FireDblClick() EVENT_STOCK_DBLCLICK()
Error void FireError(SCODE scode, LPCSTR lpszDescription, UINT nHelpID = 0 ) EVENT_STOCK_ERROR()
KeyDown void FireKeyDown (short nChar, short nShiftState) EVENT_STOCK_KEYDOWN()
KeyPress void FireKeyPress(short* pnChar) EVENT_STOCK_KEYPRESS()
KeyUp void FireKeyUp(short nChar, short nShiftState ) EVENT_STOCK_KEYUP()
 
MouseDown void FireMouseDown (short nButton, short nShiftState, float x, float y ) EVENT_STOCK_MOUSEDOWN()
MouseMove void FireMouseMove(short nButton, short nShiftState, float x, float y ) EVENT_STOCK_MOUSEMOVE()
MouseUp void FireMouseUp(short nButton, short nShiftState, float x, float y ) EVENT_STOCK_MOUSEUP()

I think you will find that handling events is very easy. One of the great things about the folks at Microsoft who developed Visual C++ and MFC is that they have been extremely developer oriented. They set up a system to handle events very similar to the message maps. They have what is called an event map. The event map has two macros to define the beginning and the end of the event map, as shown in Listing 28.3 in lines 1 and 5, respectively. Notice in line 3 the stock event for the mouse move event, as shown in Table 28.2.

Example 28.3. The Event Map of an ActiveX Control

1: BEGIN_EVENT_MAP(CMySampleCtrl, COleControl)
2: //{{AFX_EVENT_MAP(CMySampleCtrl)
3:      EVENT_STOCK_MOUSEMOVE( )
4: //}}AFX_EVENT_MAP
5: END_EVENT_MAP()

The Visual C++ ClassWizard handles putting the event map entries in for you. You can put them in by hand, but I don't recommend it. Let the tool do it for you; there is less chance for error. As with message maps, if you do add entries by hand, make sure that you don't make any changes between the comment lines that mark the area reserved by ClassWizard.

To add a stock or custom event, choose View | ClassWizard or press Ctrl+W. After the ClassWizard appears, select the ActiveX Events tab (see Figure 28.6).

28fig06.gif

Figure 28.6 Using ClassWizard to add stock events to a control.

Click the Add Event button, which brings up the Add Event dialog box. If you use the External Name combo box, you can select which stock events you want to use in your control (see Figure 28.7).

28fig07.gif

Figure 28.7 You can select stock events to support using the External Name combo box.

Methods

Methods are functions performed by the control to access the control's functionality. These functions enable an external source to manipulate the appearance, behavior, or properties of the control. These functions include actions such as GetColor, SetColor, CutToClipBoard, PasteFromClipboard, and so on. A method is the interface an application or a programmer can use to set values for or receive values from an ActiveX control.

Methods are a lot like member functions in C++. They provide accessor functions that provide and grant access to an ActiveX control's properties and data. An ActiveX control's properties are like a C++ class's member variables. Like properties, methods come it two flavors: stock and custom. Stock methods provide access to stock properties, such as color, font, and picture. Likewise, custom methods provide access to custom properties. With methods, you can change a control's appearance or initialize it with a value.

Like events, MFC provides two stock methods and handles the dispatch of all custom and stock methods, much like the message map and event map. Again, two macros define the beginning and end of the dispatch map: BEGIN_DISPATCH_MAP and END_DISPATCH_MAP (see lines 1 and 5 of Listing 28.4).

Example 28.4. The Dispatch Map That Maps the Methods Handled by an ActiveX Control

1: BEGIN_DISPATCH_MAP(CMySampleCtrl, COleControl)
2:     //{{AFX_DISPATCH_MAP(CSampleCtrl)
3:     DISP_STOCKPROP_REFRESH( )
4:     //}}AFX_DISPATCH_MAP
5: END_DISPATCH_MAP()

Notice also line 3. Line 3 has the DISP_STOCKPROP_REFRESH stock method. This is one of two stock methods supported by MFC. The other is DISP_STOCKPROP_DOCLICK. These methods also are known as the DoClick and Refresh methods. The DoClick method fires a click event to the container. The Refresh method is used by the container to force an update to the control's appearance.

Interfaces that expose properties, events, and methods for an ActiveX control often are derived from the IDispatch interface. Technically, an ActiveX control isn't required to derive its interfaces from IDispatch, but MFC enforces this restriction. Also, if you're using a scripting language, such as VBScript or JavaScript, your interfaces must be derived from IDispatch.

The IDispatch interface enables a client to call dispatch methods by name, instead of directly accessing the functions via a pointer. However, this is a multistep process that requires each method's name to be resolved before the method is actually called via the IDispatch::Invoke method. Because of the additional overhead, automation interfaces often are much slower than non-automation interfaces.

To create custom methods or add stock methods to your control in Visual C++ by using the ClassWizard, choose View | ClassWizard or press Ctrl+W. After the ClassWizard appears, select the Automation tab (see Figure 28.8).

28fig08.gif

Figure 28.8 Using the ClassWizard to invoke stock or custom methods.

Click the Add Method button, which invokes the Add Method dialog box. If you use the External Name combo box, you can select which stock methods you want your control to have, or you can create a new method by typing its name in the External Name combo box (see Figure 28.9).

28fig09.gif

Figure 28.9 You can select stock methods to support using the External Name combo box or create a new method.

Share ThisShare This

Informit Network