扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:Tony Schreiner,John Sudds 来源:微软 2007年10月16日
关键字:
通过按 F5 再次启动 Internet Explorer。文档加载后,BHO 将显示其消息。
继续浏览以观察消息框出现的时间及频率。请注意,不仅在加载页面时会显示 BHO 警告,在通过单击“上一步”按钮重新加载该页面时也会显示 BHO 警告;但在单击“刷新”按钮时不会显示该警告。在 Internet Explorer 7 中,对于每个新的选项卡都会显示该消息框。
该事件在页面被下载和解析后激发,但是在 window.onload 事件触发之前激发。在有多个框架的情况下,该事件将激发多次,结束时后面跟随的是顶层框架。在随后的代码中,通过将事件的 pDisp 参数所传递的对象与在 SetSite 中进行缓存处理的顶层浏览器进行比较来检测出这一系列事件的最后事件。
操作 DOM
以下 JavaScript 代码演示了 DOM 的基本操作。它通过将图像的样式对象的 display 属性设置为“none”在网页上隐藏图像。
以下是引用片段: function RemoveImages(doc) { var images = doc.images; if (images != null) { for (var i = 0; i < images.length; i++) { var img = images.item(i); img.style.display = "none"; } } } |
在最后这部分中,我们将说明如何以 C++ 实现这个基本逻辑。
HelloWorldBHO.h
首先打开 HelloWorldBHO.h 并将 mshtml.h 包含在内。该头文件定义了使用 DOM 时所需的接口。
#include
接下来,定义专用成员方法以包含上述 JavaScript 的 C++ 实现。
private:
void RemoveImages(IHTMLDocument2 *pDocument);
HelloWorldBHO.cpp
现在,OnDocumentComplete 事件处理程序要完成两个新任务。首先,它将缓存处理后的 WebBrowser 指针与激发事件的对象进行比较;如果两者相等,则该事件用于顶层窗口,并且文档也完全加载。其次,它检索一个指向 document 对象的指针并将其传递给 RemoveImages。
以下是引用片段: void STDMETHODCALLTYPE CHelloWorldBHO::OnDocumentComplete(IDispatch *pDisp, VARIANT *pvarURL) { HRESULT hr = S_OK; // 查询 IWebBrowser2 接口。 CComQIPtr spTempWebBrowser = pDisp; // 此事件是否与顶级浏览器相关联? if (spTempWebBrowser && m_spWebBrowser && m_spWebBrowser.IsEqualObject(spTempWebBrowser)) { // 从浏览器中获取当前文档对象…… CComPtr spDispDoc; hr = m_spWebBrowser->get_Document(&spDispDoc); if (SUCCEEDED(hr)) { // ……并查询 HTML 文档。 CComQIPtr spHTMLDoc = spDispDoc; if (spHTMLDoc != NULL) { // 最后,删除这些图像。 RemoveImages(spHTMLDoc); } } } } |
pDisp 中的 IDispatch 指针包含了已在其中加载文档的窗口或框架的 IWebBrowser2 接口。我们将该值存储在 CComQIPtr 类变量中,该变量将自动执行一个 QueryInterface。接下来,为确定该页面是否已完全加载,我们将该接口指针与顶层浏览器在 SetSite 中进行缓存处理的接口指针进行比较。本测试的结果是,我们仅从顶层浏览器框架的文档中删除了图像;未加载到顶层框架中的文档没有通过本测试。(有关详细信息,请参阅如何确定页面何时在 WebBrowser 控件中完成加载和如何获取 HTML 框架的 WebBrowser 对象模型。)
检索 HTML document 对象需要两个步骤。即使浏览器已经承载了另一种类型的文档对象(例如 Microsoft Word 文档),get_Document 也要为活动文档检索一个指针,因此,必须查询该活动文档是否有 IHTMLDocument2 接口,以确定它是否确实是 HTML 页面。通过 IHTMLDocument2 接口可以访问 DHTML DOM 的内容。
确认某 HTML 文档已加载后,将该值传递给 RemoveImages。请注意,该参数作为指针(而不是作为 CComPtr)传递给 IHTMLDocument2。
以下是引用片段: void CHelloWorldBHO::RemoveImages(IHTMLDocument2* pDocument) { CComPtr spImages; // 从 DOM 中获取图像集。 HRESULT hr = pDocument->get_images(&spImages); if (hr == S_OK && spImages != NULL) { // 获取集合中的图像数。 long cImages = 0; hr = spImages->get_length(&cImages); if (hr == S_OK && cImages > 0) { for (int i = 0; i < cImages; i++) { CComVariant svarItemIndex(i); CComVariant svarEmpty; CComPtr spdispImage; // 按索引从集合中获取图像。 hr = spImages->item(svarItemIndex, svarEmpty, &spdispImage); if (hr == S_OK && spdispImage != NULL) { // 首先,查询通用 HTML 元素接口…… CComQIPtr spElement = spdispImage; if (spElement) { // ……然后请求样式接口。 CComPtr spStyle; hr = spElement->get_style(&spStyle); // 设置 display="none" 以隐藏图像。 if (hr == S_OK && spStyle != NULL) { static const CComBSTR sbstrNone(L"none"); spStyle->put_display(sbstrNone); } } } } } } } |
使用 C++ 与 DOM 交互要比使用 JavaScript 更繁琐,但代码流在本质上相同。
上述代码将循环访问图像集合中的每个项。在脚本中,很明显就可以看出是按序数还是按名称访问集合元素;但在 C++ 中,则必须通过传递一个空变量来手动区分这些参数。我们要再次依靠 ATL 帮助程序类(这次是 CComVariant)来将我们必须编写的代码量最小化。
最后的注意事项
为便于编写脚本,DOM 中的所有对象都使用 IDispatch 来提供从多个接口派生的属性和方法。但在 C++ 中,则必须要显式查询支持要使用的属性或方法的接口。例如,图像对象同时支持 IHTMLElement 接口和 IHTMLImgElement 接口。因此,要检索图像的 style 对象,首先必须查询 IHTMLElement 接口,该接口可提供 get_style 方法。
另请注意,COM 规则不能保证发生故障时指针的有效性;因此在每次 COM 调用后都需要检查 HRESULT。此外,对于许多 DOM 方法来说,返回 NULL 值并不是错误;因此需要对返回值和指针值都进行仔细检查。为使该检查更安全,应始终预先将指针初始化为 NULL。采用防御性的详细容错编码样式将有助于防止以后发生无法预测的程序错误。
总结
虽然有各种类型的 BHO 用于多种用途,但所有 BHO 都有一个共同特点:与浏览器连接。由于 BHO 可以与 Internet Explorer 紧密集成,因此受到需要扩展浏览器功能的大量开发人员的重视。本文说明了如何创建一个简单 BHO 以用于在加载文档中修改 IMG 元素的样式属性。我们鼓励您根据自己需要将本文中的入门级示例继续延伸。可通过访问以下链接进一步探究这些可能性。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者