BHO(Browser Helper Object,瀏覽器輔助對(duì)象,簡(jiǎn)稱BHO)。BHO是微軟推出的作為瀏覽器對(duì)第三方程序員開放交互接口的業(yè)界標(biāo)準(zhǔn),通過(guò)簡(jiǎn)單的代碼就可以進(jìn)入瀏覽器領(lǐng)域的“交互接口”(INTERACTIVED Interface)。通過(guò)這個(gè)接口,程序員可以編寫代碼獲取瀏覽器的行為,比如“后退”、“前進(jìn)”、“當(dāng)前頁(yè)面”等,利用BHO的交互特性,程序員還可以用代碼控制瀏覽器行為,比如修改替換瀏覽器工具欄,添加自己的程序按鈕等。這些在系統(tǒng)看來(lái)都是沒(méi)有問(wèn)題的。BHO原來(lái)的目的是為了更好的幫助程序員打造個(gè)性化瀏覽器,以及為程序提供更簡(jiǎn)潔的交互功能,現(xiàn)在很多IE個(gè)性化工具就是利用BHO的來(lái)實(shí)現(xiàn)。
在正式利用VC++編寫代碼之前,我們需要了解一些東西,首先是IE瀏覽器加載BHO插件的原理。
1.IE的窗口打開時(shí),先尋找HKLM下的SOFTWA RE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\里的CLSID,這些CLSID,都對(duì)應(yīng)著相應(yīng)的BHO插件,然后根據(jù)這個(gè)CLSID到HKCR下的CLSIDs里找到此插件的信息,包括文件位置等。
2.IE根據(jù)找到的CLSID信息創(chuàng)建BHO對(duì)象,并且查找IObjectWithSite接口。(這個(gè)接口非常簡(jiǎn)單,只有SetSite和GetSite兩個(gè)方法)
3.IE把IWebBrowser2(瀏覽器插件)傳到BHO的SetSite方法,用戶在此方法中可掛載自己的事件處理方法。
4.窗口關(guān)閉時(shí),iE把null傳到BHO的SetSite方法,此方法用來(lái)去掉掛載的事件處理方法。
了解了IE瀏覽器加載BHO的方法之后,我們就來(lái)看一下BHO插件的編寫流程。
1、創(chuàng)建IObj ectWithSite顯式接口,創(chuàng)建COM類型,實(shí)現(xiàn)繼承IObjectWithSite接口
2、實(shí)現(xiàn)此接口并在SetSite方法里加上所要掛載的事件
3、處理事件
4、注冊(cè)此BHO到注冊(cè)表中HKLM下的Softw are\\Microsoft\\Windows\\CurrentVersion\\ExplorerBrowser Helper Objects;(HKCR下的CLSIDs是根據(jù)上面的路徑自動(dòng)注冊(cè)的)
5、.net下須設(shè)置此BHO項(xiàng)目的”配置屬性一>生成”中為Interop注冊(cè)為True,這樣才能將.net類庫(kù)文件注冊(cè)到COM。
我們采用VC6.O創(chuàng)建好環(huán)境之后,就要進(jìn)行代碼的編寫了。由于我們?cè)贗E瀏覽器中加載插件,所以其他的瀏覽器我們就不允許dll加載了。這樣我們就需要在dllMain函數(shù)中對(duì)加載該dll的程序進(jìn)行判斷,代碼如下:
STDMETHODIMP CIEH1probj::lnvoke(DISPID dispidMemberm,REFIID riid,LCID lcid,WORD wFlags,
DISPPARAMS*pDispParams,VARIANT*pvarResult,
EXCEPINFO* pExceplnfo,UINT*puArgerr)
{
USES_CONVERSION;
if (!pDispParams)
return E_INVALIDARG;
LPOLESTR lpURL=NULL;
m_spwebBrowser2->get_LocationURL(&lpURL);
char sitelist{100}{1024}; //所有站點(diǎn)名稱(1024)
char site{1024}; //站點(diǎn)名稱
ifstream in("C:\\SiteList dat",ios::in);//從我們的文件中讀取要檢測(cè)的網(wǎng)址
if(in.fail())
{
return E_INVALIDARG;
}
這段代碼我們添加在dllMain函數(shù)的開始位置,這樣就能確保只有iexplore.exe才能加載我們的插件。BHO對(duì)象與瀏覽器進(jìn)行交互是通過(guò)事件實(shí)現(xiàn)的,那么他們之間的交互有幾種事件呢?下面就列舉一下常用到的事件:
DISPID__ BEFORENAVIGATE2: 該事件在瀏覽器準(zhǔn)備下載網(wǎng)頁(yè)之前觸發(fā),這樣這個(gè)事件就可以用在我們檢測(cè)用戶輸入的網(wǎng)頁(yè)地址上。
DISPID_DOWNLOADBEGrN:該事件在瀏覽器開始下載網(wǎng)頁(yè)時(shí)觸發(fā)。
DISPID_NAVIGATECOMPLETE:該事件是在一個(gè)鏈接(網(wǎng)頁(yè)窗口或者網(wǎng)頁(yè)Frame框架窗口)被完全打開后,IE瀏覽器或WebBrowser控件觸發(fā)的事件。這個(gè)事件已經(jīng)廢棄。應(yīng)該使用DISPIDNAVIGATECOMPLETE2。
DISPID_DOVVrNLOADCOMPLETE:該事件在網(wǎng)頁(yè)完全被瀏覽器下載完畢之后觸發(fā)。
DISPID_DOCUMENTCOMPLETE:頁(yè)面加載完畢時(shí)觸發(fā)該事件,當(dāng)每個(gè)FRAME加載完畢后都會(huì)觸發(fā)DISPID_DOCUMENTCOMPLETE。可以在DISPID_DOCUMENTCOMPLETE中,判斷是否是整個(gè)頁(yè)面加載完畢。
DISPID_NEWWINDOW2:該事件在有新的窗口打開時(shí)觸發(fā),例如彈窗網(wǎng)頁(yè)。
了解了上述的事件機(jī)制之后,我們就明確怎么編寫我們的代碼了,既然是監(jiān)控網(wǎng)址的,那么我們可以在DISPID_BEFORENAVIGATE2事件中對(duì)網(wǎng)址進(jìn)行監(jiān)控。事件的方法我們需要在Invoke函數(shù)中編寫。這里我們用switch語(yǔ)句對(duì)事件進(jìn)行判斷,要處理的網(wǎng)址我們放到文件里,讓程序從文件里讀取網(wǎng)址,然后與用戶輸入的網(wǎng)址進(jìn)行對(duì)比。
編譯之后生成了一個(gè)dll文件,這就是我們需要的文件。我們?cè)诰W(wǎng)址文件中寫入我們需要攔截的網(wǎng)址,這里用百度做測(cè)試。下一步就是注冊(cè)我們的dll文件,用cmd命令“regsvr32 c://IEHelper.dll”來(lái)注冊(cè),注冊(cè)結(jié)果,注冊(cè)成功。我們打開一個(gè)新的網(wǎng)頁(yè),輸入網(wǎng)址百度回車,彈出了我們的顯示信息,ok,到這里我們的目的就完成了,當(dāng)然讀者可以通過(guò)修改上述代碼進(jìn)行二次開發(fā),可以實(shí)現(xiàn)網(wǎng)址替換、網(wǎng)頁(yè)內(nèi)容替換等等功能,這些有待讀者自己挖掘。