天天看點

在應用程式中內建浏覽器控件(Update)(Subjet to change without notice)在應用程式中內建浏覽器控件

在應用程式中內建浏覽器控件

概述

什麼是浏覽器控件

浏覽器控件是一個提供浏覽器絕大部分功能的ActiveX控件,随Microsoft? Internet Explorer 4.0(IE)或者更高版本發行。實際上,IE可以認為是一個內建浏覽器控件的程式。

為什麼要使用浏覽器控件

怎麼給使用者提供豐富的内容一直是程式員們努力的目标。盡管各種各樣的界面庫可能使你眼花缭亂,但是這些也是美工和程式員的惡夢——要自定義界面上的每個元素的外觀并不是一件容易的事情,而且有時候需要比較複雜的技術,例如自定義程式中出現的滾動條的顔色。

浏覽器控件的出現使這一切變得簡單。一個簡單的應用是使用浏覽器控件顯示豐富的内容,就像Microsoft Outlook的預覽窗格。你可以讓美工為你做出華麗的效果,例如界面上面的漸變效果、動畫GIF或者Flash動畫,而頂多隻需要編寫少量的HTML代碼;進階的應用包括使用浏覽器控件顯示整個或者部分使用者界面,例如Norton Antivirus和Real Player都使用HTML來顯示界面,使用層疊樣式表統一界面風格;使用DHTML對象模型進行無界面網頁分析,或者編寫浏覽器輔助對象(BHO)來自定義浏覽器的行為。

我也必須在這裡說一下內建浏覽器可能的問題,以供使用者斟酌

浏覽器控件是程序内元件。也就是說,如果浏覽器或者相關子產品崩潰,那麼整個程式會崩潰。雖然這不太可能會發生,但是由于浏覽器加載的元件/插件太多太多,萬無一失是肯定做不到的。這可能也是預設情況下,浏覽器控件不會加載BHO的原因。

浏覽器占用的資源很多。這包括CPU資源和記憶體。同時,網頁可能需要額外的資源,例如用戶端XML解析(CSDN……),動态的圖檔,ActiveX控件(大部分是Flash控件)。實際上,如果不限制使用者浏覽的網址,那麼網站上常見的廣告(通常是動态的圖檔或者Flash控件)會大大增加浏覽器占用的資源,特别是CPU資源。

浏覽器控件中加載的腳本可能造成程式不穩定。微軟推薦禁用腳本,但是這會大大降低浏覽器控件的實用性,同時使得某些進階的自定義特性,例如擴充DOM,變得毫無意義。

浏覽器不太适合自動化的浏覽操作。盡管微軟把浏覽器作為作業系統的核心,但是我們還是看到,在浏覽器控件不斷浏覽網頁的過程中,它的記憶體占用不斷上升。因為這種現象,浏覽器控件推薦的用途還是作為靜态或者人控的顯示。

入門

在使用浏覽器控件之前,你需要建立它。這可以通過在對話框編輯器中插入微軟浏覽器控件,或者使用類辨別CLSID_WebBrowser建立一個控件來完成。如果你僅僅把控件用于顯示,那麼第一種方法就足夠了,如果你需要一些比較進階的功能,例如自定義一些特性,那麼你需要用代碼來控制控件的建立過程。某些類庫,(例如MFC),内建了對浏覽器控件的支援(CHTMLxxxx和CDHTMLDialog類),但是它們在提供你通路浏覽器控件的便利的同時,也使得你自定義浏覽器的過程更加複雜。在我看來,它們最好的用途是用作通路浏覽器控件的示例和自定義的基礎。

浏覽器控件提供的一個最主要的COM接口是IWebBrowser2。它提供一個浏覽器的基本操作接口,以及這個接口的一些方法,例如比較常用的方法是Navigate/Navigate2,它使得浏覽器控件打開一個你指定的目标,例如一個檔案夾,一個網頁,或者一個活動文檔。你可能需要要有效地了解IWebBrowser2接口提供給你的應用程式的特性,你首先需要對Internet Explorer和DHTML兩者的對象模型有一個基本的了解。當然,完全解釋這些模型可能會占據大量篇幅,因為DHTML檔案中每種獨立的元素類型(<B>、 <HTML>、 <BODY>等等)都具有代表自己的COM對象類或者接口。獲得對這些對象模型的徹底的了解的最好方法是看一些書和獲得一個工具。Inside Dynamic HTML (微軟出版社 ,1997)是DHTML對象模型的最好的參考書之一。關于對象模型的更多技術資訊可以通過研究Internet Client SDK的DHTML相關頭檔案(特别是mshtml.h、 expdispid.h、 mshtmhst.h和mshtmdid.h)。這些檔案指定各種類或者對象支援的接口。不幸的是,可用的Internet Client SDK中的幫助文檔隻有中等程度的輔助,他們解釋了如何使用IWebBrowser2接口的細節,但是沒有提供太多對象和接口之間的關系的資訊。微軟的相關文檔很少,是以探索DHTML對象到底支援那些接口是一個有趣的事情。在微軟知識庫中,你經常會看到一些DHTML對象支撐标準的IConnectionPointContainer、IOleCommandTarget或者IOleContainer接口。

如何...

使用MFC內建浏覽器控件

MFC6開始内建了對浏覽器控件的支援,但是進階的接口,例如IDocHostUIHandler,到7.0版本才支援。使用MFC編寫浏覽器控件應用程式比較簡潔,使用者可能隻需要少量代碼就可以建立出一個具有大部分浏覽器功能的程式。使用MFC的缺點是MFC6的這些類的BUG很多,而且要自定義一些功能的話,需要了解了MFC的控件建立過程。MFC有一個MFCIE示例示範了一個使用CHTMLView建立的簡單的浏覽器程式。Paul DiLascia 在MSDN雜志中他的C++Q&A專欄中示範了在對話框中內建CHtmlView,以及禁用浏覽器控件的右鍵菜單的方法。

自定義浏覽器的行為

如果在應用程式中內建浏覽器控件,可以參考MSDN中WebBrowser Customization這篇文章。一個基于MFC的示例可以在http://www.beginthread.com/Article/Ehsan/Advanced%20CHtmlView%20Hosting找到。

如果編寫BHO,那麼MSDN文章Browser Helper Objects: The Browser the Way You Want It(Dino Esposito著)這篇文章可以用來入門。

在MFC中重載預設的控件站點

為了實作一些進階浏覽器宿主特性,需要在控件站點的同一個對象上實作某些接口。例如用于查詢宿主提供的服務接口的IServiceProvider(常用的服務有用于HTML編輯器的IHTMLEditHost或者IHTMLEditServices和用于自定義IE的安全選項的IInternetHostSecurityManager )和 用于自定義浏覽器的行為和界面的IDocHostUIHandler、IDocHostUIHandler2以及IDocHostShowUI接口。

雖然在MFC7中,可以重載CWnd::CreateControlSite來覆寫控件站點的建立,但是在MFC6中,這個工作要複雜得多。微軟知識庫文章HOWTO: Disable the Default Pop-up Menu for CHtmlView介紹了其中一種方法,但是這種方法看起來不那麼優雅,要在啟動時就覆寫掉預設的OLE容器建立者,而且視窗無法建立其他類型的控件站點。實際上,這不是必要的。分析一下下面的MFC6代碼

void AFX_CDECL AfxEnableControlContainer(COccManager* pOccManager)

{

    if (pOccManager == NULL)

        afxOccManager = _afxOccManager.GetData();

    else

        afxOccManager = pOccManager;

}

你可以看到隻需要在建立前調用AfxEnableControlContainer,傳遞你自己的容器建立者作為參數,建立站點之後再調用一次傳遞NULL作為參數就可以達到覆寫掉預設的OLE容器建立者的目的。這是在你覆寫的CHtmlView::Create中調用的.你必須覆寫這個過程,因為CHtmlView::Create會調用AfxEnableControlContainer(NULL)。

BOOL CHtmlView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName,

DWORD dwStyle, const RECT& rect, CWnd* pParentWnd,

UINT nID, CCreateContext* pContext)

{

……

//假定控件容器已經啟用

AfxEnableControlContainer();

……

常見問題

MFC6的CHtmlView的BUG

在我編寫我的一種基于網頁的遊戲的外挂的第二個版本的時候,我試圖把我的經驗編寫成文章(這篇文章已經發表在我的專欄)。作為我的第一個非測試用途的(其他的浏覽器基本上都過于簡單,而且有這樣那樣的不便或者缺陷,以至于不能友善和穩定地使用)MDI浏覽器,我碰見的第一個問題就是修複MFC的BUG(暫且不談IE本身的BUG)。盡管我查到和發現的BUG不算太多,但是用于解決這些BUG的代碼量也很可觀。我到目前為止發現的大部分BUG的都在這篇文章中的示例代碼裡解決了,盡管文章中關于這些問題的篇幅很少。

在CDHtmlDialog派生的對話框中按Ctrl+N會彈出IE

CDHtmlDialog捕獲了DWebBrowserEvets事件,并将其轉發到虛函數,而沒有捕獲DWebBrowserEvents2;是以在按Ctrl+N觸發DWebBrowserEvents2事件的時候,執行預設操作——打開新的IE視窗。這可能不是你預料之中的行為。解決的方法是自己寫一個EventSink,你可以不必将其轉發到虛函數。參見微軟知識庫文章181845

HOWTO: Create a Sink Interface in MFC-Based COM Client

ActiveX控件中通路文檔對象模型

知識庫文章Q172763 INFO: Accessing the Object Model from Within an ActiveX Control 描述了這個問題的解決方案。可以看到,可以同樣使用IOleClientSite來和IE這個控件容器互動。可以使用IOleClientSite::GetContainer得到網頁所在HTML文檔對象的IOleContainer接口,然後再查詢其他接口,例如IHTMLDocument2來進行對DHTML對象模型的通路。

建立并且操縱IE浏覽器

可以使用CoCreateInstance來建立一個浏覽器對象,使用的CLSID是CLSID_InternetExplorer。建立成功之後,可以查詢浏覽器對象的其他接口,例如IWebBrowser2,IOleObject等等。

分析網頁和自動送出網頁表單

經常被提出的問題,但是網頁千奇百怪,要寫個通用的不容易。一般的應用都是首先把可以參考MSDN中文站上的文章拆取Web頁。

如何調用網頁中Script中的函數?

IHTMLDocument2::scripts屬性表示HTML文檔中所有腳本對象。使用腳本對象的IDispatch接口的::GetIDsOfNames方法可以"發現其中的函數和對象成員,使用IDispatch::Invoke可以通路這些成員。

參考

  • Inside OLE, 第二版, Kraig Brockschmidt著 (微軟出版社)
  • Understanding ActiveX and OLE, David Chappell著 (微軟出版社)
  • Inside COM, by Dale Rogerson著 (微軟出版社)

繼續閱讀