缺少接口时正常降级

由于控件可能不支持除 IUnknown 以外的任何接口,因此当容器缺少何特定接口时,容器必须正常降级。

人们可能会质疑控件仅适用于 IUnknown。 但是,要考虑到当容器将对象识别为控件时,控件从容器的可视编程环境(如 VB)获得的好处:

  • 对象的按钮将显示在工具箱中。
  • 通过将对象从工具箱拖到窗体上,可以创建对象。
  • 可以为对象提供在可视编程环境中可识别的名称。
  • 上面 (3) 中的同一名称可以直接用于为同一窗体(甚至其他窗体)上的控件编写任何其他代码。
  • 容器可以自动为该对象中提供的任何事件提供代码入口点。
  • 容器可为任何可用属性提供自己的属性浏览 UI。

当无法将对象识别为控件时,对象可能会丧失所有这些非常强大且有益的集成功能。 例如,在 Visual Basic 4.0 中,很难真正集成一些不是完全意义上的控件的随机对象,但仍可能具有属性和事件。 由于 Visual Basic 4 对控件的概念非常严格,因此对象不会获得上述任何集成功能。 但是,即使是具有 IUnknown 的控件,其中仅控件的生存期确定是否提供某些资源,也应该能够获得上述集成功能。

由于当前工具需要大量的控件接口才能获得任何好处,因此通常会导致过度实现控件,因此控件包含的代码比实际需要的代码要多。 可能为 7K 的控件最终可能为 25K,这是 Internet 等领域的一大性能问题。 这也导致人们认为,由于实现所有接口会很复杂,人们只能使用一个工具(如 CDK)来实现控件,这样的影响是此类控件需要像 OC30.DLL 这样的大型 DLL,从而增加工作集。 如果不需要所有接口,则这将让许多开发人员可编写非常小的轻型控件,并可直接使用 OLE 或其他工具编写控件,从而最大程度地减少每个控件的开销。

这就是为什么本附录将控件识别为任何具有 CLSID 和 IUnknown 接口的对象。 即使只有 IUnknown,具有编程环境的容器也应该能够至少提供功能 #3 以及 ) 注册表项,它将获得 #1 和 #2。 如果对象为某个事件集提供 IConnectionPointContainer(和常规 IProvideClassInfo),它将获得 #5;如果它支持属性和方法的 IDispatch,则会获得 #6,以及改善容器中的代码集成。

简言之,对象应该能够仅实现 IDispatch,以及一个通过 IConnectionPointContainer 公开的事件集,即可获得上述所有可视功能。

考虑到这一点,下表描述了容器在可能没有任何接口的情况下可能执行的操作。 请注意,仅列出容器可通过 QueryInterface 直接获得的接口。 其他接口(如 IOleInPlaceActiveObject)是可通过其他方式获得。

接口 接口缺失的含义
IViewObject2
控件没有将自行绘制的可视对象,因此无法提供明确的范围。 在运行时,当此接口缺失时,容器不会尝试绘制任何内容。 在设计时,容器必须至少为此类控件绘制某种内含名称的默认矩形,以便可视编程环境中的用户可以选择该对象,并检查其存在的属性、方法和事件。 处理 IViewObject2 缺失对于良好的可视编程支持至关重要。
IOleObject
此控件不需要任何站点,也不参与任何嵌入式对象布局协商。 应使用容器提供的默认值填充容器可能希望此接口提供的任何信息(如控件范围)。
IOleInPlaceObject
此控件不会就地处于活动状态(如标签),因此从未尝试以这种方式激活。 其唯一的激活可能是其属性页。
IOleControl
控件没有助记符,也没有使用环境属性,并且不关心容器是否会忽略事件。 如果此接口缺失,容器只是不调用其方法。
IDataObject
此控件不提供任何属性集和任何可以缓存的视觉呈现,因此容器会在此接口缺失的情况下选择缓存一些默认展示(特别是支持 CF_METAFILEPICT),并禁用任何属性集相关功能。
IDispatch
此控件没有自定义属性或方法。 在这种情况下,容器不需要尝试显示任何控件属性,并且应禁止容器不识别为属于其自己的扩展控件的任何自定义方法调用(可能支持方法和属性)。 由于扩展控件通常将某些 IDispatch 调用委托给控件,因此扩展控件不应期望该控件具有 IDispatch
IConnectionPointContainer
此控件没有事件,因此容器无需考虑处理任何事件。
IProvideClassInfo
IProvideClassInfo2
此控件没有类型信息或事件,或者容器需要通过控件的注册表项进入控件的类型信息。 此接口的存在是一种优化。
ISpecifyPropertyPages
此控件没有属性页,因此,如果容器有任何 UI 将调用属性页,则容器应禁用该 UI。
IPerPropertyBrowsing
此控件本身没有显示名称,没有预先确定的字符串和值,也没有属性到页面映射。 此接口几乎始终用于生成容器用户界面,因此在此接口缺失的情况下会禁用此类 UI 元素。
IPersist*
此控件没有持久状态可言,因此容器无需担心保存任何特定于控件的数据。 当然,容器将以自己的表单或文档保存有关控件的自有信息,但控件本身对该信息没有任何贡献。
IOleCache
IOleCache2
对象不支持缓存。 容器仍可以使用 CreateDataCache 创建数据缓存本身来支持缓存。

容器