- 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
ATL Control Classes
The Active Template Library (ATL) offers advantages over MFC when building ActiveX controls when size or performance is a consideration. A control built with ATL is always smaller than an equivalent control built using MFC. In addition, you can build an ATL control that has no external dependencies—simplifying distribution, especially to network clients.
The classes used primarily for ActiveX controls follow:
| CWindow | Similar to the MFC CWnd class, but much lighter in weight. This class provides easy access to a number of commonly used functions. |
| CWindowImpl | Derived from CWindow and used by the control class to subclass or superclass an existing window class. It implements the ATL message map. |
| CDialogImpl | Derived from CWindow and used to implement dialog boxes. |
| CContainedWindow | Derived from CWindow and used to model a window contained in another object. If you superclass or subclass an existing window, an instance of CContainedWindow will represent the original window. |
In addition to these classes, ATL provides support for adding properties, events, and connection points to your ActiveX control. The following sections discuss these features.
Implementing Stock Properties Using ATL
The easiest way to add a stock property to a control built with ATL is with the ATL Object Wizard. The Stock Properties tab enables you to select stock properties to be added to your control by moving the property name between two lists (see Figure 31.1).
Figure 31.1 The ATL Object Wizard's Stock Properties tab.
The ATL Object Wizard creates a property map in your ATL control's class header that manages the properties exposed by the control. The property map begins with the BEGIN_PROP_MAP macro and ends with the END_PROP_MAP macro. Inside the property map are a series of macros, each specifying a property supported by the control (see Listing 31.1).
Example 31.1. An ATL Property Map
BEGIN_PROP_MAP(CShapedButton)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
PROP_ENTRY("Caption", DISPID_CAPTION, CLSID_NULL)
END_PROP_MAP()
Stock properties are implemented using the PROP_ENTRY macro. PROP_ENTRY has three parameters:
- The description of the stock property
- The dispatch identifier (DISPID) for the stock property
- The class ID (CLSID) of the property page used for entering the property (or CLSID_NULL if no property page is used)
Each stock property has a specific DISPID that refers to the property. DISPID_CAPTION is the dispatch ID for the control's label, for example. The ATL Object Wizard knows all about these names. If you're adding a stock property by hand, you have two choices:
- Create a dummy project and add the stock property using the ATL Object Wizard. You can add the stock property to your project using the DISPID used by the wizard in the dummy project.
- Look in the OLECTL.H header file in the Visual C++ include directory. This file contains all the standard dispatch identifiers used for stock properties, as well as other standard Automation identifiers. Search for DISPID_AUTOSIZE, which is at the start of the list.
In addition to adding an entry to the property map, the ATL Object Wizard adds a member variable to your class declaration for every property exposed by your control. The wizard doesn't add any code that explicitly maps the variable name to the property, however; it's not needed.
The mapping between variable names and properties is done in the ATL class library. Every stock property is mapped to a particular member of an anonymous union declared in the ATL CComControlBase class. ATL Object Wizard uses a name from this union when adding member variables for properties to your control. If you're adding a property by hand, you again have two choices:
- Create a dummy project, as described earlier, and use the variable name selected by the ATL Object Wizard.
- Look in the anonymous union declared in the CComControlBase class, and select the proper variable name yourself.
Whichever method you choose for selecting the variable name, make sure you name the variable correctly. If you declare a variable with a different name, your project will compile, but your property value won't be updated properly.
Implementing Custom Properties Using ATL
Implementing custom properties is similar to implementing stock properties. One obvious difference between stock and custom properties is that you must name the custom properties yourself. You add custom properties by right-clicking the IDL icon in the ClassView window and choosing the Add Properties item from the pop-up menu. A dialog box appears, as shown in Figure 31.2.
Figure 31.2 Adding a property to an ATL object.
You use the Add Property to Interface dialog box to add the named property, the IDL statements, and the glue code necessary in your class so that the property can be exposed to the outside world.
You must provide the following values:
| Property Type | The Automation-compatible type that contains the property. |
| Property Name | The name of the property as it will be exposed to the outside world. This is the name Visual Basic or Visual InterDev displays in property pages for your control, for example. |
| Parameters | Any additional parameters you want to apply to the property. |
| Get Function | If you enable this check box, the option generates a function to retrieve the property value from the control. If you disable this option, the property cannot be read. |
| Put Function | If you enable this check box, the option generates a function to set the control's property. If you disable this option, the property is read-only. |
| PropPut/PropPutRef | If you select the PropPutRef radio button, the property is set by reference, which is more efficient for large objects. If you select PropPut, the property is set with a parameter that is passed by value. |
You can click the Attributes button to set IDL attributes, such as hidden or call_as, for the property.
Consider a project named TestBtn that exposes a BevelSize property, for example. To enable this property, you fill in the Add Property to Interface dialog box with the values shown in Table 31.1.
Table 31.1. Sample Values for the Add Property to Interface Dialog Box
| Control | Value |
| Property Type | long |
| Property Name | BevelSize |
| Parameters | (none) |
| Get Function | Checked |
| Put Function | Checked |
| PropPut | Selected |
Given these values, Visual C++ creates the following IDL for the new property:
interface ITestButton : IDispatch
{
[propget, id(1), helpstring("property BevelSize")]
HRESULT BevelSize([out, retval] long *pVal);
[propput, id(1), helpstring("property BevelSize")]
HRESULT BevelSize([in] long newVal);
};
Visual C++ also will create class member functions to implement the necessary interface methods declared by the IDL fragment. What Visual C++ will not do is declare a member variable for you. You must add a member variable (if needed) and fill in the skeleton member functions provided by Visual C++, as Listing 31.2 shows.
Example 31.2. Using Member Functions to Provide Access to Custom Properties
STDMETHODIMP CTestButton::get_BevelSize(long *pVal)
{
*pVal = m_nBevelSize;
return S_OK;
}
STDMETHODIMP CTestButton::put_BevelSize(long newVal)
{
m_nBevelSize = newVal;
return S_OK;
}
Visual C++ will not automatically add the custom property to the property map, so the property will not be persisted automatically. If you want the property to be persistent, you must add an entry to the property map using the PROP_DATA_ENTRY macro, as Listing 31.3 shows.
Example 31.3. Adding a Custom Property to the Property Map
BEGIN_PROP_MAP(CSlaskPop)
PROP_DATA_ENTRY("_cx", m_sizeExtent.cx, VT_UI4)
PROP_DATA_ENTRY("_cy", m_sizeExtent.cy, VT_UI4)
PROP_DATA_ENTRY("BevelSize", m_nBevelSize, VT_I4)
PROP_ENTRY("Caption", DISPID_CAPTION, CLSID_NULL)
END_PROP_MAP()
The PROP_DATA_ENTRY macro has three parameters:
- The description of the property
- The member variable that contains the property's value
- The Automation tag that suits the variable's type
A complete list of valid Automation tags is located in the Visual C++ online documentation in the COM and ActiveX folder. You will find one good source by searching for the document titled "VARIANT and VARIANTARG."
Using Ambient Properties with ATL
You can retrieve ambient properties by calling functions implemented in the CComControlBase class. Table 31.2 lists three of the most commonly used ambient properties and the functions you use to access them.
Table 31.2. Commonly Used Ambient Properties and Access Functions
| Property | Function |
| Background Color | GetAmbientBackColor |
| Foreground Color | GetAmbientForecolor |
| Font | GetAmbientFont |
The online documentation for Visual C++ has more examples of ambient property functions. Go to the index and search for topics that begin with GetAmbient. CComControlBase also has a catchall member function named GetAmbientProperty that will return the value of any ambient property, given its DISPID.
Adding Message and Event Handlers
You can add handlers for messages and events to an ATL project in two ways. The graphical way is to right-click the ATL object's C++ class icon in ClassView and choose Add Windows Message Handler from the pop-up menu. A dialog box appears, as Figure 31.3 shows.
Figure 31.3 The New Windows Message and Event Handlers dialog box.
You can easily add handlers for Windows messages by selecting a message from the left column and clicking the Add Handler button.
Another option is to manually add the handlers to the class message map using the MESSAGE_HANDLER macro:
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
The MESSAGE_HANDLER macro has two parameters:
- The Windows message to be handled
- The name of the member function that will handle the message
If you're handling events from a WM_COMMAND message, you must use the COMMAND_CODE_HANDLER macro:
COMMAND_CODE_HANDLER(LBN_DBLCLICK, OnLbDblClicked)
The COMMAND_CODE_HANDLER macro has two parameters:
- The notification message sent in WM_COMMAND
- The name of the member function that will handle the message
You can use either of these macros inside the message map, which begins with the BEGIN_MSG_MAP macro and ends with the END_MSG_MAP macro (see Listing 31.4).
Example 31.4. A Typical ATL Message Map
BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
COMMAND_CODE_HANDLER(BN_CLICKED, OnClicked)
ALT_MSG_MAP(1)
// Alternate window message handling
END_MSG_MAP()
Note that the message map in Listing 31.4 also contains an ALT_MAP macro. This marks the beginning of an alternate message map that handle messages from another window within a single message map.
Connection Points | Next Section

Account Sign In
View your cart