扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者: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 元素的样式属性。我们鼓励您根据自己需要将本文中的入门级示例继续延伸。可通过访问以下链接进一步探究这些可能性。
濠电姷鏁告慨鐑姐€傛禒瀣劦妞ゆ巻鍋撻柛鐔锋健閸┾偓妞ゆ巻鍋撶紓宥咃躬楠炲啫螣鐠囪尙绐為梺褰掑亰閸撴盯鎮惧ú顏呪拺闂傚牊鍗曢崼銉ョ柧婵犲﹤瀚崣蹇旂節婵犲倻澧涢柛瀣ㄥ妽閵囧嫰寮介妸褋鈧帡鏌熼挊澶婃殻闁哄瞼鍠栭幃婊堝煛閸屾稓褰嬮柣搴ゎ潐濞叉ê鐣濈粙璺ㄦ殾闁割偅娲栭悡娑㈡煕鐏炲墽鐭嬫繛鍫熸倐濮婄粯鎷呯粵瀣異闂佹悶鍔嬮崡鍐茬暦閵忋倕鍐€妞ゆ劑鍎卞皬闂備焦瀵х粙鎴犫偓姘煎弮瀹曚即宕卞Ο闀愮盎闂侀潧鐗嗛幊搴㈡叏椤掆偓閳规垿鍩ラ崱妞剧凹濠电姰鍨洪敋閾荤偞淇婇妶鍛櫤闁稿鍊圭换娑㈠幢濡纰嶉柣搴㈣壘椤︾敻寮诲鍫闂佸憡鎸鹃崰搴敋閿濆鏁嗗〒姘功閻绻涢幘鏉戠劰闁稿鎹囬弻锝呪槈濞嗘劕纾抽梺鍝勬湰缁嬫垿鍩為幋锕€宸濇い鏇炴噺閳诲﹦绱撻崒娆戝妽妞ゃ劌鎳橀幆宀勫磼閻愰潧绁﹂柟鍏肩暘閸斿矂鎮為崹顐犱簻闁圭儤鍨甸鈺呮倵濮橆剦妲归柕鍥у瀵粙濡歌閸c儳绱撴担绛嬪殭婵☆偅绻堝濠氭偄绾拌鲸鏅i悷婊冪Ч閹﹢鎳犻鍌滐紲闁哄鐗勯崝搴g不閻愮儤鐓涢悘鐐跺Г閸犳﹢鏌℃担鐟板鐎规洜鍠栭、姗€鎮╅搹顐ら拻闂傚倷娴囧畷鍨叏閹惰姤鈷旂€广儱顦崹鍌炴煢濡尨绱氶柨婵嗩槸缁€瀣亜閺嶃劎鈽夋繛鍫熺矒濮婅櫣娑甸崨顔俱€愬銈庡亝濞茬喖宕洪埀顒併亜閹哄棗浜鹃梺鎸庢穿婵″洤危閹版澘绫嶉柛顐g箘椤撴椽姊虹紒妯哄鐎殿噮鍓欒灃闁告侗鍠氶崢鎼佹⒑閸撴彃浜介柛瀣閹﹢鏁冮崒娑氬幈闁诲函缍嗛崑鍡樻櫠椤掑倻纾奸柛灞剧☉缁椦囨煙閻熸澘顏柟鐓庢贡閹叉挳宕熼棃娑欐珡闂傚倸鍊风粈渚€骞栭銈傚亾濮樺崬鍘寸€规洖缍婇弻鍡楊吋閸涱垽绱遍柣搴$畭閸庨亶藝娴兼潙纾跨€广儱顦伴悡鏇㈡煛閸ャ儱濡煎褜鍨伴湁闁绘ǹ绉鍫熺畳闂備焦瀵х换鍌毼涘Δ鍛厺闁哄洢鍨洪悡鍐喐濠婂牆绀堟慨妯挎硾閽冪喖鏌曟繛褍瀚烽崑銊╂⒑缂佹ê濮囨い鏇ㄥ弮閸┿垽寮撮姀鈥斥偓鐢告煥濠靛棗鈧懓鈻嶉崶銊d簻闊洦绋愰幉楣冩煛鐏炵偓绀嬬€规洟浜堕、姗€鎮㈡總澶夌处
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者