A complete review of the COM interfaces and interactions between an ActiveX control and a control container is outside the scope of this book. If you are unfamiliar with the various interfaces and interactions described in this chapter, various other texts specifically address these topics. Inside OLE (Microsoft Press, 1995), by Kraig Brockschmidt, is the original COM text; it devotes hundreds of pages to in-place activation and visual interface components.
An ActiveX control is a superset of an in-place activated object, so you also need to read the OLE Controls Specification from Microsoft, which describes the requirements to be a control. In addition, the OLE Controls 1996 Specification, commonly referred to as the OC96 spec, documents optimizations for control activations (such as windowless controls and windowless control containment), two-pass rendering for nonrectangular windows, hit testing for nonrectangular windows, fast-activation protocols between controls and containers, and numerous other features.
Instead of rewording the material available in these references, I show you how to implement such an object. This chapter describes how to implement a feature-complete ActiveX control using ATL.
ActiveX Control Functionality
A control incorporates much of the functionality you saw in earlier chapters. For example, a control is a COM object. Therefore, an ATL control contains all the standard functionality of an ATL-based COM object. A control is a user-interface (UI) component; therefore, it has thread affinity and should live in a single-threaded apartment. A control thus derives from the CComObjectRootEx<CComSingleThreadModel> base class.
A control must be a createable class so its container can instantiate it. Therefore, the control class also derives from CComCoClass. Many controls use the CComCoClass default class object's implementation of the IClassFactory interface. Licensed controls override this default by specifying the DECLARE_CLASSFACTORY2 macro, which declares a class object that implements the IClassFactory2 interface.
In addition, most controls support one or more of the following features:
Stock properties and methods such as ForeColor and Refresh that a container can access via the control's IDispatch implementation.
Custom properties and methods that a container can access via the control's IDispatch implementation.
Stock and custom event callback methods using the connection points protocol to a container's dispinterface implementation. This requires the control to implement the IConnectionPointContainer and IProvideClassInfo2 interfaces, as well as a connection point that makes calls to the event dispinterface.
Property change notifications to one or more clients using the connection points protocol to the clients' IPropertyNotifySink interface implementations. Control properties that send such change notifications should be marked in the control's type library using the bindable or requestedit attributes, as appropriate.
On-demand rendering of a view of the object via the IViewObject, IView-Object2, and IViewObjectEx interfaces.
Standard OLE control functionality, as provided by the IOleControl interface, and in-place activation using the IOleObject and IOleInPlaceActiveObject interfaces.
Persistence support for various containers. At a minimum, a control typically provides support so that a container can save the object into a stream using the IPersistStreamInit interface. Many controls also support persistence to a property bag using IPersistPropertyBag because Visual Basic and Internet Explorer prefer this medium. Some controls additionally support IPersistStorage so that they can be embedded into OLE documents.
Fast and efficient windowless activation, as provided by the IOleInPlace-ObjectWindowless interface when the control's container supports this optimization.
Fast and efficient exchange of multiple interfaces during activation between a control and its controls using the IQuickActivate interface.
Object safety settings either through component category membership or via IObjectSafety.
Drag-and-drop support, as provided by implementations of the IDataObject, IDropSource, and IDropTarget interfaces.
A graphical user interface that provides a means to edit the control's properties. Typically, a control provides one or more COM objects, called property pages, each of which displays a user interface that can modify a logically related subset of the control's properties. A container requests the CLSIDs of the property page COM objects using the control's ISpecifyPropertyPages interface implementation.
A container's capability to access information about the properties of a control that supports property pages by using the IPerPropertyBrowsing interface. For example, the container can obtain a text string describing a property, determine which property page contains the user interface to edit the property, and retrieve a list of strings describing the allowed values for the property.
Support for arranging the control's properties by category in Visual Basic's property view. A control implements the ICategorizeProperties interface to provide the list of categories to Visual Basic and to map each property to a category.
Default keyboard handling for an ActiveX control. This is commonly needed for tabbing, default button presses on Enter, arrow keys, and pop-up help.
MiscStatus flags settings for a control. Special settings are necessary for some controls to operate properly.
Property Page Functionality
Because a control frequently provides one or more property pages, a complete control implementation also supplies one or more property page objects, which do the following:
Implement (at least) the IPropertyPage interface, which provides the main features of a property page object.
Optionally implement the IPropertyPage2 interface to support selection of a specific property. Visual Basic uses this support to open the correct property page and set the input focus directly to the specified control when the user wants to edit a property.
Receive property change notifications from one or more controls using the connection points protocol to the property page's IPropertyNotifySink interface implementation.