Delphi中BHO編程

轉自網易博客原文章地址:

http://blog.163.com/qq3076169@126/blog/static/1717240672011310739404/

瀏覽器輔助對象BHO(Browser Helper Object)是一種ATL COM對象,由IE在啓動時自動加載。BHO運行在IE的地址空間內,能對IE中可訪問對象的各類事件消息進行監聽並作出相應處理。因此,當IE已成爲進 入網絡世界的主要大門時,BHO自然變得炙手可熱,不管是擴展IE功能的輔助軟件還是令人深惡痛絕的流氓軟件,都對BHO青睞有加。那麼,用於擴展IE功 能的BHO插件到底如何開發呢?下面以開發一個過濾特定網址的BHO插件爲例進行說明。
監聽瀏覽器事件
在Delphi 7中,新建ActiveX Library項目MyBHO。再在項目中新建COM Object,命名爲MyIEBHO。作爲特殊的COM對象,BHO必須實現同瀏覽器通訊的兩個接口IObjectWithSite和 Idispatch,其中IobjectWithSite接口用來掛鉤和監控瀏覽器事件。
IE在加載BHO時,會將自己的IUnknown接口用pUnkSite參數傳給BHO。通過對pUnkSite的解析即可獲得瀏覽器接口 IWebBrowser2。而獲得IWebBrowser2後,又可得到瀏覽器事件連接點接口。再使用該接口的Advise方法,便可實現對瀏覽器事件的 監聽。IobjectWithSite接口包含GetSite和SetSite方法,其中由SetSite實現IobjectWithSite接口的主要 功能。
function TMyIEBHO.SetSite(const pUnkSite:IUnknown):HResult;
var
   cmdTarget:IOleCommandTarget;
   Sp:IServiceProvider;
begin
   if(Assigned(pUnkSite))then
   begin
     cmdTarget:=(pUnkSite as IOleCommandTarget);
     Sp:=(CmdTarget as IServiceProvider);
     if(Assigned(Sp))then     //獲得IE的WebBrowser接口,
       Sp.QueryService(IWebBrowserApp,IWebBrowser2,IEThis);
     if(Assigned(IEThis))then
     begin
       IEThis.QueryInterface(IConnectionPointContainer,CPC);   //查找連接點
       CPC.FindConnectionPoint(DWEBBrowserEvents2,CP);
       CP.Advise(Self,Cookie);    //用Advise方法實現監聽
     end;
   end;
   Result:=S_OK;
end;
function TMyIEBHO.GetSite(const riid:TIID;out site:IUnknown):HResult;
begin
   if(Assigned(IEThis))then
     Result:=IEThis.QueryInterface(riid,site)
   else Result:=E_FAIL;
end;
對瀏覽器事件進行處理
BHO的另一接口Idispatch主要用來對瀏覽器事件進行處理。每當瀏覽器有事件發生時,IE就會調用IDispatch接口的Invoke方法通知 事件類型及參數,並請求BHO對事件進行處理。因此在Idispatch接口中最重要的是Invoke方法,BHO的功能基本上都在Invoke方法中實 現。至於Idispatch的其它方法GetTypeInfoCount、GetTypeInfo和GetIDsOfNames,則只需返回 E_NOTIMPL即可。
瀏覽器事件在DWebEvents2接口中定義,每種事件類型都用特定的dspid數字符號來標識,如      DownloadComplete事件爲dispid 104,BeforeNavigate2爲dispid 250,OnQuit爲dispid 253。對過濾特定網址任務而言,只需要截獲BeforeNavigate2事件並對其作相應處理就可以了。另外,在關閉瀏覽器時還要對OnQuit事件 作處理,以斷開對瀏覽器事件的監聽。
function TMyIEBHO.Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
type
   POleVariant=^OleVariant;
var
   dps:TDispParams absolute Params;
   bHasParams:Boolean;
   pDispIDs:PDispIDList;
   iDispIDsSize:Integer;
begin
   Result:=DISP_E_MEMBERNOTFOUND;
   pDispIDs:=nil;
   iDispIDsSize:=0;
   bHasParams:=(dps.cArgs>0);
   if(bHasParams)then
   begin
     iDispIDsSize:=dps.cArgs*SizeOf(TDispID);
     GetMem(pDispIDs,iDispIDsSize);
   end;
   try
     if(bHasParams)then BuildPositionalDispIDs(pDispIDs,dps);
     case DispID of
       104:begin
           Result:=S_OK;
         end;
       250:begin
           DoBeforeNavigate2(IDispatch(dps.rgvarg^[pDispIDs^[0]].dispVal),
             POleVariant(dps.rgvarg^[pDispIDs^[1]].pvarVal)^,
             POleVariant(dps.rgvarg^[pDispIDs^[2]].pvarVal)^,
             POleVariant(dps.rgvarg^[pDispIDs^[3]].pvarVal)^,
             POleVariant(dps.rgvarg^[pDispIDs^[4]].pvarVal)^,
             POleVariant(dps.rgvarg^[pDispIDs^[5]].pvarVal)^,
             dps.rgvarg^[pDispIDs^[6]].pbool^);
           Result:=S_OK;
         end;
       253:begin
           CP.Unadvise(Cookie);
           Result:=S_OK;
         end;
     end;
   finally
     if(bHasParams)then
       FreeMem(pDispIDs,iDispIDsSize);
   end;
end;
BeforeNavigate2過程中包含了過濾特定網址的處理邏輯。它從MyIEBHO.txt文件中讀取需要過濾的網址,然後同瀏覽器當前地址作比對,如果是,則直接轉向網易網站。
procedure DoBeforeNavigate2(const pDisp:IDispatch;var URL:OleVariant;var Flags:OleVariant;var TargetFrameName:OleVariant;var PostData:OleVariant; var Headers:OleVariant;var Cancel:WordBool);
var
   s:String;
   URLFile:TextFile;
begin
     Assign(URLFile, ’c:\MyIEBHO.txt’);
     Reset(URLFile);
     Try
       while not Eof(URLFile) do
         begin
           ReadLn(URLFile, s);
           if (Trim(URL)=Trim(s)) then
             begin
               Cancel:=True;
               URL:=’http://www.163.com’;
               (pDisp as   IWebbrowser2).Navigate2(URL,Flags,TargetFrameName,PostData,Headers);
             end;
         end;
     Finally
       Close(URLFile);
     end;          
end;
註冊BHO插件
同所有COM對象一樣,BHO也需要使用regsvr32進行註冊或卸載。此外,BHO還必須將自己的Guid字符串關鍵字添加到註冊表 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\下,這樣瀏覽器才能正確加載與之對應的BHO插件。該鍵值既可手工創建,也可以在BHO中用註冊表對象直接創建。
procedure TIEAdvBHOFactory.UpdateRegistry(Register: Boolean);
begin
   inherited;
   if Register then
     CreateRegKeyValue(HKEY_LOCAL_MACHINE, ’Software\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\’                        + GuidToString(ClassID), ’’, ’’)
   else
     DeleteRegKeyValue(HKEY_LOCAL_MACHINE, ’Software\Microsoft\Windows\CurrentVersion\explorer\Browser Helper Objects\’                        + GuidToString(ClassID), ’’);
end;
將代碼生成爲MyBHO.dll文件,然後運行“regsrv32 MyBHO.dll”進行註冊,或運行“regsrv32 MyBHO.dll /u”進行註銷。另外,別忘了在C:\新建MyIEBHO.txt文件,並在裏面輸入需要過濾的網址,否則可能導致IE和資源瀏覽器出錯。註冊成功後,重 新運行IE,在地址欄中完整地輸入待過濾網址,即可發現IE直接轉向了網易網站。
BHO插件開發總結
在Windows程序開發中,COM組件技術始終是令程序員撓頭的部分。不是COM的倡導者微軟有意難爲大家,而實在是COM太過複雜。儘管Delphi 對COM進行了很好的封裝並提供了多種模板,但開發難度依然不小。而BHO在COM中屬於較新的領域,加之資料厥如,因此對許多初涉BHO開發的程序員來 說,一時不得其門而入也在情理之中。不過,從上面的簡單示例可以看出,BHO開發基本上是模板化的,各種接口的實現差不多都是固定式樣,照抄現成的代碼就 可以了,即使沒有將代碼完全弄懂也無大礙。而真正體現編程創意和功力的地方主要集中在事件的處理代碼上,相對而言,這一部分又是BHO開發中最容易掌握 的,因此只要掌握了BHO開發訣竅,就可以化難爲易。由於BHO的接口實現代碼可以完全封裝起來,估計隨着BHO開發熱的高漲,各種BHO開發控件將陸續 面市。到那時,BHO開發將不再被視爲畏途,而IE的功能也會因BHO插件的繁榮而被大大拓展。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章