- Table of Contents
- Copyright
- About the Authors
- About the Contributors
- Acknowledgments
- Tell Us What You Think!
- Introduction
- How to Use This Book
- What You Need to Use This Book
- What's New in Visual C++ 6.0
- Contacting the Main Author
- Part I: Introduction
- Chapter 1. The Visual C++ 6.0 Environment
- Part II: MFC Programming
- Chapter 2. MFC Class Library Overview
- Chapter 3. MFC Message Handling Mechanism
- Chapter 4. The Document View Architecture
- Chapter 5. Creating and Using Dialog Boxes
- Chapter 6. Working with Device Contexts and GDI Objects
- Chapter 7. Creating and Using Property Sheets
- Chapter 8. Working with the File System
- Chapter 9. Using Serialization with File and Archive Objects
- Part III: Internet Programming with MFC
- Chapter 10. MFC and the Internet Server API (ISAPI)
- Chapter 11. The WinInet API
- Chapter 12. MFC HTML Support
- Part IV: Advanced Programming Topics
- Chapter 13. Using the Standard C++ Library
- Chapter 14. Error Detection and Exception Handling Techniques
- Chapter 15. Debugging and Profiling Strategies
- Chapter 16. Multithreading
- Chapter 17. Using Scripting and Other Tools to Automate the Visual C++ IDE
- Part V: Database Programming
- Chapter 18. Creating Custom AppWizards
- Chapter 19. Database Overview
- Chapter 20. ODBC Programming
- Chapter 21. MFC Database Classes
- Chapter 22. Using OLE DB
- Chapter 23. Programming with ADO
- Part VI: MFC Support for COM and ActiveX
- Chapter 24. Overview of COM and Active Technologies
- Chapter 25. Active Documents
- Chapter 26. Active Containers
- Chapter 27. Active Servers
- Chapter 28. ActiveX Controls
- Part VII: Using the Active Template Library
- Chapter 29. ATL Architecture
- Chapter 30. Creating COM Objects Using ATL
- Chapter 31. Creating ActiveX Controls Using ATL
- ATL Control Classes
- Connection Points
- Creating Scriptable Controls
- Summary
- Chapter 32. Using ATL to Create MTS and COM+ Components
- Part VIII: Finishing Touches
- Chapter 33. Adding Windows Help
- Part IX: Appendix
Creating Scriptable Controls
A scriptable ActiveX control is a control that is constructed so that it is appropriate for use by scripting clients. Scriptable controls are designed to be safe when used in a Web browser, such as Internet Explorer, or by scripts written for the Windows Scripting Host (WSH). Scriptable controls also support mechanisms to simplify passing parameters to the control via a scripting language.
By default, ActiveX controls are not considered safe for scriptable clients. If you have an ActiveX control that could be useful by scripting clients, taking a few simple steps can expand the usefulness of your control. If you're using ATL, these changes typically take just a few minutes.
To be scriptable, an ActiveX control must meet the following requirements:
- Be safe when executed by an arbitrary scripting client. This requirement is discussed in detail in the next section.
- Be registered as "Safe for Scripting." This requirement is discussed in the next section, and an example is presented as part of the ScriptButton project later in this chapter.
- Support the IPersistPropertyBag interface. This is technically not a requirement, but it simplifies the task of passing parameters to the control. The IPersistPropertyBag interface is discussed in the section "Persistence for ActiveX Controls," later in this chapter.
Requirements for Scriptable Controls
Microsoft has published a document that describes all aspects of component object safety with regard to scripting ActiveX controls. The document, "Designing Safe ActiveX Controls," is located in the MSDN Library included with Visual C++.
Before registering your control as safe for scripting, you must ensure that your control does not take any actions that may be unsafe when used by a scripting client. Unsafe actions include exposing information normally kept private, such as passwords and other sensitive information, executing files, and making dangerous system calls.
Another type of object safety concerns the use of parameters and properties with the control. When used with a scripting client, a safe control must ensure that the control can continue to function with any possible combination of properties defined by the user.
Later in this chapter, you'll look at an example of a control that is safe for scripting.
Persistence for ActiveX Controls
By default, ActiveX controls built with ATL support the IPersistStream interface for object persistence. Persisting data via a binary stream is an efficient way to transfer data to and from a control, and this method is preferable for most containers.
The native form for parameter information for scripting clients is a text string, however, and attempting to persist binary parameter information in a scripting language such as VBScript is painful (although not impossible). For this reason, most scriptable controls support the IPersistPropertyBag interface, which allows properties to be persisted as pairs of property names and property values. Instead of passing a binary stream to an ActiveX control, a client simply can pass pairs of parameter names and values to initialize a control.
A Scriptable ActiveX Control
This book's CD-ROM includes ScriptButton, an ATL-based ActiveX control that superclasses the standard Windows pushbutton. ScriptButton is a scriptable control; sample programs presented later in this chapter demonstrate how ScriptButton is used with Internet Explorer.
ScriptButton is similar to the standard Windows pushbutton, except that it appears to be a label until the mouse pointer is placed over the control. When the pointer is over the control, the control is redrawn with beveled edges so that it looks like a standard button.
The Basic Design of ScriptButton
As you learned in the preceding section, ScriptButton is similar to the standard Windows pushbutton control, except that it only has a 3D appearance when the mouse pointer is moved over the control. For the control to be drawn properly, the control must be able to perform the following tasks:
- Track the current state of the button (raised, flat, clicked)
- Detect when the mouse pointer is over the control
- Detect when the mouse pointer is clicked over the control
- Track the current focus state of the control so that the focus rectangle can be drawn correctly
In addition, ScriptButton must use a set of stock properties that a user of a button control may want to adjust:
- Background Color
- Foreground Color
- Tab Stop
- Caption
Two events are sent from the ScriptButton control to its container:
- OnClicked is sent after the control is clicked. This event is sent if the user clicks the button with the mouse or selects the button using the keyboard.
- OnHover is sent when the control enters a raised, or hover, state. This event notifies the container that the mouse is hovering over the control and allows the container to update a status bar or provide some other sort of feedback.
Messages Handled by ScriptButton
ScriptButton handles messages sent by the operating system, as well as one message sent by the underlying button control. The control must handle the following messages:
- WM_MOUSEMOVE is sent as the mouse pointer passes over the control.
- WM_SETFOCUS is sent just before the control receives input focus.
- WM_KILLFOCUS is sent just before the control loses input focus.
- WM_LBUTTONDOWN is sent after the user presses the primary mouse button (usually the left button) while the pointer is over the control.
- WM_LBUTTONUP is sent after the user releases the primary mouse button while the pointer is over the control.
- WM_ERASEBKGND is sent just before the control is repainted by Windows.
- WM_MOUSELEAVE is sent when the mouse moves away from the control's window.
In addition, the control handles one notification message sent by the pushbutton control:
- BN_CLICKED is sent after the user successfully clicks the button control.
The BN_CLICKED message is sent after the user clicks the button with the mouse or selects the button using the keyboard. After this message is received from the underlying control, ScriptButton fires an OnClicked event to its container.
Handling Raised-Button and Flat-Button States
The WM_MOUSEMOVE and WM_MOUSELEAVE messages are used together to control the raised appearance of the control. As discussed earlier, the button initially is drawn in a flat state. After a WM_MOUSEMOVE message is received by the control, the control redraws itself in the raised state.
While the button is in the raised state, it must handle two cases:
- The user clicks on the control.
- The user moves the mouse away from the control rectangle.
The first case is handled easily and is discussed in the next section. If the mouse is moved away from the control rectangle, however, no event is sent to the control's window. To detect the departure of the mouse, the Win32 _TrackMouseEvent function is called when the mouse initially moves over the control. This function registers the control window as requesting additional information about mouse events. When the mouse is moved away from the control, a WM_MOUSELEAVE message is sent to the control's window, and the button is redrawn in its flat state.
Handling Button Clicks
Three messages are used to draw the control and report events after the user clicks the ScriptButton control. WM_LBUTTONDOWN and WM_LBUTTONUP are sent from the operating system as the user presses and releases the primary mouse button while the pointer is over the control. In response to these messages, the control is drawn in either the down state (for WM_LBUTTONDOWN) or the raised state (for WM_LBUTTONUP).
After the user clicks the ScriptButton control, the underlying button generates a BN_CLICKED notification message. This message is used to create an OnClicked event that is sent to the control's container.
Handling the Focus Rectangle
One important visual indicator supplied by user interface components is the focus rectangle. If the focus rectangle isn't drawn correctly, the user may be confused about the input state of the control.
Two messages are used to draw the focus rectangle. The WM_SETFOCUS message is sent just before the control receives input focus; it causes the familiar dotted rectangle to be drawn around the control. The WM_KILLFOCUS message is sent just before focus is lost; it causes the focus rectangle to be removed.
It's so important that the focus rectangle be drawn correctly that Windows offers an API function to handle it all for you:
DrawFocusRect(hdc, lprc);
You call DrawFocusRect with two parameters: the device context that you're drawing into, and the rectangle that describes the boundary of the focus rectangle. This function also is used to remove the focus rectangle: If you call it a second time with the same rectangle, the focus rectangle is removed.
Creating the ScrBtn Project
The ScrBtn project is used to create the ScriptButton control, which is created using the Visual C++ ATL COM AppWizard. The control begins as a DLL project, as Figure 31.6 shows.
Figure 31.6 Using the ATL COM AppWizard to create the ScrBtn project.
The ScrBtn project does not use any of the optional features offered by the ATL COM AppWizard, such as MFC or MTS support or the capability to merge proxy/stub code.
Adding the ScriptButton Control Class to the Project
The ActiveX control component in the ScrBtn project is implemented by adding a new COM class into the project with the ATL Object Wizard. Start by choosing New ATL Object from the Visual C++ Insert menu. The ATL Object Wizard appears, as shown in Figure 31.7.
Figure 31.7 The ATL Object Wizard.
The ScrBtn project will implement a full control named ScriptButton. Select Controls from the wizard's Category list box, and then select Full Control as the control type. Click Next to begin adding ScriptButton to the project.
The ATL Object Wizard Properties dialog box appears so that you can define your control's properties (see Figure 31.8). For full controls, such as ScriptButton, the dialog box offers four tabs.
Figure 31.8 The ATL Object Wizard Properties dialog box.
The Names tab is just like the Names tab used in custom COM objects in Chapter 29. You only need to fill in the Short Name property for ScriptButton—the other fields are filled in automatically and should contain the values shown in Table 31.3.
Table 31.3. Contents of the Names Tab for the ScriptButton Control
| Property | Name |
| Short Name | ScriptButton |
| Class | CScriptButton |
| .H File | ScriptButton.h |
| .CPP File | ScriptButton.cpp |
| CoClass | ScriptButton |
| Interface | IScriptButton |
| Type | ScriptButton Class |
| Prog ID | ScrBtn.ScriptButton |
The second tab is the Attributes tab; again, it is identical to the Attributes tab discussed in Chapter 29. Table 31.4 shows the values used for ScriptButton on the Attributes tab. All these values are the default options, except that ScriptButton supports connection points. (This property appears in bold in Table 31.4, because it is not a default setting.) As discussed earlier in the chapter, connection points are used to supply events to a control's container.
Table 31.4. Contents of the Attributes Tab for the ScriptButton Control
| Property | Name |
| Threading Model | Apartment |
| Interface | Dual |
| Aggregation | Yes |
| Support ISupportErrorInfo | Unchecked |
| Support Connection Points | Checked |
| Free Threaded Marshaler | Unchecked |
The third tab is the Miscellaneous tab (see Figure 31.9). It contains attributes specific to ActiveX controls.
Figure 31.9 Miscellaneous tab values for the ScriptButton control.
Table 31.5 shows the values for the ScriptButton control; nondefault values appear in bold type.
Table 31.5. Contents of the Miscellaneous Tab for the ScriptButton Control
| Property | Name |
| Opaque | Checked |
| Solid Background | Checked |
| Add Control Based On | Button |
| Invisible at Runtime | Unchecked |
| Acts Like Button | Checked |
| Acts Like Label | Unchecked |
| Normalize DC | Checked |
| Windowed Only | (Disabled) |
| Insertable | Unchecked |
The final tab in the dialog box is Stock Properties. It contains a list of all stock properties that can be implemented by an ActiveX control (see Figure 31.10).
Figure 31.10 The Stock Properties tab values for the ScriptButton control.
The ScriptButton control uses these stock properties:
- Background Color
- Caption
- Foreground Color
- Tab Stop
After you select the stock properties listed here, click OK to close the dialog box. The ATL New Object Wizard then generates the necessary code and adds it to your project.
Adding Outgoing Events
As discussed earlier, the ScriptButton control provides two events to its containers:
- OnClick is sent after the button is clicked.
- OnHover is sent after the button is raised.
Open the ScrBtn.idl file and add the two lines shown in bold in Listing 31.6 to the _IScriptButtonEvents interface. Alternatively, you can add the events by right-clicking the _IScriptButtonEvents icon in ClassView and choosing Add Method from the pop-up menu.
Example 31.6. Changing the ScrBtn IDL File to Add Outgoing Events
library SCRBTNLib
{
importlib("stdole31.tlb");
importlib("stdole2.tlb");
[
uuid(416D4C0E-A2C9-49AA-887F-0E1D8BCE280F),
helpstring("_IScriptButtonEvents Interface")
]
dispinterface _IScriptButtonEvents
{
properties:
methods:
[id(1), helpstring("OnClick event")] HRESULT OnClick();
[id(2), helpstring("OnHover event")] HRESULT OnHover();
};
[
uuid(B584CB87-D818-4E6B-9DC5-F0063CF64E22),
helpstring("ScriptButton Class")
]
coclass ScriptButton
{
[default] interface IScriptButton;
[default, source] dispinterface _IScriptButtonEvents;
};
};
Before proceeding, compile the skeleton project. This project compiles the type library and makes it possible to add the event connection point for the control.
After the project is compiled, add the connection point to the project by right-clicking the CScriptButton icon in ClassView and choosing Implement Connection Point from the pop-up menu. The Implement Connection Point dialog box appears. Enable the check box next to _IScriptButtonEvents and click OK. The CProxy_IScriptButtonEvents class then is generated and added to the project.
Modifying the Message Map
Seven messages must be added to the CScriptButton message map. Six of the messages are generated by the operating system, and one message is generated by the superclass pushbutton control.
As discussed earlier, you can open the dialog box that adds message-handling functions by right-clicking the CScriptButton icon in ClassView and choosing Add Windows Message Handler from the pop-up menu. Add handlers for these messages:
- WM_ERASEBKGND
- WM_KILLFOCUS
- WM_LBUTTONDOWN
- WM_LBUTTONUP
- WM_MOUSEMOVE
You must manually add message-handling macros for the BN_CLICKED and WM_TRACKMOUSEMESSAGE messages to the message map. Listing 31.7 shows the finished message map; the manually added macros appear in bold. Note that all the message handlers are in the main part of the message map. Make sure your message map doesn't have any macro entries in the alternate message map.
Example 31.7. The Message Map for ScriptButton
BEGIN_MSG_MAP(CScriptButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUP)
MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
COMMAND_CODE_HANDLER(BN_CLICKED, OnClicked)
CHAIN_MSG_MAP(CComControl<CScriptButton>)
ALT_MSG_MAP(1)
// Replace this with message map entries for superclassed Button
END_MSG_MAP()
Initializing the CScriptButton Object
Before adding the code needed to initialize an instance of the CScriptButton class, add an enumeration used to track button states to the ScriptButton.h header file. Insert the contents of Listing 31.8 just above the declaration of the CScriptButton class.
Example 31.8. The BtnState Enumeration
enum BtnState { bsFlat, bsDown, bsHover } ;
Earlier in this chapter, when the stock properties were added to the ScriptButton control, the ATL Object Wizard inserted member variables into the ScriptButton.h header file. These variables, shown in Listing 31.9, are used to hold the values of the stock properties supported by the control. Add the member variables shown in bold to the class declaration; these variables are used to implement various control features that will be discussed throughout the remainder of this chapter.
Example 31.9. Additional Member Variables Used by CScriptButton
public:
OLE_COLORm_clrBackColor;
CComBSTR m_bstrCaption;
OLE_COLORm_clrForeColor;
BOOL m_bTabStop;
CComPtr<IFontDisp> m_pFont; // Contains ambient font
BtnState m_btnState; // Tracks button state
bool m_fHasFocus; // TRUE if button has focus
Add the lines shown in bold in Listing 31.10 to the CScriptButton constructor. Note that you must add a comma to the first line of the constructor initializer list.
Example 31.10. The Constructor for the CScriptButton Class
CScriptButton() :
m_ctlButton(_T("Button"), this, 1), // add comma to this line
m_btnState(bsFlat),
m_fHasFocus(false),
{
m_bWindowOnly = TRUE;
}
The default handler for OnCreate will superclass a default instance of the Windows BUTTON class. The ScriptButton control must superclass a BUTTON with pushbutton attributes, so modify the OnCreate member function, as shown in Listing 31.11, by changing the lines shown in bold.
Example 31.11. Changes to the OnCreate Member Function
LRESULT OnCreate(UINT, WPARAM, LPARAM, BOOL&)
{
RECT rc;
GetWindowRect(&rc);
rc.right -= rc.left;
rc.bottom -= rc.top;
rc.top = rc.left = 0;
m_ctlButton.Create(m_hWnd,
rc,
_T("BUTTON"),
WS_CHILD|BS_PUSHBUTTON);
return 0;
}
Retrieving Ambient Properties
When the control is initially loaded, it collects the current foreground color, background color, and font from its container. A good time to collect ambient properties is when the control and its container negotiate the client site. Add the source code in Listing 31.12 to the ScriptButton.cpp source file. This function overrides the base class implementation of SetClientSite and stores the ambient values of these three properties.
Example 31.12. A New Version of SetClientSite That Collects Ambient Properties from the Container
STDMETHODIMP CScriptButton::SetClientSite(LPOLECLIENTSITE pSite)
{
HRESULT hr = CComControlBase::IOleObject_SetClientSite(pSite);
if(!m_pFont && pSite)
{
hr = GetAmbientFontDisp(&m_pFont);
}
GetAmbientBackColor(m_clrBackColor);
GetAmbientForeColor(m_clrForeColor);
return hr;
}
Add the following member function declaration to the CScriptButton class:
STDMETHOD(SetClientSite)(LPOLECLIENTSITE pSite);
A good place for this declaration is just after all the member variables used to track properties.
Handling Focus Events for the Control
When a focus event is received by the control, the focus member variable is set with the new input focus state, and the control's window is invalidated. The control's container also is notified that the control is changing its view.
Listing 31.13 contains the OnSetFocus and OnKillFocus member functions, which handle the WM_SETFOCUS and WM_KILLFOCUS messages, respectively. Add the lines shown in bold to the member functions.
Example 31.13. The OnSetFocus and OnKillFocus Member Functions for the CScriptButton Class
LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
LRESULT lRes = CComControl<CScriptButton>::OnSetFocus(uMsg,
wParam,
lParam,
bHandled);
if (m_bInPlaceActive)
{
DoVerbUIActivate(&m_rcPos, NULL);
if(!IsChild(::GetFocus()))
m_ctlButton.SetFocus();
}
m_fHasFocus = true;
FireViewChange();
return lRes;
}
LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
m_fHasFocus = false;
FireViewChange();
return 0;
}
Handling Mouse Events for the Control
The following four message handlers control mouse and click events for the CScriptButton class:
- OnLButtonDown updates the button state variable and invalidates the control's rectangle so that it will be redrawn.
- OnLButtonUp updates the button state variable and invalidates the control's rectangle so that it can be redrawn. If the mouse pointer is currently over the control, the new button state is bsHover; if the mouse pointer is not over the control, the new button state is bsFlat.
- OnMouseMove updates the button state if the current button state is bsFlat. If the button state is moving from bsFlat to bsHover, the control's rectangle is invalidated so that it can be redrawn. In addition, the OnHover event is fired to the control's container.
- OnClick is sent to the CScriptButton class when the underlying button control generates a click event. In response, the OnClick member function fires an OnClick event to the container.
To implement these member functions, add the lines shown in bold in Listing 31.14 to the member functions in the CScriptButton class.
Example 31.14. Event-Handling Member Functions in the CScriptButtonClass
LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
// Change the state to bsDown, and redraw the control
m_btnState = bsDown;
FireViewChange();
m_ctlButton.DefWindowProc(uMsg, wParam, lParam);
return 0;
}
LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
if(MouseOverCtl())
m_btnState = bsHover;
else
m_btnState = bsFlat;
FireViewChange();
m_ctlButton.DefWindowProc(uMsg, wParam, lParam);
return 0;
}
LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
if(m_btnState == bsFlat)
{
// Moving from flat to hover...
Fire_OnHover();
m_btnState = bsHover;
FireViewChange();
TRACKMOUSEEVENT tme;
ZeroMemory(&tme, sizeof(tme));
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = m_hWnd;
TrackMouseEvent(&tme);
}
m_ctlButton.DefWindowProc(uMsg, wParam, lParam);
return 0;
}
// BN_CLICKED
LRESULT OnClicked(WORD wNotifyCode, WORD wID, HWND hWndCtl,
BOOL& bHandled)
{
Fire_OnClick();
m_btnState = bsFlat;
FireViewChange();
bHandled = TRUE;
return 0;
}
Note that when the mouse pointer is initially moved over the control, the TrackMouseEvent function is called to request a notification when the pointer leaves the control's window. When the pointer leaves the control window, the operating system sends the control a WM_MOUSELEAVE message. Add the message-handling function for WM_MOUSELEAVE provided in Listing 31.15 to the ScriptButton.h header file, inside the CScriptButton class declaration.
Example 31.15. Handling the WM_MOUSELEAVE Message
LRESULT OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
m_btnState = bsFlat;
FireViewChange();
bHandled = TRUE;
return 0;
}
Several of the functions in CScriptButton need to determine whether the mouse pointer is over the control. The MouseOverCtl function provided in Listing 31.16 returns TRUE if the pointer is over the control; otherwise, it returns FALSE. Add this function to the ScriptButton.h header file, inside the CScriptButton class declaration.
Example 31.16. Handling the WM_MOUSELEAVE Message
BOOL MouseOverCtl()
{
RECT rc;
POINT pt;
GetWindowRect(&rc);
GetCursorPos(&pt);
return PtInRect(&rc, pt);
}
Drawing the Control
The most complex part of the ScriptButton control involves drawing the control. Before getting into the actual drawing code, there are two functions that make it easy to determine which state the button is in. Listing 31.17 contains the IsSelected and IsRaised member functions, which are used by the CScriptButton class when drawing the control. These functions can be found in the ScriptButton.h file.
Example 31.17. The IsSelected and IsRaised Member Functions
BOOL IsSelected()
{
return m_btnState == bsDown;
}
BOOL IsRaised()
{
return m_btnState == bsHover;
}
The source code provided in Listing 31.18 contains OnDraw, the function primarily responsible for drawing the control. The complete drawing code for the control is not shown here, but you can find the full source code on the CD-ROM that accompanies this book.
Example 31.18. The CScriptButton::OnDraw Function
HRESULT OnDraw(ATL_DRAWINFO& di)
{
RECT& rc = *(RECT*)di.prcBounds;
HDC hdc = di.hdcDraw;
COLORREF clrFore, clrBack;
OleTranslateColor(m_clrForeColor, NULL, &clrFore);
OleTranslateColor(m_clrBackColor, NULL, &clrBack);
SetTextColor(hdc, m_clrForeColor);
HBRUSH hbrBtn = CreateSolidBrush(m_clrBackColor);
FillRect(hdc, &rc, hbrBtn);
DrawEdges(hdc, &rc);
DrawButtonText(hdc, &rc, m_bstrCaption);
if(m_fHasFocus)
{
InflateRect(&rc, -2, -2);
DrawFocusRect(hdc, &rc);
}
DeleteObject(hbrBtn);
return 0;
}
Implementing IPersistPropertyBag
To simplify the use of ScriptButton in a scripting client, such as Internet Explorer, the control must support the IPersistPropertyBag interface. The ATL class library includes a class, IPersistPropertyBagImpl, that provides a default implementation of IPersistPropertyBag that is sufficient in most cases. IPersistPropertyBagImpl is a parameterized class that takes one argument—the name of the class to be persisted:
IPersistPropertyBagImpl<CLatte>
To add support for IPersistPropertyBagImpl to CScriptButton, make the changes shown in Listing 31.19 to the declaration of the CScriptButton class. These changes, shown in bold, add IPersistPropertyBagImpl to the classes that CScriptButton is derived from.
Example 31.19. Deriving CScriptButton From IPersistPropertyBagImpl
class ATL_NO_VTABLE CScriptButton :
public CComObjectRootEx<CComSingleThreadModel>,
public CStockPropImpl<CScriptButton,IScriptButton,
&IID_IScriptButton, &LIBID_SCRBTNLib>,
public CComControl<CScriptButton>,
public IPersistStreamInitImpl<CScriptButton>,
public IPersistPropertyBagImpl<CScriptButton>,
public IOleControlImpl<CScriptButton>,
public IOleObjectImpl<CScriptButton>,
public IOleInPlaceActiveObjectImpl<CScriptButton>,
public IViewObjectExImpl<CScriptButton>,
public IOleInPlaceObjectWindowlessImpl<CScriptButton>,
public IConnectionPointContainerImpl<CScriptButton>,
public IPersistStorageImpl<CScriptButton>,
public ISpecifyPropertyPagesImpl<CScriptButton>,
public IQuickActivateImpl<CScriptButton>,
public IDataObjectImpl<CScriptButton>,
public IProvideClassInfo2Impl<&CLSID_ScriptButton,
&DIID__IScriptButtonEvents, &LIBID_SCRBTNLib>,
public IPropertyNotifySinkCP<CScriptButton>,
public CComCoClass<CScriptButton, &CLSID_ScriptButton>,
public CProxy_IScriptButtonEvents< CScriptButton >
You also must add IPersistPropertyBagImpl to the COM_MAP macro by making the changes shown in bold in Listing 31.20.
Example 31.20. Adding IPersistPropertyBagImpl to the COM_MAP
BEGIN_COM_MAP(CScriptButton)
COM_INTERFACE_ENTRY(IScriptButton)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(IViewObjectEx)
COM_INTERFACE_ENTRY(IViewObject2)
COM_INTERFACE_ENTRY(IViewObject)
COM_INTERFACE_ENTRY(IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceObject)
COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
COM_INTERFACE_ENTRY(IOleInPlaceActiveObject)
COM_INTERFACE_ENTRY(IOleControl)
COM_INTERFACE_ENTRY(IOleObject)
COM_INTERFACE_ENTRY(IPersistStreamInit)
COM_INTERFACE_ENTRY(IPersistPropertyBag)
COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
COM_INTERFACE_ENTRY(IConnectionPointContainer)
COM_INTERFACE_ENTRY(ISpecifyPropertyPages)
COM_INTERFACE_ENTRY(IQuickActivate)
COM_INTERFACE_ENTRY(IPersistStorage)
COM_INTERFACE_ENTRY(IDataObject)
COM_INTERFACE_ENTRY(IProvideClassInfo)
COM_INTERFACE_ENTRY(IProvideClassInfo2)
COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer)
END_COM_MAP()
With these changes, a container that supports scripting can obtain a pointer to the control's IPersistPropertyBag interface, and will use that interface for persisting data instead of IPersistStreamInit. Containers that prefer to persist data with binary streams still will query for IPersistStream or IPersistStreamInit.
Marking the Control as Safe for Scriptable Clients
You can use two methods to mark a control as safe for scripting clients:
- Make appropriate entries directly in the System Registry.
- Implement the IObjectSafety interface.
Microsoft recommends that you use new controls to implement the IObjectSafety interface instead of making Registry entries. IObjectSafety enables the control to apply much finer-grained safety policies than possible using the Registry.
ATL provides a default implementation of IObjectSafety that you easily can take advantage of by deriving your class from the IObjectSafetyImpl class. To add this class to CScriptButton, add the code shown in bold in Listing 31.21 to the declaration of CScriptButton.
Example 31.21. Deriving CScriptButton from IObjectSafetyImpl
class ATL_NO_VTABLE CScriptButton :
.
.
.
public IPersistPropertyBagImpl<CScriptButton>,//
public IObjectSafetyImpl<CScriptButton,
INTERFACESAFE_FOR_UNTRUSTED_CALLER|
INTERFACESAFE_FOR_UNTRUSTED_DATA>,
public IOleControlImpl<CScriptButton>,
.
.
.
Two template arguments are passed to IObjectSafetyImpl:
- The name of the class deriving from IObjectSafetyImpl
- The type of safety to be applied to the control
Two values may be passed for the safety options:
- INTERFACESAFE_FOR_UNTRUSTED_CALLER specifies that your control can be used safely by a scripting client and does not violate any of Microsoft's security or safety guidelines.
- INTERFACESAFE_FOR_UNTRUSTED_DATA specifies that your control will work (or at least degrade gracefully) in the presence of any possible set of parameters passed by a client, including invalid or conflicting parameters.
You can combine either or both of these values as appropriate for your control.
You also must add IObjectSafetyImpl to the COM_MAP macro by making the changes shown in bold in Listing 31.22.
Example 31.22. Adding IObjectSafetyImpl to the COM_MAP
BEGIN_COM_MAP(CScriptButton)
.
.
.
COM_INTERFACE_ENTRY(IPersistPropertyBag)
COM_INTERFACE_ENTRY(IObjectSafety)
COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
.
.
.
END_COM_MAP()
Testing ScriptButton with Internet Explorer
Compile the CScriptButton project. You can test the control in a variety of control containers, including Visual Basic and TstCon31.exe—the test container included with Visual C++. Because the ScriptButton control is scriptable, you also can use the control on an HTML page with Internet Explorer.
The ScriptButton control can be inserted on an HTML page using the OBJECT tag, as Listing 31.23 shows. The complete source for an HTML file used to test ScriptButton is located in the ScriptButton project directory as IETest.htm.
Example 31.23. Inserting ScriptButton in an HTML File
<OBJECT classid=clsid:B584CB87-D818-4E6B-9DC5-F0063CF64E22
id=ScriptButton1
style="HEIGHT: 39px; WIDTH: 192px"VIEWASTEXT>
<PARAM NAME="_cx" VALUE="5080">
<PARAM NAME="_cy" VALUE="1032">
<PARAM NAME="BackColor" VALUE="8438015">
<PARAM NAME="Caption" VALUE="Click Me!">
<PARAM NAME="TabStop" VALUE="-1">
</OBJECT>
When the IETest.htm file runs in Internet Explorer, the ScriptButton control is displayed, as shown in Figure 31.11. When the mouse pointer moves over the control, the button raises up and changes its caption. After the button is clicked, the caption also changes.
Figure 31.11 The ScriptButton control running in Internet Explorer.
Summary | Next Section

Account Sign In
View your cart