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