Degrading Gracefully in the Absence of an Interface
Because a control may 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 VB) 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, then 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's idea of a control is very restrictive, the object does not gain any of the integration features above. 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.
As 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 like the CDK because of the complexity of implementing all the interfaces, and this has implications when a large DLL like OC30.DLL is required for such a control, increasing the working set. If not all interfaces are required, then 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 appendix 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 ) 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 of those 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, like 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 (like 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 may 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 methods. |
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 may 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 IProvideClassInfo2 |
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 IOleCache2 |
The object doesn't support caching. A container can still support caching by just creating a data cache itself using CreateDataCache. |