[Ray Linn]用Visual Studio 2008开发IE BHO(浏览器帮助对象) 之二

上一篇文章开发的RayBHO只是BHO的一个框架,根本不具备任何功能.

在这篇文章里,我们将使继续扩展这个BHO,让它具备更强的功能.首先我们学习如何让BHO接收IE的事件通知,接者学习为ie添加一个按钮,并让BHO对按钮做出响应.

要让BHO能接收事件通知, 它必须让处理函数与浏览器事件建立连接点. 为响应这些事件,它必须实现IDispEventImpl, ATL提供了一个默认实现,可以帮助简化这个事件处理逻辑。
在RayBHO.h添加:
Java代码 复制代码
  1. #include "exdispid.h"  
  2. #include "shlguid.h"  


我们的CRayBHO必须派生自IDispEventImpl,修改后的代码如下:

Java代码 复制代码
  1. class ATL_NO_VTABLE CRayBHO :   
  2.     public CComObjectRootEx<CComSingleThreadModel>,   
  3.     public CComCoClass<CRayBHO, &CLSID_RayBHO>,   
  4.     public IObjectWithSiteImpl<CRayBHO>,   
  5.     public IDispatchImpl<IRayBHO, &IID_IRayBHO, &LIBID_MySolutionPluginLib, /*wMajor =*/ 1/*wMinor =*/ 0>,   
  6.     public IDispEventImpl<1,CRayBHO,&DIID_DWebBrowserEvents2,&LIBID_SHDocVw,1,1>  


DispEventImpl为处理事件提供了一种简单安全的方法。

IDispEventImpl与事件路由表配合工作,可以将事件路由到相应的处理程序函数。在例子中,我们将"DocumentComplete"的事件交由OnDocumentComplete函数进行处理.

在public段添加路由表:
Java代码 复制代码
  1. BEGIN_SINK_MAP(CHelloWorldBHO)   
  2. SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)   
  3. END_SINK_MAP()  


上述声明中SINK_ENTRY_EX(1,...)中的"1"与接口声明中的IDispEventImpl<1,....>是对应的,在必要时可以用于区分来自不同接口的事件.

DocumentComplete将被路由到处理函数OnDocumentComplete:
Java代码 复制代码
  1. void STDMETHODCALLTYPE OnDocumentComplete(IDispatch* pDisp, VARIANT* URL);  


它的参数和参数顺序与DocumentComplete事件所定义的相同,另请注意,不要试图从事件处理程序返回值,这是因为 Internet Explorer 会忽略任何从 Invoke 返回的值.
我们还声明了一个私有变量来跟踪事件映射的处理情况

Java代码 复制代码
  1. BOOL m_fAdvised;   



SetSite函数中必须处理事件派遣:
Java代码 复制代码
  1. STDMETHODIMP CRayBHO::SetSite(IUnknown*pUnkSite)   
  2. {   
  3.     if(pUnkSite!=NULL)   
  4.     {   
  5.         HRESULT hr;   
  6.         CComPtr<IServiceProvider> sp;   
  7.   
  8.         hr = pUnkSite->QueryInterface(&sp);   
  9.         if(SUCCEEDED(hr) && sp)   
  10.         {   
  11.             //缓存指向IWebBrowser2的指针   
  12.             hr = sp->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&m_spWebBrowser);   
  13.   
  14.             if(SUCCEEDED(hr)&&m_spWebBrowser!=0)   
  15.             {   
  16.                 //注册DWebBrowserEvents2事件。   
  17.                 hr=DispEventAdvise(m_spWebBrowser2);   
  18.                
  19.                 if(SUCCEEDED(hr))   
  20.                 {   
  21.                     m_fAdvised=TRUE;   
  22.                 }   
  23.             }   
  24.         }   
  25.         m_spUnkSite = pUnkSite;   
  26.         this->m_bIsIe7=this->IsIE7();   
  27.            
  28.         //hr = sp->QueryInterface(IID_IOleCommandTarget,(void**)&m_spTarget);   
  29.         //this->GetInternetExplorerVersion();   
  30.   
  31.     }   
  32.     else  
  33.     {   
  34.         //取消注册事件。   
  35.         if(m_fAdvised)   
  36.         {   
  37.             DispEventUnadvise(m_spWebBrowser);   
  38.             m_fAdvised=FALSE;   
  39.         }   
  40.         //在此释放缓存的指针和其他资源。   
  41.         m_spWebBrowser.Release();   
  42.         //m_spTarget.Release();   
  43.     }   
  44.     //调用基类实现。   
  45.     return IObjectWithSiteImpl<CRayBHO>::SetSite(pUnkSite);   
  46. }  


我从网上找了一个OnDocumentComplete函数的例子并将之修改成范型,它对HTML Dom进行操作,将图像的属性设置为Display:None, 具体操作与javascript类似,不再赘述.

Java代码 复制代码
  1. void    STDMETHODCALLTYPE   CRayBHO::OnDocumentComplete(IDispatch*pDisp,VARIANT*pvarURL)   
  2. {   
  3.     HRESULT hr = S_OK;    
  4.     // 查询 IWebBrowser2 接口。    
  5.     CComQIPtr<IWebBrowser2> spTempWebBrowser = pDisp;    
  6.     // 此事件是否与顶级浏览器相关联?    
  7.     if (spTempWebBrowser && m_spWebBrowser && m_spWebBrowser.IsEqualObject(spTempWebBrowser))    
  8.     {    
  9.         // 从浏览器中获取当前文档对象……    
  10.         CComPtr<IDispatch>  spDispDoc;    
  11.         hr = m_spWebBrowser->get_Document(&spDispDoc);    
  12.         if (SUCCEEDED(hr))    
  13.         {    
  14.             // ……并查询 HTML 文档。    
  15.             CComQIPtr<IHTMLDocument2> spHTMLDoc = spDispDoc;    
  16.             if (spHTMLDoc != NULL) {    
  17.                 // 最后,删除这些图像。    
  18.                 RemoveImages(spHTMLDoc);    
  19.             }    
  20.         }    
  21.     }    
  22. }   
  23.   
  24. void CRayBHO::RemoveImages(IHTMLDocument2* pDocument)    
  25. {    
  26.     CComPtr<IHTMLElementCollection> spImages;    
  27.     // 从 DOM 中获取图像集。    
  28.     HRESULT hr = pDocument->get_images(&spImages);    
  29.     if (hr == S_OK && spImages != NULL) {    
  30.         // 获取集合中的图像数。    
  31.         long cImages = 0;    
  32.         hr = spImages->get_length(&cImages);    
  33.         if (hr == S_OK && cImages > 0)    
  34.         {    
  35.             for (int i = 0; i < cImages; i++)    
  36.             {    
  37.                 CComVariant svarItemIndex(i);    
  38.                 CComVariant svarEmpty;    
  39.                 CComPtr<IDispatch> spdispImage;    
  40.                 // 按索引从集合中获取图像。    
  41.                 hr = spImages->item(svarItemIndex, svarEmpty, &spdispImage);    
  42.                 if (hr == S_OK && spdispImage != NULL)    
  43.                 {    
  44.                     // 首先,查询通用 HTML 元素接口……    
  45.                     CComQIPtr<IHTMLElement> spElement = spdispImage;    
  46.                     if (spElement)    
  47.                     {    
  48.                         // ……然后请求样式接口。    
  49.                         CComPtr<IHTMLStyle> spStyle;    
  50.                         hr = spElement->get_style(&spStyle);    
  51.                         // 设置 display="none" 以隐藏图像。    
  52.                         if (hr == S_OK && spStyle != NULL)    
  53.                         {    
  54.                             static const CComBSTR sbstrNone(L"none");    
  55.                             spStyle->put_display(sbstrNone);   
  56.                         }   
  57.                     }    
  58.                 }    
  59.             }    
  60.         }    
  61.     }    
  62. }  


利用VC++操作HTML并没有想象中的繁琐, 你可以开发出更有趣的东西,比如从数据库自动填表单的BHO等等.
posted @ 2009-05-05 10:22  jcss  阅读(1318)  评论(0编辑  收藏  举报