[Ray Linn]用Visual Studio 2008开发IE BHO (浏览器帮助对象)之一
生活随笔
收集整理的這篇文章主要介紹了
[Ray Linn]用Visual Studio 2008开发IE BHO (浏览器帮助对象)之一
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
這篇文章是應(yīng)同學(xué)們的要求寫(xiě)的,以前都是用VC++ 6.0+Platform SDK完成的. 遷移到 VS2008之后,原來(lái)Visual Studio 6.0里的BHO向?qū)Р粡?fù)存在,因此特此不厭其煩,詳細(xì)說(shuō)明,本文也適用于VS2005.
首先談BHO的開(kāi)發(fā)工具,我偏向使用VC++(unmanaged C++) 作為開(kāi)發(fā)工具,因?yàn)镴ava JVM或.Net CLR的虛擬機(jī)是個(gè)很笨重的東西,也是內(nèi)存殺手, 并不具備寫(xiě)plugin的快捷輕巧的特點(diǎn).個(gè)人并不喜歡將其作為Plug-in的開(kāi)發(fā)平臺(tái),不過(guò)我會(huì)有另文說(shuō)明用C#開(kāi)發(fā)BHO的全過(guò)程, 作為那些偏重開(kāi)發(fā)效率的同學(xué)的參考.
其次是類庫(kù)的選擇,我傾向利用“活動(dòng)模板庫(kù)”(ATL) 來(lái)開(kāi)發(fā)使用 C++ 的 BHO。之所以使用 ATL,是因?yàn)樗奖愕貙?shí)現(xiàn)了我們可以按需進(jìn)行擴(kuò)展的基本樣板。盡管使用“Microsoft 基礎(chǔ)類”(MFC) 或 Win32 API 和 COM)也可以創(chuàng)建BHO,但 ATL 是為我們提供了自動(dòng)處理許多細(xì)節(jié)的輕型庫(kù),包括建立含有 BHO 類標(biāo)識(shí)符 (CLSID) 的注冊(cè)表。
ATL 的另一個(gè)優(yōu)勢(shì)在于它的 COM智能感知指針類(例如,CComPtr 和 CComBSTR),這些類可管理 COM 對(duì)象的生命周期。例如,CComPtr 在賦值時(shí)會(huì)調(diào)用 AddRef,而在對(duì)象被銷毀或超出范圍時(shí)會(huì)調(diào)用 Release。智能指針簡(jiǎn)化了代碼并且有助于避免內(nèi)存泄漏。當(dāng)在單個(gè)方法范圍內(nèi)使用時(shí),它們的穩(wěn)定性和可靠性尤為有用。
介紹完ATL, 我們也簡(jiǎn)單介紹一下BHO. BHO是將自定義功能添加到 Internet Explorer 的輕型 DLL 擴(kuò)展,除了IE, BHO 還可以將功能添加到 Windows 資源管理器外殼程序.
BHO 通常并不提供其自身的任何用戶界面 (UI)。它們而是通過(guò)在后臺(tái)響應(yīng)瀏覽器事件和用戶輸入數(shù)據(jù)來(lái)發(fā)揮作用。例如,BHO 可以攔截彈出窗口、自動(dòng)填充窗體或?yàn)槭髽?biāo)手勢(shì)添加支持。
有一種常見(jiàn)誤解認(rèn)為工具欄擴(kuò)展項(xiàng)需要 BHO.但如果將 BHO 與工具欄配合使用,則可以實(shí)現(xiàn)更豐富的用戶體驗(yàn)。(關(guān)于IE工具欄的編程,在另一篇文章中說(shuō)明).
BHO 的生命周期與它所交互的瀏覽器實(shí)例的生命周期相等。在 IE 6 和早期版本中,這意味著要為每個(gè)新的頂層窗口創(chuàng)建(和銷毀)一個(gè)新 BHO。在IE 7中則是為每個(gè)選項(xiàng)卡都創(chuàng)建和銷毀一個(gè)新 BHO。
BHO 必須實(shí)現(xiàn) IObjectWithSite 接口, 該接口提供了兩個(gè)方法GetSite和SetSite。根據(jù)MSDN的說(shuō)明:
GetSite: Gets the last site set with IObjectWithSite::SetSite. If there is no known site, the object returns a failure code. SetSite: Provides the site's IUnknown pointer to the object.
我們主要是對(duì)后者進(jìn)行調(diào)用,此方法方便了與 Internet Explorer 的初始通信,并會(huì)在其將要釋放時(shí)通知 BHO。我們實(shí)現(xiàn)此接口,然后將 BHO 的 CLSID 添加到注冊(cè)表中,就可以創(chuàng)建一個(gè)簡(jiǎn)單的瀏覽器擴(kuò)展。過(guò)程如下:
1. 在Visual Studio中,選擇VC++中的ATL項(xiàng)目, 創(chuàng)建一個(gè)新的項(xiàng)目MySolutionPlugin, 在隨后的向?qū)е?確認(rèn)Server Type是Dll, Visual Studio會(huì)為我們創(chuàng)建程序的模板.
2. 為該項(xiàng)目添加我們的程序主體, (不熟悉visual studio的同學(xué)在資源瀏覽器里的右鍵菜單里選 add-->class, 可別選到New Item), 類型選ATL Simple Object ,? short name命名為RayBHO,各項(xiàng)屬性如下:?
a) “線程模型” ---“Apartment”
b) “聚合”---“否”
c) “接口”---“雙重”
d) “支持”---勾上“IobjectWithSite”。
具體的含義請(qǐng)參考MSDN.
一般來(lái)說(shuō),Internet Explorer 至少調(diào)用SetSite方法兩次: 一次用于建立連接,另一次則是在瀏覽器退出時(shí)。我們 BHO 中的 SetSite 實(shí)現(xiàn)將執(zhí)行以下操作:
?存儲(chǔ)對(duì)站點(diǎn)的引用。在初始化期間,瀏覽器將 IUnknown 指針傳遞給頂層 WebBrowser 控件,然后 BHO 將對(duì)它的引用存儲(chǔ)在一個(gè)專用成員變量中。
?釋放目前被占用的站點(diǎn)指針。Internet Explorer 傳遞 NULL 時(shí),BHO 必須釋放所有接口引用并且斷開(kāi)與瀏覽器的連接。
要實(shí)現(xiàn)SetSite,我們需手工在添加一個(gè)public的方法:
STDMETHOD(SetSite)(IUnknown * pUnkSite);
STDMETHOD 宏是將方法標(biāo)記為虛方法并且確保其具有適用于公共 COM 接口的調(diào)用約定的一個(gè)ATL 約定, 它有助于區(qū)分 COM 接口和該類中可能存在的其他公共方法。其實(shí)現(xiàn)成員方法時(shí)應(yīng)相應(yīng)使用 STDMETHODIMP 宏。同時(shí)我們需要聲明一個(gè)私有變量來(lái)保存Browser的指針"
CComPtr<IWebBrowser2> m_spWebBrowser;//保存Browser指針的私有變量
然后是SetSite的實(shí)現(xiàn)
STDMETHODIMP CRayBHO::SetSite(IUnknown*pUnkSite) { if(pUnkSite!=NULL) { //緩存指向IWebBrowser2的指針。 pUnkSite->QueryInterface(IID_IWebBrowser2,(void**)&m_spWebBrowser); } else { //在此釋放緩存的指針和其他資源。 m_spWebBrowser.Release(); } //返回基類實(shí)現(xiàn) return IObjectWithSiteImpl::SetSite(pUnkSite); }
從上面的介紹我們知道, 初始化期間,瀏覽器將傳遞一個(gè)對(duì)其頂層 IWebBrowser2 接口(我們對(duì)其進(jìn)行緩存處理)的引用。瀏覽器關(guān)閉時(shí)將傳遞 NULL,為避免內(nèi)存泄漏和循環(huán)引用計(jì)數(shù),此時(shí)釋放所有指針和資源非常重要。最后,我們調(diào)用基類實(shí)現(xiàn)以便繼續(xù)執(zhí)行接口合約的其余部分。
加載DLL 后,系統(tǒng)將通過(guò) DLL_PROCESS_ATTACH 通知調(diào)用 DllMain 函數(shù)。由于 Internet Explorer 大量使用多線程,因此,對(duì) DllMain 的頻繁的 DLL_THREAD_ATTACH 和 DLL_THREAD_DETACH 通知會(huì)降低擴(kuò)展和瀏覽器進(jìn)程的整體性能。
如果BHO 不需要線程級(jí)的跟蹤,我們可以在 DLL_PROCESS_ATTACH 通知期間調(diào)用 DisableThreadLibraryCalls 以避免新線程通知的額外開(kāi)銷。修改DllMain.cpp 中的DllMain函數(shù):
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { if(dwReason==DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hInstance); } return _AtlModule.DllMain(dwReason,lpReserved); }
要使BHO工作,我們還需要把BHO 的 CLSID 添加到注冊(cè)表中。此條目會(huì)將 此DLL 標(biāo)記為瀏覽器幫助程序?qū)ο?#xff0c;并使 Internet Explorer 在啟動(dòng)時(shí)加載 BHO。我們可以在MySolutionPlugin.idl中找到該BHO的CLSID.幸運(yùn)的,Visual Studio會(huì)幫助我們實(shí)現(xiàn)這些, 你看到:
importlib("stdole2.tlb"); [ uuid(057F3E68-6C2E-40A5-A641-E8CF9D6766F3), helpstring("RayBHO Class") ]
您的機(jī)器的CLSID可能有所不同, 接著打開(kāi)RayBHO.rgs文件,添加入:
HKLM { NoRemove SOFTWARE { NoRemove Microsoft { NoRemove Windows { NoRemove CurrentVersion { NoRemove Explorer { NoRemove 'Browser Helper Objects' { ForceRemove {057F3E68-6C2E-40A5-A641-E8CF9D6766F3} = s 'RayBHO Class' { val NoExplorer = d '1' } } } } } } } }
這一段是為了在注冊(cè)表里添加一個(gè)雙字節(jié)的NoExplorer=1的鍵,不讓W(xué)indows Explorer加載該BHO,因此該BHO只能在ie中運(yùn)行.
你可以編譯這個(gè)BHO. 如果一切正常, 你可以在IE的管理加載項(xiàng)里看到這個(gè)BHO.
如果不幸報(bào)錯(cuò): 該BHO無(wú)法被注冊(cè),根據(jù)我的經(jīng)驗(yàn),原因大概有2類,可以依次檢查
1. 你是否有管理員權(quán)限以修改注冊(cè)表,如不是管理員身份,可以在菜單上右擊Microsoft Visual studio 2008,從右鍵菜單中選擇"運(yùn)行方式"...
2. 你的注冊(cè)表?xiàng)l目語(yǔ)法是否正確,或者含有非法字符.
首先談BHO的開(kāi)發(fā)工具,我偏向使用VC++(unmanaged C++) 作為開(kāi)發(fā)工具,因?yàn)镴ava JVM或.Net CLR的虛擬機(jī)是個(gè)很笨重的東西,也是內(nèi)存殺手, 并不具備寫(xiě)plugin的快捷輕巧的特點(diǎn).個(gè)人并不喜歡將其作為Plug-in的開(kāi)發(fā)平臺(tái),不過(guò)我會(huì)有另文說(shuō)明用C#開(kāi)發(fā)BHO的全過(guò)程, 作為那些偏重開(kāi)發(fā)效率的同學(xué)的參考.
其次是類庫(kù)的選擇,我傾向利用“活動(dòng)模板庫(kù)”(ATL) 來(lái)開(kāi)發(fā)使用 C++ 的 BHO。之所以使用 ATL,是因?yàn)樗奖愕貙?shí)現(xiàn)了我們可以按需進(jìn)行擴(kuò)展的基本樣板。盡管使用“Microsoft 基礎(chǔ)類”(MFC) 或 Win32 API 和 COM)也可以創(chuàng)建BHO,但 ATL 是為我們提供了自動(dòng)處理許多細(xì)節(jié)的輕型庫(kù),包括建立含有 BHO 類標(biāo)識(shí)符 (CLSID) 的注冊(cè)表。
ATL 的另一個(gè)優(yōu)勢(shì)在于它的 COM智能感知指針類(例如,CComPtr 和 CComBSTR),這些類可管理 COM 對(duì)象的生命周期。例如,CComPtr 在賦值時(shí)會(huì)調(diào)用 AddRef,而在對(duì)象被銷毀或超出范圍時(shí)會(huì)調(diào)用 Release。智能指針簡(jiǎn)化了代碼并且有助于避免內(nèi)存泄漏。當(dāng)在單個(gè)方法范圍內(nèi)使用時(shí),它們的穩(wěn)定性和可靠性尤為有用。
介紹完ATL, 我們也簡(jiǎn)單介紹一下BHO. BHO是將自定義功能添加到 Internet Explorer 的輕型 DLL 擴(kuò)展,除了IE, BHO 還可以將功能添加到 Windows 資源管理器外殼程序.
BHO 通常并不提供其自身的任何用戶界面 (UI)。它們而是通過(guò)在后臺(tái)響應(yīng)瀏覽器事件和用戶輸入數(shù)據(jù)來(lái)發(fā)揮作用。例如,BHO 可以攔截彈出窗口、自動(dòng)填充窗體或?yàn)槭髽?biāo)手勢(shì)添加支持。
有一種常見(jiàn)誤解認(rèn)為工具欄擴(kuò)展項(xiàng)需要 BHO.但如果將 BHO 與工具欄配合使用,則可以實(shí)現(xiàn)更豐富的用戶體驗(yàn)。(關(guān)于IE工具欄的編程,在另一篇文章中說(shuō)明).
BHO 的生命周期與它所交互的瀏覽器實(shí)例的生命周期相等。在 IE 6 和早期版本中,這意味著要為每個(gè)新的頂層窗口創(chuàng)建(和銷毀)一個(gè)新 BHO。在IE 7中則是為每個(gè)選項(xiàng)卡都創(chuàng)建和銷毀一個(gè)新 BHO。
BHO 必須實(shí)現(xiàn) IObjectWithSite 接口, 該接口提供了兩個(gè)方法GetSite和SetSite。根據(jù)MSDN的說(shuō)明:
GetSite: Gets the last site set with IObjectWithSite::SetSite. If there is no known site, the object returns a failure code. SetSite: Provides the site's IUnknown pointer to the object.
我們主要是對(duì)后者進(jìn)行調(diào)用,此方法方便了與 Internet Explorer 的初始通信,并會(huì)在其將要釋放時(shí)通知 BHO。我們實(shí)現(xiàn)此接口,然后將 BHO 的 CLSID 添加到注冊(cè)表中,就可以創(chuàng)建一個(gè)簡(jiǎn)單的瀏覽器擴(kuò)展。過(guò)程如下:
1. 在Visual Studio中,選擇VC++中的ATL項(xiàng)目, 創(chuàng)建一個(gè)新的項(xiàng)目MySolutionPlugin, 在隨后的向?qū)е?確認(rèn)Server Type是Dll, Visual Studio會(huì)為我們創(chuàng)建程序的模板.
2. 為該項(xiàng)目添加我們的程序主體, (不熟悉visual studio的同學(xué)在資源瀏覽器里的右鍵菜單里選 add-->class, 可別選到New Item), 類型選ATL Simple Object ,? short name命名為RayBHO,各項(xiàng)屬性如下:?
a) “線程模型” ---“Apartment”
b) “聚合”---“否”
c) “接口”---“雙重”
d) “支持”---勾上“IobjectWithSite”。
具體的含義請(qǐng)參考MSDN.
一般來(lái)說(shuō),Internet Explorer 至少調(diào)用SetSite方法兩次: 一次用于建立連接,另一次則是在瀏覽器退出時(shí)。我們 BHO 中的 SetSite 實(shí)現(xiàn)將執(zhí)行以下操作:
?存儲(chǔ)對(duì)站點(diǎn)的引用。在初始化期間,瀏覽器將 IUnknown 指針傳遞給頂層 WebBrowser 控件,然后 BHO 將對(duì)它的引用存儲(chǔ)在一個(gè)專用成員變量中。
?釋放目前被占用的站點(diǎn)指針。Internet Explorer 傳遞 NULL 時(shí),BHO 必須釋放所有接口引用并且斷開(kāi)與瀏覽器的連接。
要實(shí)現(xiàn)SetSite,我們需手工在添加一個(gè)public的方法:
STDMETHOD(SetSite)(IUnknown * pUnkSite);
STDMETHOD 宏是將方法標(biāo)記為虛方法并且確保其具有適用于公共 COM 接口的調(diào)用約定的一個(gè)ATL 約定, 它有助于區(qū)分 COM 接口和該類中可能存在的其他公共方法。其實(shí)現(xiàn)成員方法時(shí)應(yīng)相應(yīng)使用 STDMETHODIMP 宏。同時(shí)我們需要聲明一個(gè)私有變量來(lái)保存Browser的指針"
CComPtr<IWebBrowser2> m_spWebBrowser;//保存Browser指針的私有變量
然后是SetSite的實(shí)現(xiàn)
STDMETHODIMP CRayBHO::SetSite(IUnknown*pUnkSite) { if(pUnkSite!=NULL) { //緩存指向IWebBrowser2的指針。 pUnkSite->QueryInterface(IID_IWebBrowser2,(void**)&m_spWebBrowser); } else { //在此釋放緩存的指針和其他資源。 m_spWebBrowser.Release(); } //返回基類實(shí)現(xiàn) return IObjectWithSiteImpl::SetSite(pUnkSite); }
從上面的介紹我們知道, 初始化期間,瀏覽器將傳遞一個(gè)對(duì)其頂層 IWebBrowser2 接口(我們對(duì)其進(jìn)行緩存處理)的引用。瀏覽器關(guān)閉時(shí)將傳遞 NULL,為避免內(nèi)存泄漏和循環(huán)引用計(jì)數(shù),此時(shí)釋放所有指針和資源非常重要。最后,我們調(diào)用基類實(shí)現(xiàn)以便繼續(xù)執(zhí)行接口合約的其余部分。
加載DLL 后,系統(tǒng)將通過(guò) DLL_PROCESS_ATTACH 通知調(diào)用 DllMain 函數(shù)。由于 Internet Explorer 大量使用多線程,因此,對(duì) DllMain 的頻繁的 DLL_THREAD_ATTACH 和 DLL_THREAD_DETACH 通知會(huì)降低擴(kuò)展和瀏覽器進(jìn)程的整體性能。
如果BHO 不需要線程級(jí)的跟蹤,我們可以在 DLL_PROCESS_ATTACH 通知期間調(diào)用 DisableThreadLibraryCalls 以避免新線程通知的額外開(kāi)銷。修改DllMain.cpp 中的DllMain函數(shù):
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { if(dwReason==DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hInstance); } return _AtlModule.DllMain(dwReason,lpReserved); }
要使BHO工作,我們還需要把BHO 的 CLSID 添加到注冊(cè)表中。此條目會(huì)將 此DLL 標(biāo)記為瀏覽器幫助程序?qū)ο?#xff0c;并使 Internet Explorer 在啟動(dòng)時(shí)加載 BHO。我們可以在MySolutionPlugin.idl中找到該BHO的CLSID.幸運(yùn)的,Visual Studio會(huì)幫助我們實(shí)現(xiàn)這些, 你看到:
importlib("stdole2.tlb"); [ uuid(057F3E68-6C2E-40A5-A641-E8CF9D6766F3), helpstring("RayBHO Class") ]
您的機(jī)器的CLSID可能有所不同, 接著打開(kāi)RayBHO.rgs文件,添加入:
HKLM { NoRemove SOFTWARE { NoRemove Microsoft { NoRemove Windows { NoRemove CurrentVersion { NoRemove Explorer { NoRemove 'Browser Helper Objects' { ForceRemove {057F3E68-6C2E-40A5-A641-E8CF9D6766F3} = s 'RayBHO Class' { val NoExplorer = d '1' } } } } } } } }
這一段是為了在注冊(cè)表里添加一個(gè)雙字節(jié)的NoExplorer=1的鍵,不讓W(xué)indows Explorer加載該BHO,因此該BHO只能在ie中運(yùn)行.
你可以編譯這個(gè)BHO. 如果一切正常, 你可以在IE的管理加載項(xiàng)里看到這個(gè)BHO.
如果不幸報(bào)錯(cuò): 該BHO無(wú)法被注冊(cè),根據(jù)我的經(jīng)驗(yàn),原因大概有2類,可以依次檢查
1. 你是否有管理員權(quán)限以修改注冊(cè)表,如不是管理員身份,可以在菜單上右擊Microsoft Visual studio 2008,從右鍵菜單中選擇"運(yùn)行方式"...
2. 你的注冊(cè)表?xiàng)l目語(yǔ)法是否正確,或者含有非法字符.
轉(zhuǎn)載于:https://www.cnblogs.com/jcss2008/archive/2009/05/05/1449643.html
總結(jié)
以上是生活随笔為你收集整理的[Ray Linn]用Visual Studio 2008开发IE BHO (浏览器帮助对象)之一的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java创建cookie和删除cooki
- 下一篇: 伪类的顺序