IObjectWithSite 接口 从这个高层来看待浏览器助手对象,一个概念就清晰的显现出来了:BHO 是一个动态连接库 (DLL),它能附着在 Internet Explorer 的一个新实例上,在某些情况下,也能附着在 Windows Explorer 的实例上。这样的模块能通过容器的现场与浏览器建立联系。
通常,现场是指处于容器和每个被包含的对象之间的中介对象。容器通过它管理包含的对象,并随后使对象的内置功能可用。这种容器和对象之间的基于现场的关系涉及到在容器一端实现像 IOleClientSite 这样的接口,以及在对象一端实现像 IOleObject 这样的接口。通过调用 IOleObject 上的方法,容器使对象知道它的宿主环境。
当容器是 Internet Explorer (或支持 Web 的 Windows Explorer),从性能的角度考虑,需用将这种通讯模式降低到必要的程度。现在对象需要实现更简单更小的叫作 IObjectWithSite 的接口。它只需提供两个方法。
表 3. IObjectWithSite 接口定义
对 BHO 的唯一严格要求就是实现这个接口。注意你要避免从前面所说的函数中返回 E_NOTIMPL。你要么不去实现这个接口,要么正确地编写它的方法。
编写浏览器助手对象 浏览器助手对象是进程内的 COM 服务程序,那么还有什么比 Active Template Library (ATL)更适合用来编写它呢?选择 ATL 的另一个原因是它已经默认提供了一个很好的 IObjectWithSite 接口。还有,在 ATL COM 向导内置支持的预定义对象类型中,有一个 Internet Explorer 对象,正好是 BHO 的对象类型。实际上,ATL Internet Explorer 对象是一个简单的对象。就是说,一个 COM 服务程序,支持 IUnknown 和自我注册加上 IObjectWithSite。如果你在 ATL 项目中添加一个这样的对象,并引用 CViewSource 类,你可以从向导中得到以下代码:
class ATL_NO_VTABLE CViewSource :
public CComObjectRootEx
,
public CComCoClass,
public IObjectWithSiteImpl,
public IDispatchImpl
&LIBID_HTMLEDITLib>
像你看到的那样,向导已经使这个类继承 IObjectWithSiteImpl,它是提供 IObjectWithSite 的基本实现的一个 ATL 模板类。(参见 Microsoft Visual Studio 98 中 ATL\INCLUDE 目录下的 atlcom.h。) 通常不需要重载 GetSite() 成员函数。相反,GetSite() 的已有代码常常(即使并不总是)需要按用户要求来重写。实际上,ATL 只是简单地将 IUnknown 指针保存到一个叫 m_spUnkSite 的成员变量里。
在文章的其余部分我们将讨论一个相当复杂的 BHO 例子。这个对象只附加到 Internet Explorer 上,并显示一个带有所查看的页面源代码的文本框。当你改变页面时,此代码窗口自动更新,并且,在 Internet Explorer 显示的不是一个 HTML 页面时变成灰色。你对 HTML 代码的任何修改会立即在浏览器中反映出来。动态 HTML (DHTML) 使这种魔术成为可能。这样的代码窗口可以隐藏,并在以后通过热键召回。可见时,它同 Internet Explorer 分享整个桌面工作区,并能像图 3 所示的那样适当地改变尺寸。
图 3. 工作中的浏览器助手对象。它附着在 Internet Explorer 上并显示所查看页面的源代码。它同时允许你修改代码 (但不能保存)。
这个例子的关键点是访问 Internet Explorer 的浏览机制,而它不过是 WebBrowser 控件的一个实例。这个例子可以分为以下五个主要步骤:
1、检测谁加载了对象,是 Internet Explorer 还是 Windows Explorer;
2、获得处理 WebBrowser 对象的 IWebBrowser2 接口;
3、捕获 WebBrowser 的特定事件;
4、访问正在查看的文档,确定它是 HTML 文档;
5、管理显示 HTML 源代码的对话框窗口。
第一步在 DllMain() 编码时完成。然而,我们是在 SetSite() 中获得指向 WebBrowser 对象的指针。下面我们来看一看这些步骤的详细内容。