如何对webbrowser和IE编程(九)

接收事件的途径

依靠开发工具你创建客户应用程序 ,你可以接收事件通过不同的途径. 显然, 在Vb中接收事件同在VC中接收事件相比是如此不同和容易.在 C++ 应用中,你可以用不同的技术,通过使用 ATL, MFC, 或者标准C++.

Visual Basic 中接收事件

Visual Basic是创建大多数类型应用的最轻松的工具, 所以我告诉你VB是处理事件最溶的工具时也不要惊奇. ATL 和 Visual Basic 示例我们同样的工作,但是ATL花费了我4个小时, 而 Visual Basic 例子仅仅只花20 分钟.别说我错了—我是ATL, 和 MFC, C++的忠实信徒, 尤其是你建立一个接口的时候.但是 Visual Basic当建立客户应用程序从类似IE这样的服务器接收事件时是伟大的工具.

OK,如何从Visual Basic 应用程序中接收事件?当宿主WebBrowser 控件,你不必做任何特别的事. Visual Basic 在form上为WebBrowser 控件接收事件.你所需要做的全部事情就是未你要接收的任何事件创建一个事件处理句柄.

你象创建其他事件句柄一样创建句柄 (例如 Form_Load event). 从Procedure下拉列表框中选择你象控制的句柄, 在事件句柄中,加入任何你型在事件激发时执行的任何代码.

当自动化服务器时候接收事件 , 例如在VB应用中的Internet Explorer,过程直截了当.首先设置对服务器的类型库的引用, 你可以访问Project/References 菜单.之后,采用 WithEvents 关键字声明服务器对象的变量.举例, 如果你自动化Internet Explorer, 你将声明变量如下:

Dim WithEvents InternetExplorer1 As InternetExplorer  

下一步,采用 new或者其他 关键字创建实例变量 ,如下::

Set InternetExplorer1 = CreateObject("InternetExplorer.Application.1")  

或者 :

Set InternetExplorer1 = New InternetExplorer  

当你采用以上途径生成实例接收事件 , Visual Basic 自动为你初始化和管理事件接收.你不必担心连接点问题,VB为你处理它们.

在你输入建立服务器的代码之后 ,你插入符合服务器事件的方法调用. 举例来说, 如果你想控制由IE激活的 DownloadBegin event, 你应当声明类似如下的方法声明:

Private Sub InternetExplorer1_DownloadBegin()


   ' Insert your best Visual Basic code here.


End Sub  

当你不再想接收来自服务器的事件,简单设置变量为 _ Nothing _ :

Set InternetExplorer1 = Nothing  


C++中接收事件

C++ 应用程序中接收事件比Vb中多一些工作.但如果你在MFC对话框程序中宿主过WebBrowser控件, 你可以在classwizard中选择你想控制的事件.使用C++的其他应用程序宿主WebBrowser 或者自动化Internet Explorer 需要多一点的工作,但是仍然不需要更多的工作.在C++客户接收事件,仅仅需要以下5个步骤:

1. 获取连接点容器的指针 ( IConnectionPointContainer ).

2. 调用 _ IconnectionPointContainer _ 的方法 _ FindConnectionPoint _ 找出你想接收的事件。对 Internet Explorer 来讲 , 你应当为 _ DWebBrowserEvents2 _ 连接点接口实现事件 . ( 作为可选 , 你可以调用 _ EnumConnectionPoints _ 以枚举服务器支持的全部连接点 )

3. 实现接入你想接收事件的连接点的通报( Advise )。 当实现通告时 , 传递一个事件接收槽的 _ Iunknown _ 接口的指针。 记住,事件接收槽必须实现 _ IDispatch _ 接口以接收来自 WebBrowser 的事件。 _ Advise _ 方法将返回一个 cookie ,该 Cookie 在你调用 _ Unadvise _ 方法的时候携带上。

4. 实现 _ IDispatch::Invoke _ 以控制任何激发的事件。 . ( 开发工具如 MFC 及 ATL 能够容易为你做到 .)

5. 当你不再接受事件,调用 _ Unadvise _ , __ 并且传递 cookie.

以上步骤如果采用 VB和MFC \ATL等可能不很明显,但是当你采用标准C++创建应用程序的时候就应当很明显了.

以下 C++ 代码允许你在自动化IE的时候接收事件. 留意注释代码实现了哪一个步骤. 假定当你想连接事件时 ConnectEvents 方法被调用,且当应用程序退出时候 Exit 方法被调用. 同样的,类 CSomeClass 继承自 IDispatch , __ 且 m_pIE 数据成员为通过 CoCreateInstance 方法创建的IE的实例

void CSomeClass::ConnectEvents()


{


   IConnectionPointContainer* pCPContainer;


 


   // Step 1: 获取连接点的指针.


   //


   HRESULT hr = m_pIE->QueryInterface(IID_IConnectionPointContainer, 


                                      (void**)&pCPContainer);


   if (SUCCEEDED(hr))


   {


      // m_pConnectionPoint is defined like this:


      // IConnectionPoint* m_pConnectionPoint;


 


      // Step 2: 选找连接点.


      //


      hr = pCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2, 


                                             &m_pConnectionPoint);


 


      if (SUCCEEDED(hr))


      {


         // Step 3: 实现连接点地事件接收


         //


         hr = m_pConnectionPoint->Advise(this, &m_dwCookie);


         if (FAILED(hr))


         {


            ::MessageBox(NULL, "Failed to Advise",


                         "C++ Event Sink", MB_OK);


         }


      }


 


      pCPContainer->Release();


   }


}


 


void CSomeClass::Exit()


{


   // Step 5: Unadvise. 注意m_pConnectionPoint 应当在CSomeClass的析构函数中释放


   //


   if (m_pConnectionPoint)


   {


      HRESULT hr = m_pConnectionPoint->Unadvise(m_dwCookie);


      if (FAILED(hr))


      {


         ::MessageBox(NULL, "Failed to Unadvise",


                      "C++ Event Sink", MB_OK);


      }


   }


}  

注意此处少了 step4:客户端的 IDispatch::Invoke 方法实现. 我将很快讨论此点. 每一次服务器激发事件会调用此. 当事件被激发,服务器传递事件的DISPID 到 Invoke . 对于 Internet Explorer 5, 以下DISPIDs 定义于ExDispID.h 头文件.

· DISPID_BEFORENAVIGATE2

  • DISPID_COMMANDSTATECHANGE
  • DISPID_DOCUMENTCOMPLETE
  • DISPID_DOWNLOADBEGIN
  • DISPID_DOWNLOADCOMPLETE
  • DISPID_NAVIGATECOMPLETE2
  • DISPID_NEWWINDOW2
  • DISPID_ONFULLSCREEN
  • DISPID_ONMENUBAR
  • DISPID_ONQUIT
  • DISPID_ONSTATUSBAR
  • DISPID_ONTHEATERMODE
  • DISPID_ONTOOLBAR
  • DISPID_ONVISIBLE
  • DISPID_PROGRESSCHANGE
  • DISPID_PROPERTYCHANGE
  • DISPID_STATUSTEXTCHANGE
  • DISPID_TITLECHANGE

现在我们返回讨论 _ Invoke _ . 该方法有8个参数, 但我们将仅仅讨论其中的两个: dispidMemberpDispParams . (其余的参见MSDN中的 IDispatch::Invoke .)

_ dispidMember _ 参数将告诉你哪一个事件被激发.如果客户应用程序接收来自Internet Explorer的事件, dispidMember 参数的值应当是DISPIDs 列表中的某个.

_ pDispParams _ 输入参数是指向容器结构的指针, 存储事件激发时的其他项. 传递到事件句柄的参数存储在 pDispParams -> rgvarg ,逆序存放. 举例来说, Internet Explorer 激发NavigateComplete2 事件如下所示:

NavigateComplete2(pDisp, URL)  

当 _ Invoke _ 被调用, _pDispParams- >cArgs _ 将包含两个值, URL 参数在 _pDispParams- >rgvarg[0] _ 以及 pDisp 参数存储在 _pDispParams- >rgvarg[1] _ . 这些就是COM次序传递参数给 Invoke 方法的方式.

以下为 _ NavigateComplete2 _ 事件的处理.注意采用ATL的 CComVariant 处理从 VARIANTBSTR 包装.

#include
  1<strstrea.h>
  2    
  3    
  4    STDMETHODIMP CSomeClass::Invoke(DISPID dispidMember,
  5    
  6    
  7                                    REFIID riid,
  8    
  9    
 10                                    LCID lcid, 
 11    
 12    
 13                                    WORD wFlags,
 14    
 15    
 16                                    DISPPARAMS* pDispParams,
 17    
 18    
 19                                    VARIANT* pvarResult,
 20    
 21    
 22                                    EXCEPINFO*  pExcepInfo,
 23    
 24    
 25                                    UINT* puArgErr)
 26    
 27    
 28    {
 29    
 30    
 31       USES_CONVERSION;
 32    
 33    
 34       strstream strEventInfo;
 35    
 36    
 37     
 38    
 39    
 40       if (!pDispParams)
 41    
 42    
 43          return E_INVALIDARG;
 44    
 45    
 46     
 47    
 48    
 49       switch (dispidMember)
 50    
 51    
 52       {
 53    
 54    
 55          // The parameters for this DISPID:
 56    
 57    
 58          // [0]: URL navigated to - VT_BYREF|VT_VARIANT
 59    
 60    
 61          // [1]: An object that evaluates to the top-level or frame
 62    
 63    
 64          //      WebBrowser object corresponding to the event. 
 65    
 66    
 67          //
 68    
 69    
 70          case DISPID_NAVIGATECOMPLETE2:
 71    
 72    
 73             // Check the argument's type.
 74    
 75    
 76             if (pDispParams-&gt;rgvarg[0].vt == (VT_BYREF|VT_VARIANT))
 77    
 78    
 79             {
 80    
 81    
 82                CComVariant varURL(*pDispParams-&gt;rgvarg[0].pvarVal);
 83    
 84    
 85                varURL.ChangeType(VT_BSTR);
 86    
 87    
 88     
 89    
 90    
 91                // strEventInfo is an object of type strstream.
 92    
 93    
 94                //
 95    
 96    
 97                strEventInfo &lt;&lt; "NavigateComplete2: "
 98    
 99    
100                             &lt;&lt; OLE2T(vtURL.bstrVal)
101    
102    
103                             &lt;&lt; ends;
104    
105    
106     
107    
108    
109                ::MessageBox(NULL, strEventInfo.str(), "Invoke", MB_OK);
110    
111    
112             }
113    
114    
115             break;    
116    
117    
118     
119    
120    
121          default:
122    
123    
124             break;
125    
126    
127       }
128    
129    
130     
131    
132    
133       return S_OK;
134    
135    
136    }  
137  
138---  
139  
140##  在  ATL中接收事件 
141
142连同实现了缺省的  COM 接口实现, ATL提供了两个函数— _AtlAdvise_ 和 _AtlUnadvise_ —使得任何课连接对象的事件接收简单化. 
143
144_ AtlAdvise  _ 函数告诉一个可连接对象客户想从此可连接对象接收事件.该函数封装实现接收事件的步骤1到3. _AtlAdvise_ 理所当然省了大量的时间.就像 _IConnectionPoint::Advise_ 方法, _AtlAdvise_ 返回一个cookie供你稍后调用 _AtlUnadvise_ . _AtlUnadvise_ 告诉可连接对象客户不再接收事件. 
145
146让我们行说吧, 举个例子  , ATL应用程序自动化Internet Explorer, 所以你想知道任何IE激发的事件. 为了告知Internet Explorer客户想接收事件,发出对 _AtlAdvise_ 的以下调用: 
147    
148    
149    HRESULT hr = AtlAdvise(m_spInetExplorer, GetUnknown(),
150    
151    
152                           DIID_DWebBrowserEvents2, &amp;m_dwCookie);  
153  
154---  
155  
156四个参数传递给 _ AtlAdvise  _ . 第一个参数是指向可连接对象的 _IUnknown_ 接口的指针. _m_spInetExplorer_ 数据成员是一个经过我们自动化当前运行的Internet Explorer实例的指针. 因为 _m_spInetExplorer_ 指向的对象直接或者间接继承自 _IUnknown_ , 编译器自动转换 _m_spInetExplorer_ 为当前运行的 IE实例的 _IUnknown_ 接口指针. 
157
158_ AtlAdvise  _ 第二个参数必须指向提供事件的对象的 _IUnknown_ 接口. _GetUnknown_ 函数返回此接口.记住,提供事件的类必须通过某种途径实现 _IDispatch_ in.在此例子中,该类继承自 _IDispatch_ . 
159
160第三个参数为你象接收的事件的  IID, Internet Explorer 事件的可连接对象的IIS是 _DIID_DWebBrowserEvents2_ . 
161
162最后一个参数指向  DWORD的指针,该DWORD接收返回的Cookie. 该 cookie 将用于调用 _AtlUnadvise_ . 
163
164客户必须实现 _ IDispatch::Invoke  _ 以控制Internet Explorer 激发的事件. 当你的应用程序完成从IE接收事件, 只需要调用 call _AtlUnadvise_ , 如下: 
165    
166    
167    HRESULT hr = AtlUnadvise(m_spInetExplorer,
168    
169    
170                             DIID_DWebBrowserEvents2, 
171    
172    
173                             m_dwCookie);  
174  
175---  
176  
177** ![](http://dev.csdn.net/images/blog_csdn_net/shanhe/CH7-3.jpg) **
178
179** Figure 7-3.  ** _ATLIEEvtSpy._
180
181以下展示如何自动化  IE: 
182    
183    
184    hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, 
185    
186    
187                          IID_IWebBrowser2, (void**)&amp;m_spInetExplorer);
188    
189    
190    if (SUCCEEDED(hr))
191    
192    
193    {
194    
195    
196       m_spInetExplorer-&gt;put_Visible(TRUE);
197    
198    
199       m_spInetExplorer-&gt;GoHome();        
200    
201    
202       
203  
204
205---  
206  
207接下来  , _AtlAdvise_ 调用以接收事件, 如下: 
208    
209    
210    hr = AtlAdvise(m_spInetExplorer, GetUnknown(),
211    
212    
213                   DIID_DWebBrowserEvents2, &amp;m_dwCookie);  
214  
215---  
216  
217_ CIEEvtObj  _ 类继承自 _IDispatch_ , __ 所以 _CIEEvtObj_ 类可以作为事件接收对象. _Invoke_ 实现控制事件. 每当Internet Explorer 激发一个事件, 在listBox中显示一个消息.以西为invoke的代码: 
218    
219    
220    STDMETHODIMP CIEEvtObj::Invoke(DISPID dispidMember, 
221    
222    
223                                   REFIID riid, 
224    
225    
226                                   LCID lcid, 
227    
228    
229                                   WORD wFlags, 
230    
231    
232                                   DISPPARAMS* pDispParams, 
233    
234    
235                                   VARIANT* pvarResult,
236    
237    
238                                   EXCEPINFO* pExcepInfo,  
239    
240    
241                                   UINT* puArgErr)
242    
243    
244    {
245    
246    
247       _ASSERT(m_spInetExplorer);
248    
249    
250     
251    
252    
253       USES_CONVERSION;
254    
255    
256       strstream strEventInfo;
257    
258    
259     
260    
261    
262       if (!pDispParams)
263    
264    
265          return E_INVALIDARG;
266    
267    
268     
269    
270    
271       switch (dispidMember)
272    
273    
274       {
275    
276    
277          //
278    
279    
280          // The parameters for this DISPID are as follows:
281    
282    
283          // [0]: Cancel flag  - VT_BYREF|VT_BOOL
284    
285    
286          // [1]: HTTP headers - VT_BYREF|VT_VARIANT
287    
288    
289          // [2]: Address of HTTP POST data  - VT_BYREF|VT_VARIANT 
290    
291    
292          // [3]: Target frame name - VT_BYREF|VT_VARIANT 
293    
294    
295          // [4]: Option flags - VT_BYREF|VT_VARIANT
296    
297    
298          // [5]: URL to navigate to - VT_BYREF|VT_VARIANT
299    
300    
301          // [6]: An object that evaluates to the top-level or frame
302    
303    
304          //      WebBrowser object corresponding to the event 
305    
306    
307          //
308    
309    
310          case DISPID_BEFORENAVIGATE2:
311    
312    
313             strEventInfo &lt;&lt; "BeforeNavigate2: ";
314    
315    
316     
317    
318    
319             if (pDispParams-&gt;cArgs &gt;= 5
320    
321    
322                &amp;&amp; pDispParams-&gt;rgvarg[5].vt == (VT_BYREF|VT_VARIANT))
323    
324    
325             {
326    
327    
328                CComVariant vtURL(*pDispParams-&gt;rgvarg[5].pvarVal);
329    
330    
331                vtURL.ChangeType(VT_BSTR);
332    
333    
334     
335    
336    
337                strEventInfo &lt;&lt; OLE2T(vtURL.bstrVal);
338    
339    
340             }
341    
342    
343             else
344    
345    
346                strEventInfo &lt;&lt; "NULL";
347    
348    
349     
350    
351    
352             strEventInfo &lt;&lt; ends;
353    
354    
355             break;
356    
357    
358     
359    
360    
361          //
362    
363    
364          // The parameters for this DISPID:
365    
366    
367          // [0]: Enabled state - VT_BOOL
368    
369    
370          // [1]: Command identifier - VT_I4
371    
372    
373          //
374    
375    
376          case DISPID_COMMANDSTATECHANGE:
377    
378    
379             strEventInfo &lt;&lt; "CommandStateChange: ";
380    
381    
382     
383    
384    
385             if (pDispParams-&gt;cArgs == 0)
386    
387    
388                strEventInfo &lt;&lt; "NULL";
389    
390    
391             else
392    
393    
394             {
395    
396    
397                if (pDispParams-&gt;cArgs &gt; 1 
398    
399    
400                   &amp;&amp; pDispParams-&gt;rgvarg[1].vt == VT_I4)
401    
402    
403                {
404    
405    
406                   strEventInfo &lt;&lt; "Command = " 
407    
408    
409                                &lt;&lt; pDispParams-&gt;rgvarg[1].lVal;
410    
411    
412                }
413    
414    
415     
416    
417    
418                if (pDispParams-&gt;rgvarg[0].vt == VT_BOOL)
419    
420    
421                {
422    
423    
424                   
425  
426---</strstrea.h>
Published At
Categories with Web编程
Tagged with
comments powered by Disqus