Nearly all of the requirements for both controls and control containers are fulfilled using OLE technologies that we've seen in this book. You can guess that properties are most likely handled through OLE Automation and that events are handled through connection points. The complete list of which technologies satisfy which requirements is shown in Table 24-1.
Requirement | OLE Technologies Applied |
Control properties | OLE Automation, Property Change Notifications, Property Pages, Connectable Objects (for change notification) |
Control events | OLE Automation, Connectable Objects |
Control visuals | OLE Documents (including in-place activation) |
Control mnemonics | OLE Controls (in-place activation handles accelerators for the UI-active control that has the focus) |
Control persistence | Structured Storage, Object Persistence (any persistence model) |
Container layout | OLE Documents, OLE Drag and Drop |
Container form persistence | Structured Storage, Object Persistence |
Container ambient properties | OLE Automation, OLE Controls |
Container event handlers | OLE Automation, Connectable Objects |
Container extended objects | OLE Controls |
Container keyboard | OLE Controls |
Table 24-1
OLE technologies used to create OLE Controls.
The entries in Table 24-1 marked "OLE Controls" refer to the specific interfaces and protocols involved only with OLE Controls. The interfaces in question are IOleControl, IOleControlSite, and ISimpleFrameSite.
Structurally, an OLE control is a COM object, housed inside a server (as we saw in Chapter 5), that implements the following interfaces in addition to IUnknown:4 IOleObject, IOleInPlaceObject, IOleInPlaceActiveObject (on a subobject if necessary), IOleControl, IDataObject, IViewObject2, IRunnableObject, IExternalConnection (if linking to embeddings is allowed), IOleCache2, IDispatch, IConnectionPointContainer, ISpecifyPropertyPages, IProvideClassInfo, and one or more of IPersistStorage, IPersistStream, or IPersistStreamInit (depending on the necessary persistence models). Besides implementing these interfaces, a control should also self-register and can optionally support licensing through IClassFactory2. Controls are usually marked with OLEMISC_ACTIVATEWHENVISIBLE, and each must include type information that describes its incoming and outgoing dispinterfaces. In addition, a control's coclass entry in its type information should also be marked with "control." Finally, a control must also support a connection point for each outgoing interface by virtue of implementing IConnectionPointContainer.
To the outside world, then, an OLE control appears as shown in Figure 24-1. It should be obvious that implementing an object with this many interfaces, along with type information, self-registration, licensing, and so on, is not exactly a trivial task. Although Microsoft chose to use existing technologies as a base and to provide maximum flexibility, it is up to tools like the CDK to make implementation less complex.
Figure 24-1.
The structure of an in-process OLE control.
Much of the implementation of an OLE control is to support OLE Automation and OLE Documents. The same applies to a control container, which, in addition to all of those site interfaces for an in-place–capable container that we've seen up to Chapter 22, implements IOleControlSite, ISimpleFrameSite, and IDispatch on the control site as well. (IDispatch exposes the container's ambient properties.) In addition, a site provides sink objects for however many event sets a control has, as well as a sink that implements IPropertyNotifySink. All of this is illustrated on the next page in Figure 24-2, which doesn't show that a container usually supports some of its own user interface for manipulating control properties and events.
Figure 24-2.
The structure of an OLE control container.
As you might have noticed in several places in this book, many of the interfaces for both control and container were first defined expressly for OLE Controls. Controls were the first such components to really require licensing, general connection points, property pages, and so forth.
Because we've already explored the majority of these interfaces in previous chapters, we need to look at only the specific points that are relevant to OLE Controls, many of which are involved with the IOleControl and IOleControlSite interfaces. But before we do this, there are a number of other fine points to cover about control architecture, so the following sections discuss each in detail.
4 This list of interfaces assumes an in-process control. An in-process control can choose not to support caching by not exposing IOleCache2. At the time of writing, some of these interfaces did not support marshaling, so implementing controls in local servers was not possible. Typically, controls are in-process, which is why such support wasn't provided in the first place. |