[Ray Linn]用Visual Studio 2008开发IE BHO(浏览器帮助对象) 之二
程序员文章站
2022-06-25 18:07:18
...
原文发表于blogs.ejb.cc,版权为Ray_linn所有。
上一篇文章开发的RayBHO只是BHO的一个框架,根本不具备任何功能.
在这篇文章里,我们将使继续扩展这个BHO,让它具备更强的功能.首先我们学习如何让BHO接收IE的事件通知,接者学习为ie添加一个按钮,并让BHO对按钮做出响应.
要让BHO能接收事件通知, 它必须让处理函数与浏览器事件建立连接点. 为响应这些事件,它必须实现IDispEventImpl, ATL提供了一个默认实现,可以帮助简化这个事件处理逻辑。
在RayBHO.h添加:
我们的CRayBHO必须派生自IDispEventImpl,修改后的代码如下:
DispEventImpl为处理事件提供了一种简单安全的方法。
IDispEventImpl与事件路由表配合工作,可以将事件路由到相应的处理程序函数。在例子中,我们将"DocumentComplete"的事件交由OnDocumentComplete函数进行处理.
在public段添加路由表:
上述声明中SINK_ENTRY_EX(1,...)中的"1"与接口声明中的IDispEventImpl<1,....>是对应的,在必要时可以用于区分来自不同接口的事件.
DocumentComplete将被路由到处理函数OnDocumentComplete:
它的参数和参数顺序与DocumentComplete事件所定义的相同,另请注意,不要试图从事件处理程序返回值,这是因为 Internet Explorer 会忽略任何从 Invoke 返回的值.
我们还声明了一个私有变量来跟踪事件映射的处理情况
SetSite函数中必须处理事件派遣:
我从网上找了一个OnDocumentComplete函数的例子并将之修改成范型,它对HTML Dom进行操作,将图像的属性设置为Display:None, 具体操作与javascript类似,不再赘述.
利用VC++操作HTML并没有想象中的繁琐, 你可以开发出更有趣的东西,比如从数据库自动填表单的BHO等等.
上一篇文章开发的RayBHO只是BHO的一个框架,根本不具备任何功能.
在这篇文章里,我们将使继续扩展这个BHO,让它具备更强的功能.首先我们学习如何让BHO接收IE的事件通知,接者学习为ie添加一个按钮,并让BHO对按钮做出响应.
要让BHO能接收事件通知, 它必须让处理函数与浏览器事件建立连接点. 为响应这些事件,它必须实现IDispEventImpl, ATL提供了一个默认实现,可以帮助简化这个事件处理逻辑。
在RayBHO.h添加:
#include "exdispid.h" #include "shlguid.h"
我们的CRayBHO必须派生自IDispEventImpl,修改后的代码如下:
class ATL_NO_VTABLE CRayBHO : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CRayBHO, &CLSID_RayBHO>, public IObjectWithSiteImpl<CRayBHO>, public IDispatchImpl<IRayBHO, &IID_IRayBHO, &LIBID_MySolutionPluginLib, /*wMajor =*/ 1, /*wMinor =*/ 0>, public IDispEventImpl<1,CRayBHO,&DIID_DWebBrowserEvents2,&LIBID_SHDocVw,1,1>
DispEventImpl为处理事件提供了一种简单安全的方法。
IDispEventImpl与事件路由表配合工作,可以将事件路由到相应的处理程序函数。在例子中,我们将"DocumentComplete"的事件交由OnDocumentComplete函数进行处理.
在public段添加路由表:
BEGIN_SINK_MAP(CHelloWorldBHO) SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete) END_SINK_MAP()
上述声明中SINK_ENTRY_EX(1,...)中的"1"与接口声明中的IDispEventImpl<1,....>是对应的,在必要时可以用于区分来自不同接口的事件.
DocumentComplete将被路由到处理函数OnDocumentComplete:
void STDMETHODCALLTYPE OnDocumentComplete(IDispatch* pDisp, VARIANT* URL);
它的参数和参数顺序与DocumentComplete事件所定义的相同,另请注意,不要试图从事件处理程序返回值,这是因为 Internet Explorer 会忽略任何从 Invoke 返回的值.
我们还声明了一个私有变量来跟踪事件映射的处理情况
BOOL m_fAdvised;
SetSite函数中必须处理事件派遣:
STDMETHODIMP CRayBHO::SetSite(IUnknown*pUnkSite) { if(pUnkSite!=NULL) { HRESULT hr; CComPtr<IServiceProvider> sp; hr = pUnkSite->QueryInterface(&sp); if(SUCCEEDED(hr) && sp) { //缓存指向IWebBrowser2的指针 hr = sp->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&m_spWebBrowser); if(SUCCEEDED(hr)&&m_spWebBrowser!=0) { //注册DWebBrowserEvents2事件。 hr=DispEventAdvise(m_spWebBrowser2); if(SUCCEEDED(hr)) { m_fAdvised=TRUE; } } } m_spUnkSite = pUnkSite; this->m_bIsIe7=this->IsIE7(); //hr = sp->QueryInterface(IID_IOleCommandTarget,(void**)&m_spTarget); //this->GetInternetExplorerVersion(); } else { //取消注册事件。 if(m_fAdvised) { DispEventUnadvise(m_spWebBrowser); m_fAdvised=FALSE; } //在此释放缓存的指针和其他资源。 m_spWebBrowser.Release(); //m_spTarget.Release(); } //调用基类实现。 return IObjectWithSiteImpl<CRayBHO>::SetSite(pUnkSite); }
我从网上找了一个OnDocumentComplete函数的例子并将之修改成范型,它对HTML Dom进行操作,将图像的属性设置为Display:None, 具体操作与javascript类似,不再赘述.
void STDMETHODCALLTYPE CRayBHO::OnDocumentComplete(IDispatch*pDisp,VARIANT*pvarURL) { HRESULT hr = S_OK; // 查询 IWebBrowser2 接口。 CComQIPtr<IWebBrowser2> spTempWebBrowser = pDisp; // 此事件是否与*浏览器相关联? if (spTempWebBrowser && m_spWebBrowser && m_spWebBrowser.IsEqualObject(spTempWebBrowser)) { // 从浏览器中获取当前文档对象…… CComPtr<IDispatch> spDispDoc; hr = m_spWebBrowser->get_Document(&spDispDoc); if (SUCCEEDED(hr)) { // ……并查询 HTML 文档。 CComQIPtr<IHTMLDocument2> spHTMLDoc = spDispDoc; if (spHTMLDoc != NULL) { // 最后,删除这些图像。 RemoveImages(spHTMLDoc); } } } } void CRayBHO::RemoveImages(IHTMLDocument2* pDocument) { CComPtr<IHTMLElementCollection> spImages; // 从 DOM 中获取图像集。 HRESULT hr = pDocument->get_images(&spImages); if (hr == S_OK && spImages != NULL) { // 获取集合中的图像数。 long cImages = 0; hr = spImages->get_length(&cImages); if (hr == S_OK && cImages > 0) { for (int i = 0; i < cImages; i++) { CComVariant svarItemIndex(i); CComVariant svarEmpty; CComPtr<IDispatch> spdispImage; // 按索引从集合中获取图像。 hr = spImages->item(svarItemIndex, svarEmpty, &spdispImage); if (hr == S_OK && spdispImage != NULL) { // 首先,查询通用 HTML 元素接口…… CComQIPtr<IHTMLElement> spElement = spdispImage; if (spElement) { // ……然后请求样式接口。 CComPtr<IHTMLStyle> spStyle; hr = spElement->get_style(&spStyle); // 设置 display="none" 以隐藏图像。 if (hr == S_OK && spStyle != NULL) { static const CComBSTR sbstrNone(L"none"); spStyle->put_display(sbstrNone); } } } } } } }
利用VC++操作HTML并没有想象中的繁琐, 你可以开发出更有趣的东西,比如从数据库自动填表单的BHO等等.