现在,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)来将我们必须编写的代码量最小化。

