關(guān)鍵字 WebBrowser,,IDocHostUIHandler,GetExternal
1 引言
在用Delphi,、Visual Basic等可視化快速開發(fā)工具編寫Windows應(yīng)用程序時,,常會遇到這樣幾個問題:
1) 希望程序界面美觀。在Delphi中,,開發(fā)人員通常使用各種控件來實現(xiàn)界面的風(fēng)格化,,但缺點是造成應(yīng)用程序體積較大,且在升級時常會被控件版本與Delphi版本不兼容帶來的問題所困擾,。
2) 希望應(yīng)用程序在功能不變的情況下具有不同的界面風(fēng)格,。這常常通過換"皮膚"的技術(shù)來實現(xiàn),,但一般實現(xiàn)"換膚"功能的控件體積都較大,且界面反應(yīng)速度比較慢,,而且 "皮膚"的制作比較麻煩,。
3) 程序界面的維護(hù)困難。為了使界面與代碼實現(xiàn)相分離而獲得"換膚"等靈活性,,通常要用到一些設(shè)計模式的技術(shù),,這對于不熟悉設(shè)計模式的開發(fā)人員來說比較困難。
微軟公司預(yù)計將于2006年發(fā)布下一代操作系統(tǒng)(開發(fā)代號為Longhorn)中,,應(yīng)用程序的結(jié)構(gòu)及部署將有重大變革,,其中一項就是應(yīng)用程序的界面完全以XML的一個擴(kuò)展集XAML語言來描述,以便達(dá)到界面的高度可定制性,。這無疑能夠方便地解決上述幾個問題,。問題是在目前來說有沒有類似的方法呢?答案就是使用瀏覽器控件,。
微軟公司的網(wǎng)頁瀏覽器Internet Explorer的核心被設(shè)計為可以嵌入到應(yīng)用程序中重用的ActiveX組件,,它有極強的可編程能力和與容器交互的能力,使得開發(fā)人員能夠快速地開發(fā)出功能強勁的應(yīng)用程序,。從下面的Internet Explorer的架構(gòu)圖可以看到,,我們平常運行的iexplorer.exe其實只是一個外殼程序,,真正的瀏覽網(wǎng)頁,、記錄歷史等工作是由嵌入其窗口的封裝在shdocvw.dll中的WebBrowser Control來完成的。
Shdocvw.dll的功能則是調(diào)用mshtml.dll來解析網(wǎng)頁,,以及在它的窗口中嵌入其它活動文檔組件(如Microsoft Office,、Adobe Acrobat等應(yīng)用程序的文檔都可以嵌入到瀏覽器窗口中查看)。而mshtml.dll一方面處理HTML解析以及作為腳本引擎,、java虛擬機,、ActiveX控件、插件的宿主,,另一方面,,它實現(xiàn)了活動文檔服務(wù)器接口,允許應(yīng)用程序以標(biāo)準(zhǔn)的COM接口來把它嵌入到程序中并通過它暴露的接口來訪問其中的網(wǎng)頁及網(wǎng)頁元素,。
通過shdocvw.dll提供的豐富接口,,網(wǎng)頁中的元素可以訪問外殼應(yīng)用程序提供的屬性和方法(如window.external.AddFavorite(location.href, document.title)則是調(diào)用IE的AddFavorite方法把當(dāng)前頁添加到收藏夾),而通過mshtml.dll提供的接口,,外殼應(yīng)用程序則反過來可以訪問網(wǎng)頁中元素的屬性,、方法、行為,、事件等等,。解決文章開頭提出的幾個問題的方法就是基于shdocvw.dll和mshtml.dll實現(xiàn)的,。一些著名軟件如:Microsoft Money、Microsoft Visual Studio .NET,、Macromedia Dreamweaver MX 2004等都運用了這種技術(shù),。
2 原理
1) 程序的界面完全由制作網(wǎng)頁來完成。網(wǎng)頁在文字,、圖像,、聲音等方面具有強大的表現(xiàn)能力,運用所見即所得的網(wǎng)頁制作工具可以輕松制作出圖文并茂的網(wǎng)頁,。以網(wǎng)頁作為程序的界面,,其效果勝過任何界面控件。
2) "換膚"功能容易實現(xiàn),。只需制作不同風(fēng)格的網(wǎng)頁,,即可輕松實現(xiàn)樣式各異的程序界面。
3) 程序的功能在應(yīng)用程序內(nèi)部編寫代碼來實現(xiàn),,并通過一個自動化接口提供給網(wǎng)頁中的元素調(diào)用,。這就實現(xiàn)了程序界面和代碼的分離,網(wǎng)頁布局及風(fēng)格的改變不會影響到程序的實現(xiàn),。
3 從網(wǎng)頁調(diào)用外殼程序的屬性和方法
3.1 GetExternal接口方法
WebBrowser Control提供的接口使得外殼應(yīng)用程序可以用自己的對象,、方法和屬性等來擴(kuò)展IE的對象模型(DOM),以達(dá)到個性化定制的目的,。在網(wǎng)頁中訪問外殼應(yīng)用程序的擴(kuò)展則通過文檔的"external"對象來實現(xiàn),,如外殼程序提供了名為AddFavorite的方法,網(wǎng)頁中就通過window.external.AddFavorite()來調(diào)用,。實現(xiàn)這一功能的核心是IDocHostUIHandler接口的GetExternal方法:
HRESULT GetExternal(IDispatch **ppDispatch);
在自定義的WebBrowser Control中實現(xiàn)IDocHostUIHandler接口,,當(dāng)網(wǎng)頁元素通過"external"對象訪問外殼擴(kuò)展的屬性和方法時,GetExternal方法就會被調(diào)用,,在此方法的中將實現(xiàn)外殼程序?qū)傩院头椒ǖ淖詣踊涌趥鬟f給ppDispatch即可,。自定義的WebBrowser Control示例代碼如下,在其中將GetExternal包裝為OnGetExternal事件供外部程序調(diào)用,。IDocHostUIHandler接口有15個方法,,此處我們只關(guān)心GetExternal方法,故略去其余14個(省略號處為略去的代碼),。
unit ZoCWebBrowser;
interface
uses
Variants,IEConst, Windows, SysUtils, Classes, SHDocVw, ActiveX, shlObj, MSHTML, comobj;
type
……
TGetExternalEvent = function(out ppDispatch: IDispatch): HRESULT of object; //定義OnGetExternal事件類型
TZoCWebBrowser = class(TWebBrowser, IDocHostUIHandler)
private
……
FOnGetExternal: TGetExternalEvent;
protected
……
function GetExternal(out ppDispatch: IDispatch): HRESULT; stdcall;
published
……
property OnGetExternal: TGetExternalEvent read FOnGetExternal write FOnGetExternal;
end;
……
implementation
……
function TZoCWebBrowser.GetExternal(out ppDispatch: IDispatch): HRESULT;
begin
if Assigned(FOnGetExternal) then
Result := FOnGetExternal(ppDispatch)
else
Result := S_FALSE;
end;
initialization
OleInitialize(nil);
finalization
try
OleUninitialize;
except
end;
end.
3.2 實現(xiàn)外殼程序擴(kuò)展自動化接口
在Delphi的"New Items"對話框中,,切換到"ActiveX"頁,選擇"Automation Object",,新建一個自動化對象,,并在"CoClass Name"一欄中填入接口名"MyExternal","Instancing"選擇為"Internal",,表示該對象只能在程序內(nèi)部被創(chuàng)建,,外部程序不能直接創(chuàng)建,。點擊"OK"按鈕后在Type Library編輯對話框中為IMyExternal接口添加兩個方法ShowAboutBox和SwitchUI,此時代碼大致如下所示:
unit MyExternalImpl;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
ComObj, ActiveX, Project1_TLB, StdVcl;
type
TMyExternal = class(TAutoObject, IMyExternal)
protected
procedure ShowAboutBox; safecall;
procedure SwitchUI; safecall;
end;
implementation
uses ComServ;
procedure TMyExternal.ShowAboutBox;
begin
MessageBox(MainForm.Handle, ‘GetExternal Demo‘, ‘ZoCWebBrowser‘, MB_OK or MB_ICONASTERISK);
end;
procedure TMyExternal.SwitchUI;
begin
ShowSwitchUIForm; //顯示切換程序界面對話框
end;
initialization
TAutoObjectFactory.Create(ComServer, TMyExternal,
Class_MyExternal, ciInternal, tmApartment);
end.
3.3 從網(wǎng)頁中調(diào)用外殼程序接口
在程序主窗口中放置一個自定義的WebBrowser Control,,命名為ZoCWebBrowser,,編寫它的OnGetExternal事件(由網(wǎng)頁中的window.external調(diào)用觸發(fā)),代碼如下:
function TMainForm.ZoCWebBrowserGetExternal(
out ppDispatch: IDispatch): HRESULT;
var
MyExternal: TMyExternal;
begin
MyExternal:= TMyExternal.Create; //創(chuàng)建實現(xiàn)自動化接口的對象
ppDispatch :=MyExternal; //將對象接口傳遞給WebBrowser Control
//這樣當(dāng)"external"對象被調(diào)用時,,真正被調(diào)用的是我們實現(xiàn)的TMyExternal對象
Result :=S_OK;
end;
假設(shè)我們制作了兩個風(fēng)格迥異的的網(wǎng)頁Style1.html和Style2.html作為程序界面,,這兩個網(wǎng)頁中都有兩個按鈕(也可以是其它網(wǎng)頁元素),其HTML代碼示例如下:
<BUTTON onclick=" window.external.ShowAboutBox">關(guān)于</BUTTON>
<BUTTON onclick=" window.external.SwitchUI">切換界面</BUTTON>
在程序開始運行時讓W(xué)ebBrowser Control布滿整個Form,,且顯示Style1.html頁面,,則當(dāng)點擊"關(guān)于"按鈕時程序?qū)@示一個關(guān)于信息對話框,而點擊"切換界面"按鈕時將顯示切換界面的對話框,,在其中選擇Style2.html并讓W(xué)ebBrowser Control顯示它即可獲得風(fēng)格完全不同的界面,,但在功能上與Style1.html完全一樣。
4 總結(jié)
從上面的例子可以看到,,我們以及其簡單的方式實現(xiàn)了程序界面與實現(xiàn)的分離,,這有利于程序的維護(hù)和擴(kuò)展。傳統(tǒng)方式下,,界面設(shè)計和編碼通常都由程序員來完成,,一來造成程序員負(fù)擔(dān)較重,二來難以保證界面質(zhì)量,。實用上述方法,,程序界面可以由專業(yè)美工人員來設(shè)計,他可以在完全不知道程序如何實現(xiàn)的情況下設(shè)計出完整的界面,,而程序員只需專注于代碼的編寫,,并將必要的方法和屬性通過一個自動化接口暴露出來。合并的時候,,在網(wǎng)頁中合適的位置放入所需的按鈕或其它網(wǎng)頁元素,并賦予簡單的腳本調(diào)用即可,。
(以上代碼均在WindowsXP+Delphi 7環(huán)境下調(diào)試通過)
5 參考文獻(xiàn)
《MSDN Library - July 2003》