Introduction to ActiveX Control Containers
This article is an overview for Microsoft ActiveX control containers. The following sections explain how to implement and program ActiveX control containers that will function well with other controls and containers.
- Container Requirements
- Required and Optional Interfaces
- Not Required and Optional Methods
- Status Bit Support
- Requirements for Special Features
- Degrading When an Interface Is Not Supported
- Unsupported Interfaces
- Programming Considerations
- Programming Guidelines
- Related topics
An ActiveX control container supplies an environment in which an ActiveX control can run. Additionally, control containers manipulate, manage, and provide services to all the controls they contain. For example, containers supply controls with event handlers and deal with properties. At a minimum, a container must be able to host an ActiveX control. Additionally, it may support component categories, which are sets of interfaces grouped into areas of functionality and assigned a GUID. For example, data binding is a component category. A container may or may not support data binding depending on the needs of its ActiveX control. If a control needs data binding support from a container, the control enters this requirement in the registry. By using the registry information, the container offers for insertion only those controls that it can support.
ActiveX controls have become the primary architecture for developing software components for use in a variety of containers. For a control to operate well in different containers, it must be able to rely on a minimum level of container functionality.
ActiveX control containers must provide support for the following:
- Embedded objects from in-process servers
- In-place activation
- Event handling
- The OLEMISC_ACTIVATEWHENVISIBLE status bit of the OLEMISC enumeration
This section addresses the requirements for interfaces, methods, status bits, and special features of ActiveX control containers.
Required and Optional Interfaces
Specific interfaces are used to program ActiveX control containers. The following table lists those interfaces and shows which are optional and which are mandatory.
|IAdviseSink||Optional||This interface is required only when the container requires notifications, such as data change notifications from controls with IDataObject, view change notifications from controls that are not active and have IViewObject2, and other notifications from controls acting as standard embedded objects.|
|IClassFactory2||Optional||This interface is not required but supporting it is recommended.|
|IDispatch for ambient properties||Required|
|IErrorInfo||Required||IErrorInfo is mandatory if a container supports dual interfaces.|
|IOleContainer||Required||IOleContainer is implemented on the document or form object that holds the container sites. Controls use the IOleContainer to navigate to other controls in the same document or form.|
|IPropertyNotifySink||Optional||This interface is only needed for containers that have their own property-editing UI.|
|ISimpleFrameSite||Optional||This interface, as well as support for nested simple frames is optional.|
Not Required and Optional Methods
An ActiveX control container can implement an interface without implementing every method in the interface. For methods that are not mandatory the container may simply return E_NOTIMPL, S_FALSE, or S_OK, as appropriate. There are two types of nonmandatory methods; not required and optional. Methods that are not required do not need to be implemented. Optional methods must be implemented but may return E_NOTIMPL.
The following table identifies and describes the not required and optional methods. Except for the methods listed in the table, all methods from the required ActiveX control container interfaces must be implemented and cannot return E_NOTIMPL.
|IDispatch for ambient properties||GetIDsOfNames||Not required. This method is necessary only for containers that support nonstandard ambient properties.|
|GetTypeInfo||Not required. This method is necessary only for containers that support nonstandard ambient properties.|
|GetTypeInfoCount||Not required. This method is necessary only for containers that support nonstandard ambient properties.|
|IDispatch for an event sink||GetIDsOfNames||Not required. This method is not necessary for an event sink.|
|GetTypeInfo||Not required. This method is not necessary for an event sink.|
|GetTypeInfoCount||Not required. This method is not necessary for an event sink.|
|IOleClientSite||GetMoniker||Not required. This method is necessary only if the container supports linking to controls within its own form or document.|
|SaveObject||Not required. This method is necessary only if persistence is supported.|
|IOleContainer||EnumObjects||Not required. This method is only necessary to enumerate ActiveX controls.|
|LockContainer||Not required. This method is only necessary for containers that link to controls or other embedded objects.|
|ParseDisplayName||Not required. This method is only necessary for containers that link to controls or other embedded objects.|
|IOleControlSite||GetExtendedControl||Not required. This method is only necessary for containers that support extended controls.|
|ShowPropertyFrame||Not required. This method is necessary only for containers that want to include their own property pages to handle extended control properties in addition to those provided by a control.|
|TranslateAccelerator||Not required. This method can return S_FALSE and perform no action.|
|GetBorder||Not required. This method is necessary only for containers with toolbar UI.|
|InsertMenus||Not required. This method is necessary only for containers with menu UI.|
|RemoveMenus||Not required. This method is necessary only for containers with menu UI.|
|RequestBorderSpace||Not required. This method is necessary only for containers with toolbar UI.|
|SetBorderSpace||Not required. This method is necessary only for containers with toolbar UI.|
|SetMenus||Not required. This method is necessary only for containers with menu UI.|
|SetStatusText||Not required. This method is necessary only for containers that have a status line.|
|DeactivateAndUndo||Not required. This method must implement deactivation, but undo is optional.|
|DiscardUndoState||Not required. This method can return S_OK and perform no action.|
|Scroll||Not required. This method can return S_FALSE and perform no action.|
Status Bit Support
ActiveX control containers must also recognize and support certain status bits of the OLEMISC enumeration. The following table shows which of the OLEMISC status bits are required and which are optional.
|IGNOREACTIVATEWHENVISIBLE||Optional||This bit is set for containers hosting inactive and windowless controls.|
|INVISIBLEATRUNTIME||Required||This bit designates a control that should be visible at design time, but invisible at run time.|
|ACTSLIKEBUTTON||Optional||Support for this bit is normally required, although it is not necessary for document-style containers.|
|ACTSLIKELABEL||Optional||Support for this bit is normally required, although it is not necessary for document-style containers.|
|SETCLIENTSITEFIRST||Optional||Support for this bit is recommended but is not required.|
Requirements for Special Features
In addition to the mandatory interfaces, methods, and status bits, special features like keyboard handling and automatic clipping require certain functionalities for implementation. The following table lists some special feature requirements.
|Keyboard Handling||The following are required for a container to support a keyboard:
|Storage Interfaces||Containers must be able to support controls that implement IPersistStorage, IPersistStream, or IPersistStreamInit. Optionally, a container can support any other persistence interface, such as IPersistMemory, IPersistPropertyBag, and IPersistMoniker.
After an ActiveX control container has selected and initialized a storage interface, the storage interface will remain the primary storage interface for the life of the control. This does not preclude the container from saving to other storage interfaces.
|Ambient Properties||At a minimum, ActiveX control containers must support the following ambient properties using the standard DISPIDs.
|Extended Controls||ActiveX control containers are not required to support extended controls. However, if the container does support extended controls, it must support the following:
|Message Reflection||ActiveX control containers are not required to support message reflection. Message reflection is the ability of a subclassed control to handle notification messages itself, rather than have them handled by the container.
Using controls that can handle their own messages results in more efficient controls. If a container supports message reflection, the MessageReflect ambient property must be supported and set to TRUE.
|Automatic Clipping||ActiveX control containers are not required to support automatic clipping. Automatic clipping is the ability of a container to ensure that a control's visual output goes only to the container's current clipping region.
Using containers that support automatic clipping results in more efficient controls because a control can paint without regard to its clipping region. The container will automatically clip any painting that occurs outside the control's area. If a container supports automatic clipping, the AutoClip property must be supported and set to TRUE.
Degrading When an Interface Is Not Supported
All controls do not support all interfaces. In fact, there are controls that support only the IUnknown interface. When a container encounters a control that does not support a required interface, the container must degrade.
The following table describes what a container might do in the absence of a particular interface. Note that the table lists only those interfaces that a container can obtain through QueryInterface.
|IViewObject2||If the IViewObject2 interface is unsupported, a control has no extents and at run time draws nothing. At design time, the container draws a default rectangle, so a user in a visual programming environment can select the object and check its properties, methods, and events. Your application should be prepared to handle the lack of a visual control.|
|IOleObject||Any information, such as control extents, that a container might expect from this interface should be filled in with container-provided defaults.|
|IOleInPlaceObject||If this interface is absent, the control does not attempt to activate in-place. Therefore no handling is required.|
|IOleControl||In the absence of this interface, the container does not call its members.|
|IDataObject||The control provides no property sets or visual renderings that can be cached, so in the absence of this interface the container should cache some default data.|
|IDispatch||The control has no custom properties or methods. Therefore the container does not need to show any control properties and should not allow any custom method calls.|
|IConnectionPointContainer||The control has no events, so the container does not have to handle events.|
|IProvideClassInfo2||If this interface is missing, the control either does not have type information or events, or the container must obtain the control's type information from the control's registry entries.|
|ISpecifyPropertyPages||The control has no property pages. Therefore if the container has any UI that invokes property pages, the container should disable the UI.|
|IPerPropertyBrowsing||The control has no display name, no predetermined strings and values, and no property-to-page mapping. This interface is used to generate a container user interface. Therefore in the absence of this interface, UI elements should be disabled.|
|IPersist* Objects||The control has no persistent state, so the container does not have to save any control-specific data.|
|IOleCache2||The control does not support caching.|
This section describes some guidelines for ActiveX control and ActiveX control container developers.
The following table offers programming suggestions to produce more reliable and interoperable components.
|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. To ensure that a container always has the current values for a control's properties, an ActiveX control container can overload the IPropertyNotifySink interface and use it to notify the container that a control property has changed.|
|Container-Specific private interfaces||Some containers provide container-specific private interfaces for additional functionality or improved performance. Controls that rely on container-specific interfaces should, if possible, work without those interfaces so that the control will function in different containers. For example, Microsoft Visual Basic implements private interfaces that provide string formatting functionality to controls. If a control makes use of those private interfaces, it should be able to run with default formatting support if the interfaces are not available.|
|Multithreaded issues||ActiveX provides support for multithreaded applications, allowing applications to make ActiveX calls from multiple threads. This multithreaded support is called the apartment model. It is important that all ActiveX components using multiple threads follow this model. For more information about apartment model threading, refer to the Windows Software Development Kit (SDK) documentation.|
|Event freezing||A container can notify a control that it is not ready to respond to events by calling IOleControl::FreezeEvents(TRUE). When a container freezes events, it is freezing event processing, not event receiving. 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. If it is important for a control to process an event, the control should take note of an IOleControl::FreezeEvents(TRUE) call. While a container's event processing is frozen, a control should implement one of the following techniques:
|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 contain any number of ActiveX controls, 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. Including the vendor's name in each control enables 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:
|IOleContainer::EnumObjects||This method is used to enumerate all the OLE objects contained in a document or form, returning an interface pointer for each object. The container must return pointers to each ActiveX 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 a pointer 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.
|Enhanced metafile||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. A 32-bit ActiveX control container should use enhanced metafiles, but 16-bit containers must use standard metafiles. ActiveX supports enhanced metafiles and includes backward compatibility with standard metafiles.|
|Licensing||In order to embed licensed controls successfully, ActiveX control containers must use IClassFactory2 instead of IClassFactory. Several ActiveX creation and loading helper functions, such as OleLoad and CoCreateInstance, call IClassFactory and not IClassFactory2, and therefore cannot be used to create or load licensed ActiveX controls.|
|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 offers late binding support, but Vtable binding offers 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.|
|IPropertyBag and IPersistPropertyBag||ActiveX control containers that implement a "save as text" mechanism should use IPropertyBag and IPersistPropertyBag. IPropertyBag is implemented by a container and is roughly analogous to IStream. IPersistPropertyBag is implemented by controls, and is roughly analogous to IPersistStream.|