本文介紹如下內容
1、如何顯示內存中的 HTML 網頁;
2、如何屏蔽掉鼠標右鍵的上下文菜單;
3、如何擴展 HTML 中的腳本(external)對象;
4、如何顯示 HTML 樣式的對話窗;
5、如何執行 HTML 腳本;
一、如何顯示內存中的 HTML 網頁
或者因為網頁保密的考慮;或者因為軟件分發的考慮,有的時候就需要讓 IE 或 IE 浏覽器控件顯示內存或資源中的 HTML 網頁。在 MFC 中,CHtmlView::LoadFromResource() 可以顯示程序資源中的 HTML 內容。我們都知道MFC的 CHtmlView 其實是對 IWebBrowser2 的一個包裝,但是在 IWebBrowser2 中卻沒有類似的方法。那麼它是如何實現的那?步驟如下:
1、首先通過 IWebBrowser2::Navigate2() 顯示一個網頁,其目的是產生有效的對象,從而得到 IHTMLDocument2 接口;
2、IWebBrowser2::get_Document() 得到 IHTMLDocument2 接口指針;
3、IHTMLDocument2::QueryInterface() 得到 IPersistStreamInit 接口指針;
4、IPersistStreamInit::InitNew() 初始化接口對象;
5、IPersistStreamInit::Load() 裝載內存中的 HTML 數據流(IStream *);
內存指針轉換為流的方法是:
I、GlobalAlloc() 申請內存;
II、復制 HTML 字符串內容到上述的內存中;
III、CreateStreamFromHGlobal() 轉換內存為 IStream 指針;
原理性代碼如下:
// 顯示一個空白網頁
m_ie.Navigate2( &CComVariant(_T("about:blank")),NULL,NULL,NULL,NULL);
// 得到 IHTMLDocument2 指針
CComPtr< IDispatch > spDoc(m_ie.GetDocument());
// 得到 IPersistStreamInit 指針
CComQIPtr< IPersistStreamInit, &IID_IPersistStreamInit > spPSI( spDoc );
// 申請內存,復制 HTML 字符串
LPTSTR lpMem = (LPTSTR)::GlobalAlloc( GPTR, ::lstrlen( lpHtml )+1 );
lstrcpy( lpMem, "xxx xxx" );
// 轉換內存為流對象指針
CComPtr< IStream > spStream;
CreateStreamOnHGlobal( lpMem, TRUE, &spStream );
// 初始化後,裝載顯示
spPSI->InitNew();
spPSI->Load( spStream );
圖一、IE控件顯示內存中的 HTML 文件
圖二、HTML對話窗
IE 所能支持的數據傳輸協議,除了大家所熟悉的 http、ftp、file......還有一個協議是 res ,它表示浏覽顯示文件中的 HTML 資源。你可以在 IE 的地址欄上直接輸入這樣格式的 URL:"res://文件名/資源名"。
把 HTML 文件加入到程序資源的方法比較簡單,在資源卡片中,鼠標右鍵彈出菜單,執行 Import...(引入),選擇指定的 HTML 文件,然後給一個資源名稱即可。(在這裡,最方便的資源名稱用字符串比較好,如果使用整數ID,那麼將來在使用的時候是這樣的格式:res://文件名/#101,這裡假設 101 是資源的ID號。真麻煩!我不太喜歡這樣的方式。)對於圖片文件等其它的附件,則需要手工編輯資源 RC 文件(用 IDE 環境引入,它會試圖用文本方式打開一個2進制文件,多數情況下會“死機”)。下圖是事例程序引入資源後的樣式:
圖三、HTML 資源的引入
手工編輯 RC 文件的部分是:
......
/////////////////////////////////////////////////////////////////////////////
//
// HTML
//
HTML_TOWORD HTML DISCARDABLE "res\\ToWord.htm" // 這兩個是HTML文件,可以引入
HTML_DLG HTML DISCARDABLE "res\\html_dlg.htm"
~SEND_R1_C1.GIF HTML DISCARDABLE "res\\~Send_r1_c1.gif" // 下面的是GIF文件,需要手工加入
~SEND_R1_C2.GIF HTML DISCARDABLE "res\\~Send_r1_c2.gif"
LOGO.GIF HTML DISCARDABLE "res\\Logo.gif"
SEND_R1_C1.GIF HTML DISCARDABLE "res\\Send_r1_c1.gif"
SEND_R1_C2.GIF HTML DISCARDABLE "res\\Send_r1_c2.gif"
SPACER.GIF HTML DISCARDABLE "res\\spacer.gif"
#endif // Chinese (P.R.C.) resources
/////////////////////////////////////////////////////////////////////////////
......
二、屏蔽 IE 控件的上下文菜單
屏蔽或自定義 IE 控件的上下文菜單,其實就是需要實現 IDocHostUIHandler 接口中的 ShowContextMenu 方法。如果使用 ATL 編寫程序,我認為實現是比較簡單的(也許是我使用 ATL 寫 COM 比 MFC 熟悉一些的因素吧)。事例程序由於用 MFC 書寫,真是搞的我頭暈眼花,翻箱倒櫃終於找到了微軟書寫的演示代碼,於是我就直接復制過來使用了。(換句話說,讀者在閱讀這部分代碼的時候,如果有問題可不要問我。你直接打電話去咨詢 Microsoft 哈。)
三、擴展 HTML 腳本中的 external 對象
從 CCmdTarget 派生一個自動化對象(新建C++類的時候,注意別忘了選擇 Automation)。在這個類裡,你可以使用 ClassWizard 的 Automation 卡片,添加自定義的方法和屬性。而在 HTML 的腳本程序中,就可以使用 window.external 進行調用了。用這個方法,實現了對 HTML 腳本功能的擴充。在 HTML 腳本和自動化對象之間要建立起關系,則需要實現 IDocHostUIHandler::GetExternal() 方法。
四、顯示 HTML 樣式的對話窗
這節內容是本文的重點。
用戶的界面設計經歷了若干個發展階段。最早的程序設計,可以說沒有用戶界面;然後發展出一些簡單的與用戶交互的界面(控制台界面,全屏文本界面);再然後發展出了圖形界面。其實我們現在的商業程序設計中,界面的處理代碼占用了很大的篇幅。為了使界面的處理變得簡單、通用、易修改維護,人們制作了很多的界面程序庫。說實在話,大多數的界面程序庫由於封裝的不好,一是不靈活,二是經常需要修改它內部的 BUG,重用的效果並不理想。通用的換膚軟件也只能實現對標准的窗口類進行皮膚美化,對自定義的窗口類還是需要自己寫鉤子。咳......
現在,我們已經有一個非常好的方法進行界面設計了,那就是使用 HTML(使用 Visual Studio.net 的程序員,一定對 .net 的界面很喜歡吧?!.net 開發環境,無處不在使用 HTML 方式的界面)。即使是一個完全地道的本地軟件(非B/S軟件),也可以使用本地 ASP 方式,HTA 方式進行程序設計。軟件用戶非常喜歡這樣的程序,因為他早就熟悉並掌握了浏覽器的操作,另外,對於程序員來說,也非常喜歡這種方式,因為不會再為換膚,不同用戶不同的界面特化而傷腦筋了。
微軟將要在下一代的程序設計中使用 XML 來描述用戶界面。這種設計方式將會解放你、我這樣的程序員,把咱們的工作量全部都轉化到美工師那裡去了:) 借 vckbase 的平台,現在呼吁大家,盡快學習和掌握 HTML、XML 的設計和腳本編程,並能熟練地對它們與 C++ 對象的交互進行編程。可以預計在未來的兩三年內,擁有這樣水平的程序員,一定會開始吃香饽饽了,嘿嘿......
下面,就如何顯示一個 HTML 對話窗,開始我們未來軟件方式的編程吧。
我們要調用 MSHTML.DLL 中的一個函數 ShowHTMLDialog(Ex) 來完成 HTML 對話窗的顯示和數據交互。這個函數的聲明是:
HRESULT ShowHTMLDialogEx(
HWND hwndParent,
IMoniker *pMk,
DWORD dwDialogFlags,
VARIANT *pvarArgIn,
WCHAR *pchOptions,
VARIANT *pvarArgOut
);
hwndParent 對話窗的父窗口句柄 這個太簡單了,不多羅嗦。 pMk URL的命名接口指針 表示在對話窗中顯示哪個URL的頁面。但它不是以簡單的URL字符串方式提供的。它使用了moniker(命名)接口指針。 根據URL得到IMoniker *很簡單,調用CreateURLMoniker()。唯一要注意的是,這個函數需要連接 Urlmon.lib 庫。 dwDialogFlags 對話窗類型 可以組合 HTMLDLG_NOUI、HTMLDLG_MODAL、HTMLDLG_MODELESS、HTMLDLG_PRINT_TEMPLATE、HTMLDLG_VERIFY。
示例程序使用的是模式對話窗。HTMLDLG_NOUI 在下一節中介紹。
pvarArgIn 對話窗的輸入參數 一個傳入對話窗的VARIANT變量,對話窗腳本中使用 window.dialogArguments 可以取得。 pchOptions 對話窗樣式 用字符串表示的對話窗樣式。參考 IHTMLWindow2::showModalDialog()函數。比如:"dialogHeight:100px dialogWidth:200px"表示200點寬,100點高。如果你不想在程序中指定,也可以在HTML中<html style=....>描述。
pvarArgOut 對話窗輸出參數 對話窗的VARIANT返回值,對話窗腳本中使用 window.returnValue 可以賦值。這個函數在 vc.net 的頭文件上有完整的聲明,如果你使用 vc 6.0 的話,那麼函數聲明、函數指針定義和一些常量,你需要手工添加。還好,本文的示例程序是在 VC6 下編寫和調試的,下載代碼後,請仔細閱讀源文件和注釋就可以了。
五、執行 HTML 腳本
關於調用腳本的方法,我在 vckbase 發表了好幾篇文章(鬼知道我為什麼對腳本這麼有興趣)。ShowHTMLDialogEx()函數中,如果類型參數給出 HTMLDLG_NOUI,則表示並不真正顯示一個對話窗,而是加載指定的 HTML 並執行其中的腳本。示例程序的該腳本中,執行一連串的動作,完成了把上一個 HTML 對話窗中用戶輸入的文本,發送到 MS WORD 中去。以此上下串聯起來,演示了本文章中所討論的所有功能。下面我把腳本和注釋給朋友們展現一下:
On Error Resume Next
Set wordapp=CreateObject("Word.Application") ''''運行 MS WORD
if err<>0 then
MsgBox("沒有安裝 MS WORD")
else
wordapp.visible = true ''''顯示WORD界面
wordapp.Documents.Add "",false, 0 ''''新增一個空文檔
wordapp.Selection.TypeText window.dialogArguments ''''鍵入傳遞進來的文本
end if
window.close '''' 關閉
六、結束語
好好學習、天天向上。做合格的社會主義計算機軟件事業接班人。嘿嘿......
本文配套源碼