Contents Index Topic Contents | ||
Previous Topic: Registering an ActiveX Object as the Player for a Media Type Next Topic: Active Documents |
ActiveX Control Containers
The purpose of this topic is to provide guidelines for implementing containers that will interoperate well with other controls and containers. This document defines the minimum set of interfaces, methods, and features that are required of ActiveX containers to accomplish seamless and useful interoperability.
Overview
An ActiveX control container must be able to host a minimal ActiveX control as specified in this document. It will also support a number of additional interfaces. A container can optionally support a number of interfaces and methods, which are grouped into functional areas known as component categories. A container can support any combination of component categories. For example, a component category exists for Databinding; a container may or may not support the data-binding functionality, depending on the market needs of the container. If a control needs data-binding support from a container to function, the control will enter this requirement in the registry. This allows a container to offer for insertion only those controls it knows it can successfully host. It is important to note that component categories are specified as part of OLE and are not specific to ActiveX Controls. The controls architecture uses component categories to identify areas of functionality that an OLE component might support. Component categories are not cumulative or exclusive, so a container can support one category without necessarily supporting another.
These guidelines define those interfaces and methods that a control can expect a container to support, although as always a control should check the return values when using QueryInterface or other methods to obtain pointers to these interfaces. A container should not expect a control to support anything more than the IUnknown interface. These guidelines identify what interfaces a control can support and what the presence of a particular interface means.
Why Are the ActiveX Control Container Guidelines Important?
ActiveX Controls have become the primary architecture for developing programmable software components for use in a variety of different containers, ranging from software development tools to end-user productivity tools. For a control to operate well in a variety of containers, the control must be able to assume some minimum level of functionality that it can rely on in all containers.
By following these guidelines, control and container developers make their controls and containers more reliable and interoperable, and, ultimately, better and more usable components for building component-based solutions.
This document provides guidelines toward good interoperability. It is expected that new interfaces and component categories will develop over time; future versions of this document reflecting these changes will be made readily available through Microsoft. It is important to note that this document does not cover detailed semantics of the OLE interfaces; this is covered by the SDK documentation.
What If an Interface You Need Is Not Available?
This section states some fundamental rules that apply to all OLE programming. OLE programs should use QueryInterface to acquire interface pointers, and must check the return value. OLE applications cannot safely assume that QueryInterface will succeed; this requirement applies to all OLE applications. If the requested interface is not available (that is, QueryInterface returns E_NOINTERFACE), the control or container must degrade gracefully, even if that means it cannot perform its designated job function.
What's New?
This release of the guidelines embraces the concept of component categories, which are a part of the OLE specification. In previous versions of this document, component categories were loosely referred to as "function groups" and were used to identify areas of functionality that a container can optionally support. For this version there has been a definition of how component categories work for ActiveX Controls, and some fundamental categories are identified. The use of component categories allows the relaxing of some of the previous rules that identified interfaces as being mandatory, and allows greater flexibility for controls to efficiently target certain areas of functionality without having to provide superfluous additional support in order to qualify as a control. This edition of the guidelines also discusses what the presence or absence of an interface means and what to do in that situation.
ActiveX Control Containers
ActiveX control containers must provide support for the following features:
- Embedded objects from in-process servers
- In-place activation
- OLEMISC_ACTIVATEWHENVISIBLE
- Event handling
The following sections describe the specific interfaces, methods, and other features that are required of ActiveX control containers. The last section describes how to gracefully degrade when a particular control interface is not supported.
Required Interfaces
The following table lists the ActiveX control container interfaces, denoting which interfaces are optional, and which are mandatory and must be implemented by containers.
Interface Support mandatory? Comments IOleClientSite Yes IAdviseSink No Only when the container wants data change notifications (controls with IDataObject), view change notification (controls that are not active and have IViewObject[2]), and other notifications from controls acting as standard embedded objects. IOleInPlaceSite Yes IOleControlSite Yes IOleInPlaceFrame Yes IOleContainer Yes See Note 1. IDispatch for ambient properties Yes See Note 2 and "Ambient Properties" section Control Event Sets Yes See Note 2. ISimpleFrameSite No This interface and support for nested simple frames is optional. IPropertyNotifySink No Only needed for containers that have their own property-editing UI, which would require updating whenever a control changed a property itself, or containers that want to control [requestedit] property changes and other such data-binding features. IErrorInfo Yes Mandatory if container supports dual interfaces. See Note 2. IClassFactory2 No Support is strongly recommended. Notes:
- IOleContainer is implemented on the document or form object (or appropriate analog) that holds the container sites. Controls use IOleContainer to navigate to other controls in the same document or form.
- Support for dual interfaces is not mandatory, but is strongly recommended. Writing ActiveX control containers to take advantage of dual interfaces will afford better performance with controls that offer dual interface support.
ActiveX control containers must support OLE Automation exceptions. If a container supports dual interfaces, it must capture automation exceptions through IErrorInfo.
Optional Methods
An OLE component can implement an interface without implementing all the semantics of every method in the interface, instead returning E_NOTIMPL or S_OK as appropriate. The following table describes those methods that an ActiveX control container is not required to implement (that is, the container can return E_NOTIMPL).
The table also describes optional methods; note that the method must still exist, but can simply return E_NOTIMPL instead of implementing "real" semantics. Note that any method from a mandatory interface that is not listed in the table must be considered mandatory and cannot return E_NOTIMPL.
Method Comments IOleClientSite SaveObject Necessary for persistence to be successfully supported. GetMoniker Necessary only if the container supports linking to controls within its own form or document. IOleInPlaceSite ContextSensitiveHelp Optional. Scroll Can return S_FALSE with no action. DiscardUndoState Can return S_OK with no action. DeactivateAndUndo Deactivation is mandatory; Undo is optional. IOleControlSite GetExtendedControl Necessary for containers that support extended controls. ShowPropertyFrame Necessary for containers that want to include their own property pages to handle extended control properties in addition to those provided by a control. TranslateAccelerator Can return S_FALSE with no action. LockInPlaceActive Optional. IDispatch (ambient properties) GetTypeInfoCount Necessary for containers that support nonstandard ambient properties. GetTypeInfo Necessary for containers that support nonstandard ambient properties. GetIDsOfNames Necessary for containers that support nonstandard ambient properties. IDispatch (event sink) GetTypeInfoCount The control knows its own type information, so it has no need to call this. GetTypeInfo The control knows its own type information, so it has no need to call this. GetIDsOfNames The control knows its own type information, so it has no need to call this. IOleInPlaceFrame ContextSensitiveHelp GetBorder Necessary for containers with toolbar UI (which is optional). RequestBorderSpace Necessary for containers with toolbar UI (which is optional). SetBorderSpace Necessary for containers with toolbar UI (which is optional). InsertMenus Necessary for containers with menu UI (which is optional). SetMenu Necessary for containers with menu UI (which is optional). RemoveMenus Necessary for containers with menu UI (which is optional). SetStatusText Necessary only for containers that have a status line. EnableModeless Optional. TranslateAccelerator Optional. IOleContainer ParseDisplayName Only if linking to controls or other embeddings in the container is supported, as this is necessary for moniker binding. LockContainer As for ParseDisplayName. EnumObjects Returns all ActiveX Controls through an enumerator with IEnumUnknown, but not necessarily all objects (since there's no guarantee that all objects are ActiveX Controls; some might be regular Windows® controls). Miscellaneous Status Bits Support
ActiveX control containers must recognize and support the following OLEMISC_ status bits.
Status bit Support mandatory? Comments ACTIVATEWHENVISIBLE Yes IGNOREACTIVATEWHENVISIBLE No Needed for inactive and windowless control support. See Note 1. INSIDEOUT No Not generally used with ActiveX Controls but rather with compound document embeddings. Note this is contrary to some SDK documentation that says this bit must be set for the ACTIVATEWHENVISIBLE bit to be set. INVISIBLEATRUNTIME Yes Designates a control that should be visible at design time, but invisible at run time. ALWAYSRUN Yes ACTSLIKEBUTTON No Support is normally mandatory, although it is not necessary for document-style containers. ACTSLIKELABEL No Support is normally mandatory, although it is not necessary for document-style containers. NOUIACTIVATE Yes ALIGNABLE No SIMPLEFRAME No SETCLIENTSITEFIRST No Support for this bit is recommended but not mandatory. IMEMODE No Notes:
- The IGNOREACTIVATEWHENVISIBLE bit is for containers hosting inactive and windowless controls. The bit is introduced as part of the ActiveX Controls 96 specification; see this documentation for more details.
Keyboard Handling
Keyboard handling support for the following functionality is strongly recommended, although it is recognized that it is not applicable to all containers:
- Support for OLEMISC_ACTSLIKELABEL and OLEMISC_ACTSLIKEBUTTON status bits.
- Implementing the DisplayAsDefault ambient property (if it exists, it can return FALSE).
- Implementing tab handling, including tab order.
Some containers use ActiveX Controls in traditional compound document scenarios. For example, a spreadsheet might allow a user to embed an ActiveX control into a worksheet. In such scenarios, the container would do keyboard handling differently, because the keyboard interface should remain consistent with the user's expectations of a spreadsheet. Documentation for such products should inform users of differences in control handling in these different scenarios. Other containers should endeavor to honor the above functionality correctly, including mnemonic handling.
Storage Interfaces
Containers must be able to support controls that implement IPersistStorage, IPersistStream, or IPersistStreamInit. Optionally, a container can support any other persistence interfaces, such as IPersistMemory, IPersistPropertyBag, and IPersistMoniker, for those controls that provide support.
After an ActiveX control container has chosen and initialized a storage interface to use (IPersistStorage, IPersistStream, IPersistStreamInit, and so on), that storage interface will remain the primary storage interface for the lifetime of the control; that is, the control will remain in possession of the storage. This does not preclude the container from saving to other storage interfaces.
ActiveX control containers do not need to support a "save as text" mechanism, thus using IPersistPropertyBag and the associated container-side interface IPropertyBag are optional.
Ambient Properties
At a minimum, ActiveX control containers must support the following ambient properties using the standard DISPIDs.
Ambient property DISPID Comments/Conditions LocaleID -705 UserMode -709 For containers that have different user and run environments. DisplayAsDefault -713 For those containers where a default button is relevant. Extended Properties, Events, and Methods
ActiveX control containers are not required to support extended controls. However, if the container does support extended properties, it must support the following minimal set:
Visible Parent Default Cancel Currently, extended properties, events, and methods do not have standard DISPIDs.
Message Reflection
It is strongly recommended that an ActiveX control container support message reflection. This will result in more efficient operation for subclassed controls. If message reflection is supported, the MessageReflect ambient property must be supported and have a value of TRUE. If a container does not implement message reflection, the OLE CDK creates two windows for every subclassed control, to provide message reflection on behalf on the container.
Automatic Clipping
It is strongly recommended that an ActiveX control container support automatic clipping of its controls. This will result in more efficient operation for most controls. If automatic clipping is supported, the AutoClip ambient property must be supported and have a value of TRUE.
Automatic clipping is the ability of a container to ensure that a control's drawn output goes only to the container's current clipping region. In a container that supports automatic clipping, a control can paint without regard to its clipping region, because the container will automatically clip any painting that occurs outside the control's area. If a container does not support automatic clipping, CDK-generated controls will create an extra parent window if a nonnull clipping region is passed.
Degrading Gracefully in the Absence of an Interface
Because a control might not support any interface other than IUnknown, a container has to degrade gracefully when it encounters the absence of any particular interface.
One might question the usefulness of a "control" with nothing more than IUnknown. But consider the advantages that a control receives from a container's visual programming environment (such as Microsoft Visual Basic®) when the container recognizes the object as a "control":
- A button for the object appears in a toolbox.
- One can create an object by dragging it from the toolbox onto a form.
- One can give the object a name that is recognized in the visual programming environment.
- The same name in (3) above can be used immediately in writing any other code for controls on the same form (or even a different form).
- The container can automatically provide code entry points for any events available from that object.
- The container provides its own property browsing UI for any available properties.
When an object isn't recognized as a "control," it potentially loses all of these very powerful and beneficial integration features. For example, in Visual Basic 4.0 it is very difficult to really integrate some random object that is not a "control" in the complete sense, but may still have properties and events. Because Visual Basic 4.0's idea of a control is very restrictive, the object does not gain any of the integration features mentioned previously. But even a control with IUnknown, where the mere lifetime of the control determines the existence of some resource, should be able to gain the integration capabilities described above.
Because current tools require a large set of control interfaces to gain any advantage, controls are generally led to over-implementation, such that they contain more code than they really need. Controls that could be 7K might end up being 25K, which is a big performance problem in areas such as the Internet. This has also led to the perception that one can only implement a control with one tool, such as the CDK, because of the complexity of implementing all the interfaces. This has implications when a large DLL such as Oc30.dll is required for such a control, increasing the working set. If not all interfaces are required, this opens up many developers to writing very small and light controls with straight OLE or with other tools as well, minimizing the overhead for each control.
This is why this document recognizes a "control" as any object with a CLSID and an IUnknown interface. Even with nothing more than IUnknown, a container with a programming environment should be able to provide at least features (3) and (4) from the list above. If the object provides a ToolBoxBitmap32 registry entry, it gains (1) and (2). If the object supplies IConnectionPointContainer (and IProvideClassInfo generally) for some event set, it gains (5), and if it supports IDispatch for properties and methods, it gains (6), as well as better code integration in the container.
In short, an object should be able to implement as little as IDispatch and one event set exposed through IConnectionPointContainer to gain all the visual features above.
With this in mind, the following table describes what a container might do in the absence of any possible interface. Note that only those interfaces are listed that the container will directly obtain through QueryInterface. Other interfaces, such as IOleInPlaceActiveObject, are obtained through other means.
Interface Meaning of interface absence IViewObject2 The control has no visuals that it will draw itself, so has no definite extents to provide. In run time, the container simply doesn't attempt to draw anything when this interface is absent. In design time, the container must at least draw some kind of default rectangle with a name in it for such a control, so a user in a visual programming environment can select the object and check out its properties, methods, and events that exist. Handling the absence of IViewObject2 is critical for good visual programming support. IOleObject The control doesn't need the site whatsoever, nor does it take part in any embedded object layout negotiation. Any information (such as control extents) that a container might expect from this interface should be filled in with container-provided defaults. IOleInPlaceObject The control doesn't go in-place active (like a label) and thus never attempts to activate in this manner. Its only activation might be its property pages. IOleControl Control has no mnemonics and no use of ambient properties, and doesn't care if the container ignores events. In the absence of this interface, the container just doesn't call its members. IDataObject The control provides no property sets nor any visual renderings that could be cached, so the container would choose to cache some default presentation in the absence of this interface (support for CF_METAFILEPICT, specifically) and disable any property-set related functionality. IDispatch The control has no custom properties or methods. The container does not need to try to show any control properties in this case, and should disallow any custom method calls that the container doesn't recognize as belonging to its own extended controls (that might support methods and properties). As extended controls generally delegate certain IDispatch calls to the control, an extended control should not expect the control to have IDispatch at all. IConnectionPointContainer The control has no events, so the container doesn't have to think about handling any. IProvideClassInfo[2] The control either doesn't have type information or events, or the container needs to go into the control's type information through the control's registry entries. The existence of this interface is an optimization. ISpecifyPropertyPages The control has no property pages, so if the container has any UI that would invoke them, the container should disable that UI. IPerPropertyBrowsing The control has no display name itself, no predetermined strings and values, and no property to page mapping. This interface is nearly always used for generating container user interface, so such UI elements would be disabled in the absence of this interface. IPersist* The control has no persistent state to speak of, so the container doesn't have to worry about saving any control-specific data. The container will, of course, save its own information about the control in its own form or document, but the control itself has nothing to contribute to that information. IOleCache[2] The object doesn't support caching. A container can still support caching by just creating a data cache itself using CreateDataCache. General Guidelines
This section describes various features, hints, and tips for ActiveX control and ActiveX control container developers to help ensure good interoperability between controls and containers.
Overloading IpropertyNotifySink
Many ActiveX control containers implement a modeless property browsing window. If a control's properties are altered through the control's property pages, the control's properties can get out of sync with the container's view of those properties (the control is always right, of course). To ensure that it always has the current values for a control's properties, an ActiveX control container can overload the IPropertyNotifySink interface (data binding) and also use it to be notified that a control property has changed. This technique is optional, and is not required of ActiveX control containers or ActiveX Controls.
Note A control should use IPropertyNotifySink::OnRequestEdit only for data binding; it is free to use OnChanged for either or both purposes.
Container-Specific Private Interfaces
Some containers provide container-specific private interfaces for additional functionality or improved performance. Controls that rely on those container-specific interfaces should, if possible, work without those container-specific interfaces present so that the control functions in different containers. For example, Visual Basic implements private interfaces that provide string formatting functionality to controls. If a control makes use of the Visual Basic private formatting interfaces, it should be able to run with default formatting support if these interfaces are not available. If the control can function without the private interfaces, it should take appropriate action (such as warn the user of reduced functionality) but continue to work. If this is not an option, a component category should be registered as required to ensure that only containers supporting this functionality can host these controls.
Multithreaded Issues
Starting with Microsoft Windows 95 and Microsoft Windows NT® 3.51, OLE provides support for multithreaded applications, allowing applications to make OLE calls from multiple threads. This multithreaded support is called the "apartment model"; it is important that all OLE components using multiple threads follow this model. The apartment model requires that interface pointers are marshaled (using CoMarshalInterface and CoUnmarshalInterface) when passed between threads. For more information about apartment model threading, refer to the Platform SDK documentation and the OLEAPT sample (in the Platform SDK).
Event Freezing
A container can notify a control that it is not ready to respond to events by calling IOleControl::FreezeEvents(TRUE). It can unfreeze the events by calling IOleControl::FreezeEvents(FALSE). When a container freezes events, it is freezing event processing, not event receiving; that is, a container can still receive events while events are frozen. If a container receives an event notification while its events are frozen, the container should ignore the event. No other action is appropriate.
A control should take note of a container's call to IOleControl::FreezeEvents(TRUE) if it is important to the control that an event is not missed. While a container's event processing is frozen, a control should implement one of the following techniques:
- Fire the events in the full knowledge that the container will take no action.
- Discard all events that the control would have fired.
- Queue up all pending events and fire them after the container has called IOleControl::FreezeEvents(FALSE).
- Queue up only relevant or important events and fire them after the container has called IOleControl::FreezeEvents(FALSE).
Each technique is acceptable and appropriate in different circumstances. The control developer is responsible for determining and implementing the appropriate technique for the control's functionality.
Container Controls
As described previously, container controls are ActiveX Controls that visually contain other controls. The ActiveX Controls architecture specifies the ISimpleFrameSite interface to enable container controls. Containers can also support container controls without supporting ISimpleFrameSite, although the behavior cannot be guaranteed. For this reason, a component category exists for SimpleFrameSite controls where the full functionality of this interface is required.
To support container controls without implementing ISimpleFrameSite, an ActiveX control container must:
- Activate all controls at all times.
- Reparent the contained controls to the hWnd of the containing control.
- Remain the parent of the container control.
WS_GROUP and WS_TABSTOP Flags in Controls
A control should not use the WS_GROUP and WS_TABSTOP flags internally; some containers rely on these flags to manage keyboard handling.
Multiple Controls in One DLL
A single .ocx DLL can container any number of ActiveX Controls, thus simplifying the distribution and use of a set of related controls.
If you ship multiple controls in a single DLL, be sure to include the vendor name in each control name in the package. Including the vendors' names in each control name will enable users to easily identify controls within a package. For example, if you ship a DLL that implements three controls, Con1, Con2 and Con3, the control names should be:
<Your company name> Con1 Control <Your company name> Con2 Control <Your company name> Con3 ControlIOleContainer::EnumObjects
This method is used to enumerate over all the OLE objects contained in a document or form, returning an interface pointer for each OLE object. The container must return pointers to each OLE object that shares the same container. Nested forms or nested controls must also be enumerated.
Some containers implement "extender controls," which wrap non-ActiveX controls, and then return pointers to these extender controls as a form is enumerated. This behavior enables ActiveX Controls and ActiveX control containers to integrate well with non-ActiveX controls, and is thus recommended but not required.
Enhanced Metafiles
Not surprisingly, enhanced metafiles provide more functionality than standard metafiles; using enhanced metafiles generally simplifies rendering code. An enhanced metafile device context (DC) is used in exactly the same way as a standard metafile DC. Enhanced metafiles are not available in 16-bit OLE. OLE supports enhanced metafiles, and includes backward compatibility with standard metafiles and 16-bit applications.
A 32-bit ActiveX control container should use enhanced metafiles instead of standard metafiles.
Licensing
In order to embed licensed controls successfully, ActiveX control containers must use IClassFactory2 instead of IClassFactory. Several OLE creation and loading helper functions (for example, OleLoad and CoCreateInstance) explicitly call IClassFactory and not IClassFactory2, and therefore cannot be used to create or load licensed ActiveX Controls. ActiveX control containers should explicitly create and load ActiveX Controls using IClassFactory2. In the future, Microsoft will update these standard APIs to use both IClassFactory and IClassFactory2, as appropriate.
Dual Interfaces
OLE Automation enables an object to expose a set of methods in two ways: through the IDispatch interface, and through direct OLE Vtable binding. IDispatch is used by most tools available today, and offers support for late binding to properties and methods. Vtable binding offers much higher performance because this method is called directly instead of through IDispatch::Invoke. IDispatch offers late bound support, where direct Vtable binding offers a significant performance gain; both techniques are valuable and important in different scenarios. By labeling an interface as "dual" in the type library, an OLE Automation interface can be used through IDispatch, or it can be bound to directly. Containers can thus choose the most appropriate technique. Support for dual interfaces is strongly recommended for both controls and containers.
IPropertyBag and IpersistPropertyBag
IPropertyBag and IPersistPropertyBag optimize "save as text" mechanisms, and therefore are recommended for ActiveX control containers that implement a "save as text" mechanism. IPropertyBag is implemented by a container, and is roughly analogous to IStream. IPersistPropertyBag is implemented by controls, and is roughly analogous to IPersistStream.
Related Topics
COM Objects for the Internet Asynchronous and URL Monikers Hyperlinks
Top of Page
© 1997 Microsoft Corporation. All rights reserved. Terms of Use.