Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1578888
  • 博文数量: 239
  • 博客积分: 1760
  • 博客等级: 上尉
  • 技术积分: 1595
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-08 23:53
文章分类

全部博文(239)

文章存档

2016年(1)

2015年(28)

2014年(53)

2013年(42)

2012年(50)

2011年(65)

分类: Windows平台

2014-05-15 15:38:48

 在自己的程序中使用 CHtmlView 或直接嵌入 Webbrowser 控件显示网页时,常常需要获取网页元素的一些事件,以实现对网页显示的控制或与网页元素进行交互。最常见的莫过于获取用户对网页上超链接的所有点击事件。要实现这个需求,在 MSDN 中描述了接收网页元素事件的基本方法,但这篇文章的一些细节语焉不详,让人摸不着头脑。在 CodeProject 这篇文章中提出了一种替代的方法,但是存在一些小的限制。本文详细演示了如何实现在 CHtmlView 中监视链接点击的方法。

为了实现监视所有的链接点击而不是特定的超链接,第一步需要在 CHtmlView 的 IHTMLDocument 上安装全局的 EventHandler 以接收 DISPID_HTMLELEMENTEVENTS2_ONCLICK(或 DISPID_HTMLDOCUMENTEVENTS2_ONCLICK)鼠标点击事件;接下来在事件处理代码中判断是否是在超链接上发生。在处理事件响应函数的安装和卸载时要格外小心,重复安装可能导致重复接收到消息甚至程序崩溃,而忘记卸载则会导致 COM 资源泄漏。
接收网页事件

要响应网页事件,需要实现 IDispatch 接口,并在其 Invoke() 方法中处理接收到的消息。对于 MFC,因为 CCmdTarget 类已经实现了 IDispatch 接口,因此继承 CCmdTarget 并结合相关宏可以较简单的处理消息。代码如下:

点击(此处)折叠或打开

  1. // DocEvtHandler.h
  2. // SDocEvtHandler 消息处理类声明 by 旧日重来

  3. #pragma once
  4. #import <mshtml.tlb>

  5. class SDocEvtHandler : public CCmdTarget
  6. {
  7.   DECLARE_DYNAMIC(SDocEvtHandler)
  8. public:
  9.   SDocEvtHandler();
  10.   virtual ~SDocEvtHandler();

  11.   // 消息处理函数
  12.   void OnClick(MSHTML::IHTMLEventObjPtr pEvtObj);

  13.   DECLARE_MESSAGE_MAP()
  14.   DECLARE_DISPATCH_MAP()
  15.   DECLARE_INTERFACE_MAP()
  16. };

  17. // DocEvtHandler.cpp
  18. // SDocEvtHandler 消息处理类实现 by 旧日重来

  19. #include "stdafx.h"
  20. #include "DocEvtHandler.h"
  21. #include "mshtmdid.h"

  22. IMPLEMENT_DYNAMIC(SDocEvtHandler, CCmdTarget)

  23. SDocEvtHandler::SDocEvtHandler()
  24. {
  25.   EnableAutomation(); // 重要:激活 IDispatch
  26. }

  27. SDocEvtHandler::~SDocEvtHandler()
  28. {
  29. }

  30. BEGIN_MESSAGE_MAP(SDocEvtHandler, CCmdTarget)
  31. END_MESSAGE_MAP()

  32. BEGIN_DISPATCH_MAP(SDocEvtHandler, CCmdTarget)
  33.   DISP_FUNCTION_ID(SDocEvtHandler,"HTMLELEMENTEVENTS2_ONCLICK",
  34.   DISPID_HTMLELEMENTEVENTS2_ONCLICK, OnClick,
  35.   VT_EMPTY, VTS_DISPATCH)
  36. END_DISPATCH_MAP()

  37. BEGIN_INTERFACE_MAP(SDocEvtHandler, CCmdTarget)
  38.   INTERFACE_PART(SDocEvtHandler,
  39.   DIID_HTMLButtonElementEvents2, Dispatch)
  40. END_INTERFACE_MAP()

  41. void SDocEvtHandler::OnClick(MSHTML::IHTMLEventObjPtr pEvtObj)
  42. {
  43.   // 鼠标点击处理代码...详见下节
  44. }

接下来,在 DocumentComplete 事件中安装事件处理响应函数:

点击(此处)折叠或打开

  1. // 事件响应函数的管理 by 旧日重来
  2. ///////////////////// .h //////////////////////
  3. class SWebpageView : public CHtmlView
  4. {
  5.   // 其他代码...
  6.   SDocEvtHandler *m_pEventHandler;
  7.   DWORD m_dwDocCookie; // 用于卸载事件响应函数
  8.   IDispatch *m_pDispDoc; // 用于卸载事件响应函数
  9. };

  10. //////////////////// .cpp ////////////////////
  11. SWebpageView::SWebpageView()
  12. : m_dwDocCookie(0)
  13. , m_pDispDoc(NULL)
  14. {
  15.   m_pEventHandler = new SDocEvtHandler;
  16. }

  17. // 安装响应函数。省略了一些失败判断以突出主要步骤
  18. void SWebpageView::InstallEventHandler()
  19. {
  20.   if(m_dwDocCookie) // 已安装,卸载先。最后一次安装的才有效
  21.     UninstallEventHandler();

  22.   m_pDispDoc = GetHtmlDocument();
  23.   IConnectionPointContainerPtr pCPC = m_pDispDoc;
  24.   IConnectionPointPtr pCP;
  25.   // 找到安装点
  26.   pCPC->FindConnectionPoint(DIID_HTMLDocumentEvents2, &pCP);
  27.   IUnknown* pUnk = m_pEventHandler->GetInterface(&IID_IUnknown);
  28.   //安装
  29.   HRESULT hr = pCP->Advise(pUnk, &m_dwDocCookie);
  30.   if(!SUCCEEDED(hr)) // 安装失败
  31.     m_dwDocCookie = 0;
  32. }

  33. // 卸载响应函数。省略了一些失败判断以突出主要步骤
  34. void SWebpageView::UninstallEventHandler()
  35. {
  36.   if(0 == m_dwDocCookie) return;

  37.   IConnectionPointContainerPtr pCPC = m_pDispDoc;
  38.   IConnectionPointPtr pCP;
  39.   pCPC->FindConnectionPoint(DIID_HTMLDocumentEvents2, &pCP);
  40.   hr = pCP->Unadvise(m_dwDocCookie);
  41. }

  42. // 在 DocumentComplete 事件中安装响应函数
  43. void SWebpageView::OnDocumentComplete(LPCTSTR lpszURL)
  44. {
  45.   // 其他代码...
  46.   InstallEventHandler();
  47. }

  48. // 在 BeforeNavigate2 和 Destroy 事件中卸载响应函数
  49. void SWebpageView::OnBeforeNavigate2(/* ... */)
  50. {
  51.   UninstallEventHandler();
  52.   // 其他代码...
  53. }
  54. void SWebpageView::OnDestroy()
  55. {
  56.   UninstallEventHandler();
  57.   CHtmlView::OnDestroy();
  58. }
在正确安装了事件响应函数之后,就可以接收网页事件了。
检测超链接点击事件

全局事件处理接口成功安装后,当 Webbrowser 控件中有相应的事件发生,则会自动调用事件响应函数。在上面的情况下,会接收到网页中所有的鼠标点击事件,因此我们需要判断用户是否是点击超链接对象。因为超链接内部可能还包含有子结构,例如图像,因此鼠标点击事件不一定直接发生在超链接对象,因此需要根据事件发生的对象逐级向上检查,代码如下:


点击(此处)折叠或打开

  1. void SDocEvtHandler::OnClick(MSHTML::IHTMLEventObjPtr pEvtObj)
  2. {
  3.   MSHTML::IHTMLElementPtr pElement =
  4.     pEvtObj->GetsrcElement(); // 事件发生的对象元素
  5.   while(pElement) // 逐层向上检查
  6.   {
  7.     _bstr_t strTagname;
  8.     pElement->get_tagName(&strTagname.GetBSTR());
  9.     if(_bstr_t("a") == strTagname || _bstr_t("A") == strTagname)
  10.     {
  11.       // 已找到 "a" 标签,在这里写相应代码
  12.       // 例1:取得目标地址:
  13.       _variant_t vHref = pElement->getAttribute("href", 0);
  14.       // 例2:取消点击,禁止转到目标页面
  15.       pEvtObj->put_returnValue(_variant_t(VARIANT_FALSE, VT_BOOL));
  16.       break;
  17.     }
  18.     pElement = pElement->GetparentElement();
  19.   }
  20. }


转载自:http://blog.csdn.net/zhongbin104/article/details/8731163

阅读(3989) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~