天天看點

實作和IE浏覽器互動的幾種方法的介紹

1.引言

  如何實作對IE浏覽器中對象的操作是一個很有實際意義問題,通過和IE綁定的DLL我們可以記錄IE浏覽過的網頁的順序,分析使用者的使用行為和模式。我們可以對網頁的内容進行過濾和翻譯,可以自動填寫網頁中經常需要使用者填寫的Form内容等等,我們所有的例子代碼都是通過VC來表示的,采用的原理是通過和IE對象的接口的互動來實作對IE的通路。實際上是采用COM的技術,我們知道COM是和語言無關的一種二進制對象互動的模式,是以實際上我們下面所描述的内容都可以用其他的語言來實作,比如VB,DELPHI,C++ Builder等等。

  2.IE執行個體周遊實作

  首先我們來看系統是如何知道目前有多少個IE的執行個體在運作。

  我們知道在Windows體系結構下,一個應用程式可以通過作業系統的運作對象表來和這些應用的執行個體進行互動。但是IE目前的實作機制是不在運作對象表中進行注冊,是以需要采用其他的方法。我們知道可以通過ShellWindows集合來代表屬于shell的目前打開的視窗的集合,而IE就是屬于shell的一個應用程式。

  下面我們描述一下用VC實作對目前 IE執行個體的進行周遊的方法。IShellWindows是關于系統shell的一個接口,我們可以定義一個如下的接口變量:

SHDocVw::IShellWindowsPtr m_spSHWinds;

  然後建立變量的執行個體:

  m_spSHWinds.CreateInstance

  (__uuidof(SHDocVw::ShellWindows));

  通過IShellWindows接口的方法GetCount

  可以得到目前執行個體的數目:

  long nCount = m_spSHWinds- >GetCount();

  通過IShellWindows接口的方法Item

  可以得到每一個執行個體對象

  IDispatchPtr spDisp;

  _variant_t va(i, VT_I4);

  spDisp = m_spSHWinds->Item(va);

  然後我們可以判斷執行個體對象是不是

  屬于IE浏覽器對象,通過下面的語句實作:

  SHDocVw::IWebBrowser2Ptr spBrowser(spDisp);

  assert(spBrowser != NULL)

在得到了IE浏覽器對象以後,我們可以調用IWebBrowser2Ptr接口的方法來得到目前的文檔對象的指針: MSHTML::IHTMLDocument2Ptr spDoc(spBrowser->GetDocument());

  然後我們就可以通過這個接口對這個文檔對象進行操作,比如通過Gettitle得到文檔的标題。

  我們在浏覽網絡的時候,一般總會同時開很多IE的執行個體,如果這些頁面都是很好的話,我們可能想儲存在硬碟上,這樣,我們需要對每一個執行個體進行儲存,而如果我們采用上面的原理,我們可以得到每一個IE的執行個體及其網頁對象的接口,這樣就可以通過一個簡單的程式來批量的儲存目前的所有打開的網頁。采用上面介紹的方法實作了對目前IE執行個體的周遊,但是我們希望得到每一個IE執行個體所産生的事件,這就需要通過DLL的機制來實作。

3.和IE相綁定的DLL的實作

  我們介紹一下如何建立和IE進行綁定的DLL的實作的過程。為了和IE的運作執行個體進行綁定,我們需要建立一個能夠和每一個IE執行個體進行綁定的DLL。IE的啟動過程是這樣的,當每一個IE的執行個體啟動的時候,它都會在系統資料庫中去尋找這個的一個CLSID,具體的系統資料庫的鍵位置為:

HKEY_LOCALL_MACHINE\SOFTWARE\Microsoft\Windows

  \CurrentVersion\Explorer\Browser Helper Objects

  當在這個鍵位置下存在CLSIDs的時候,IE會通過使用CoCreateInstance()方法來建立列在該鍵位置下的每一個對象的執行個體。注意對象的CLSIDs必須用子鍵而非名字值的形式表現,比如{DD41D66E-CE4F-11D2-8DA9-00A0249EABF4} 就是一個有效的子鍵。我們使用DLL的形式而非EXE的形式的原因是因為DLL和IE執行個體運作在同一個程序空間裡面。每一個這種形式的DLL必須實作接口IObjectWithSite,其中方法SetSite必須被實作。通過這個方法,我們自己的DLL就可以得到一個指向IE COM對象的IUnknown的指針,實際上通過這個指針我們就可以通過COM對象中的方法QueryInterface來周遊所有可以得到的接口,這是COM的基本的機制。當然我們需要的隻是IWebBrowser2這個接口。

  實際上我們建立的是一個COM對象,DLL隻不過是COM對象的一種表現形式。我們建立的COM對象需要建立和實作的方法有:

1. IOleObjectWithSite接口的方法SetSite必須實作。實際上IE執行個體通過這個方法向我們的COM對象傳遞一個接口的指針。假設我們有一個接口指針的變量,不妨設為:

CComQIPtr< IWebBrowser2, &IID_IWebBrowser2 > m_myWebBrowser2;

  我們就可以在方法SetSite中把這個傳進來的接口指針賦給m_myWebBrowser2。 2. 在我們得到了指向IE COM對象的接口後,我們需要把自己的DLL和IE執行個體所發生的事件相關連,為了實作這個目的,需要介紹兩個接口:

  (1) IConnectionPointContainer。這裡使用這個接口的目的是用來根據它得到的IID來建立和DLL的一個特定的連接配接。比如我們可以進行如下的定義:

CComQIPtr< IConnectionPointContainer,

  &IID_IConnectionPointContainer > 

  spCPContainer(m_myWebBrowser2);

然後,我們需要把所有IE中發生的事件和我們的DLL進行通訊,可以使用 IConnectPoint。

  (2) IConnectPoint。通過這個接口,客戶可以對連接配接的對象開始或者是終止一個advisory循環。IConnectPoint有兩個主要的方法,一個為Advice,另一個為Unadvise。對于我們的應用來說,Advise是用來在每一個IE發生的事件和DLL之間建立一個通道。而Unadvise就是用來終止以前用Advise建立的通知關系。比如我們可以定義IConnectPoint接口如下: CComPtr< IConnectionPoint > spConnectionPoint;

  然後,我們要使所有在IE執行個體中發生的事件和我們的DLL相關,可以使用 如下的方法:

hr = spCPContainer->FindConnectionPoint(

  DIID_DWebBrowserEvents2, &spConnectionPoint);

然後我們通過IConnectPoint接口的方法Advice使每當IE有一個新的事件發生的時候,都能夠讓我們的DLL知道。可以用如下的語句實作:

hr = spConnectionPoint- >Advise(

  (IDispatch*)this, &m_dwIDCode);

在把IE執行個體中的事件和我們的DLL之間建立聯系以後,我們可以通過IDispatch接口的Invoke()方法來處理所有的IE的事件。

3. IDispatch接口的Invoke()方法。IDispatch是從IUnknown中繼承的一個接口的類型,通過COM接口提供的任何服務都可以通過IDispatch接口來實作。IDispatch::Invoke的工作方式同vtbl幕後的工作方式是類似的,Invoke将實作一組按索引來通路的函數,我們可以對Invoke方法進行動态的定制以提供不同的服務。Invoke方法的表示如下:

STDMETHOD(Invoke)(DISPID dispidMember,REFIID

  riid, LCID lcid, WORD wFlags,

  DISPPARAMS * pdispparams, VARIANT * pvarResult,

  EXCEPINFO * pexcepinfo, UINT * puArgErr); 來

 其中,DISPID是一個長整數,它辨別的是一個函數。對于IDispatch的某一個特定的實作,DISPID都是唯一的。IDispatch的每一個實作都有其自己的IID,這裡dispidMemeber實際上是可以認為是和IE執行個體所發生的每一個事件相關的方法,比如:DISPID_BEFORENAVIGATE2,DISPID_NAVIGATECOMPLETE2等等。 這個方法中另外一個比較重要的參數是DISPPARAMS,它的結構如下:

typedef struct tagDISPPARAMS

  {

  VARIANTARG* rgvarg;

  //VARIANTARG是同VARAIANT相同的,可以在

  //OAIDL.IDL中找到。是以實際上rgvarg是一個參數數

  //組

  DISPID* rgdispidNameArgs; //命名參數的DISPID

  unsigned int cArgs; //表示數組中元素的個數

  unsigned int CnameArgs; //命名元素的個數

  }DISPPARAMS

要注意的是每一個參數的類型都是VARIANTARG,是以在IE和我們DLL之間可以傳遞的參數類型的數目是有限的。隻有那些能夠被放到VARIANTARG結構中的類型才可以通過排程接口進行傳遞。 比如對于事件DISPID_NAVIGATECOMPLETE2來說:第一個參數表示IE在通路的URL的值,類型是VT_BYREF|VT_VARIANT。注意DISPID_NAVIGATECOMPLETE2等DISPID已經在VC中被定義,我們可以直接進行使用。 如上說述,我們在方法Invoke中可以得到所有IE執行個體所發生的事件,我們可以把這些資料放到檔案中進行事後的分析,也可以放到一個清單框中實時的顯示。

  4.微軟的HTML文檔對象模型和應用分析

  下面我們來看如何得到網頁文檔的接口:網頁文檔的接口為IHTMLDocument2,可以通過調用IE COM對象的get_Document方法來得到網頁的接口。使用如下的語句:

hr = m_spWebBrowser2- >get_Document(&spDisp);

  CComQIPtr< IHTMLDocument2, 

  &IID_IHTMLDocument2 > spHTML;

  spHTML = spDisp;

  這樣我們就得到了網頁對象的接口,然後我們就可以對網頁進行分析,比如通過IHTMLDocument2提供的方法get_URL我們可以得到和該網頁相關的URL的位址值,通過get_forms方法可以該網頁中所有的Form對象的集合。實際上W3C組織已經制定了一個DOM(Document Objec Model)标準,當然這個标準不僅僅是針對HTML,同時還是針對XML制定的。W3C組織隻是定義了網頁對象的接口,不同的公司可以采用不同的語言和方法進行具體的實作。按照W3C組織定義的網頁對象被認為是動态的,即使用者可以動态的對網頁對象裡面所包含的每一個對象進行操作。這裡的對象可以是指一個輸入框,也可以是圖象和聲音等對象。同時按照W3C的正式文檔的說明,網頁對象是可以動态增加和删除的。事實上,很少有廠商實作了DOM定義的所有功能。微軟對網頁對象的定義也基本上是按照這個标準實作的。但是目前的接口還不支援動态的增加和删除元素,但是可以對網頁中的基本元素進行屬性的修改。比如IHTMLElementCollection表示網頁中一些基本的元素的集合,IHTMLElement表示網頁中的一個基本的元素。而象IHTMLOptionElement接口就表示一個特定的元素Option。基本的元素都有setAttribute和geAttribute方法來動态的設定和得到元素的名稱和值。

  較為常見的一個應用是我們能夠分析網頁中是否有需要填寫的Forms,如果這個網址的Forms以前已經填寫過而且資料我們已經儲存下來的話,我們就可以把資料自動放到和該URL下的Forms的相關的位置中去。另外,我們可以總結網頁上需要填寫的Form的資料項,先對這些資料項進行指派,以後碰到有相同的資料項的時候就自動把我們指派的内容填寫進去。實際上Form是對象,Form中包含的元素,比如INPUT,OPTION,SELECT等類型的輸入元素都是對象。

  另外一個可以想到的應用是自動對網頁中的文本進行翻譯,因為我們可以修改網頁中任何對象的屬性,是以我們可以把裡面不屬于本國語言的部分自動翻譯成本國語言,當然真正的實作還要靠自然語言了解方面技術的突破,但是IE浏覽器的接口和對象的形式使我們能夠靈活的控制整個IE,無論是從事件對象還是到網頁對象。

  5.小結

  上面我們分析了如何得到所有IE的執行個體,同時介紹了和IE執行個體相捆綁的DLL的詳細的實作機制,同時對網頁的對象化進行了分析。并且介紹了幾個相關的應用和實作的方法及存在的技術問題。IE是一個元件化的以COM為基礎的浏覽器,它具有強大的功能,同時為應用開發者留下了廣闊的空間,當然它也存在體積比較大,速度相對比較慢的缺點。但是它的體系結構代表了微軟先進的創新的技術,是以具有強大的生命力。

轉自http://edu.21cn.com/ruankao/g_185_990746-3.htm

  來源:教育聯展網- 軟體水準考試