IE里的探索之定制瀏覽器好助手(下) (作者:青蘋(píng)果工作室編譯 2001年02月08日 14:00)
訪問(wèn)文檔對(duì)象 現(xiàn)在 BHO 引用了 Internet Explorer 的 WebBrowser 控件并已經(jīng)連接到瀏覽器 以接收它產(chǎn)生的事件。在 Web 頁(yè)面被完全下載并被正確地初始化之后,現(xiàn)在終于可以通過(guò) DHTML 文檔對(duì)象模型訪問(wèn)它了。WebBrowser 的 Document 屬性返回一個(gè)指向文檔對(duì)象的 IDispatch 接口的指針:
CComPtr<IDispatch> pDisp;
HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);
get_Document() 方法提供的只是一個(gè)指向接口的指針。我們需要確定在 IDispatch 指針后面確實(shí)是一個(gè) HTML 文檔對(duì)象。如果使用 Visual Basic,以下是等價(jià)的代碼:
Dim doc As Object
Set doc = WebBrowser1.Document
If TypeName(doc)="HTMLDocument" Then
' Get the document content and display
Else
' Disable the display dialog
End If
現(xiàn)在我們需要判斷 get_Document() 返回的 IDispatch 指針的實(shí)質(zhì)。Internet Explorer 不僅是一個(gè) HTML 瀏覽器,還能處理任何 ActiveX 文檔 ;即任何有作為 ActiveX 文檔服務(wù)程序的應(yīng)用程序支持的文檔。這樣一來(lái),就不能保證查看的文檔的確是一個(gè) HTML 頁(yè)面。
有一個(gè)解決辦法就是查看 URL 并檢查 URL 的擴(kuò)展名。但該如何處理 Active Server Pages (ASP) 或一個(gè)暗含指向 HTML 頁(yè)面的 URL?如果你使用了像 about 或 res 這樣的定制協(xié)議又該如何?
我們決定采取另一種方式,它和上面的 Visual Basic 代碼性質(zhì)相同。這種想法就是,如果 IDispatch 指針確實(shí)指向一個(gè) HTML 文檔,對(duì) IHTMLDocument2 接口的訪問(wèn)就能成功地返回。IHTMLDocument2 是綜合了 DHTML 對(duì)象模型為 HTML 頁(yè)面實(shí)現(xiàn)的所用功能的接口。以下代碼片斷說(shuō)明如何進(jìn)行這樣的判斷:
CComPtr<IDispatch> pDisp;
HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);
CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spHTML;
spHTML = pDisp;
if (spHTML) {
// 取得文檔的內(nèi)容并顯示它
}
else {
// 禁止代碼窗口控件
}
如果訪問(wèn) IHTMLDocument2 接口失敗,spHTML 指針為 NULL。否則,我們就可以正常訪問(wèn) DHTML 對(duì)象模型的方法和屬性了。
現(xiàn)在的問(wèn)題是如何獲得已顯示的頁(yè)面的源代碼。幸好,基本的 DHTML 知識(shí)就足以做到這一點(diǎn)。由于 HTML 頁(yè)面將它所有的內(nèi)容包含在 <BODY> 標(biāo)記中,DHTML 對(duì)象模型要求你首先獲得指向 Body 對(duì)象的指針:
CComPtr<IHTMLElement> m_pBody;
hr = spHTML->get_body(&m_pBody);
奇特的是,DHTML 對(duì)象模型不讓你知道在 <BODY> 之前的標(biāo)記,例如 <HEAD> 的原始內(nèi)容。這些內(nèi)容已經(jīng)被處理并被保存到一系列屬性中了,但你依然不能得到一個(gè)最初的 HTML 文件的原始內(nèi)容。然而,現(xiàn)在 body 能告訴我們的就足夠了。我們需要將 outerHTML 屬性的內(nèi)容讀取到一個(gè) BSTR 變量里以獲得包含在 <BODY> 和 </BODY> 之間的 HTML 代碼。
BSTR bstrHTMLText;
hr = m_pBody->get_outerHTML(&bstrHTMLText);
現(xiàn)在,在代碼窗口中顯示文本的工作就是創(chuàng)建窗口、將字符串從 Unicode 轉(zhuǎn)換為 ANSI,并如圖 3 中所示設(shè)置編輯框。以下是完成這些工作的全部代碼:
HRESULT CViewSource::GetDocumentContent()
{
USES_CONVERSION;
// 獲得 WebBrowser 文檔對(duì)象
CComPtr<IDispatch> pDisp;
HRESULT hr = m_spWebBrowser2->get_Document(&pDisp);
if (FAILED(hr))
return hr;
// 驗(yàn)證我們得到了一個(gè)指向 IHTMLDocument2 接口的指針
// 我們查詢 IHTMLDocument2 接口 (通過(guò)靈巧指針)
CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spHTML;
spHTML = pDisp;
// 獲得文檔的源代碼
if (spHTML)
{
// 獲得 BODY 對(duì)象
hr = spHTML->get_body(&m_pBody);
if (FAILED(hr))
return hr;
// 獲得 HTML 文本
BSTR bstrHTMLText;
hr = m_pBody->get_outerHTML(&bstrHTMLText);
if (FAILED(hr))
return hr;
// 將文本從 Unicode 轉(zhuǎn)換為 ANSI
LPTSTR psz = new TCHAR[SysStringLen(bstrHTMLText)];
lstrcpy(psz, OLE2T(bstrHTMLText));
// 允許修改文本
HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT);
EnableWindow(hwnd, true);
hwnd = m_dlgCode.GetDlgItem(IDC_APPLY);
EnableWindow(hwnd, true);
// 設(shè)置代碼窗口的文本
m_dlgCode.SetDlgItemText(IDC_TEXT, psz);
delete [] psz;
}
else // 文檔不是 HTML 頁(yè)面
{
m_dlgCode.SetDlgItemText(IDC_TEXT, "");
HWND hwnd = m_dlgCode.GetDlgItem(IDC_TEXT);
EnableWindow(hwnd, false);
hwnd = m_dlgCode.GetDlgItem(IDC_APPLY);
EnableWindow(hwnd, false);
}
return S_OK;
}
由于我們運(yùn)行這段代碼以響應(yīng) DocumentComplete 通知,每個(gè)新頁(yè)面都會(huì)迅速地自動(dòng)處理。DHTML 對(duì)象模型允許你修改顯現(xiàn)的頁(yè)面的結(jié)構(gòu),但在你按 F5 鍵或?yàn)g覽器的 Refresh 按鈕刷新視圖后,所有的修改會(huì)立即丟失。通過(guò)對(duì) DownloadComplete 事件進(jìn)行處理你能同時(shí)刷新代碼窗口。(注意 DownloadComplete 事件比 DocumentComplete 事件先到達(dá)) 這時(shí),你應(yīng)該忽略第一次下載頁(yè)面時(shí)產(chǎn)生的 DownloadComplete 而只考慮刷新時(shí)產(chǎn)生的事件。一個(gè)簡(jiǎn)單的布爾成員例如 m_bDocumentCompleted 可以用來(lái)區(qū)分這兩種情況。
管理代碼窗口 用以顯示當(dāng)前頁(yè)面的 HTML 源代碼的代碼窗口是 ATL 的另一個(gè)基本元素,一個(gè)可以在 ATL 對(duì)象向?qū)У?Miscellaneous 頁(yè)里找到的對(duì)話框窗口。我們重置這個(gè)窗口的尺寸以響應(yīng) WM_INITDIALOG 消息,并使此窗口占據(jù)桌面工作區(qū),即屏幕的可用部分減掉任務(wù)欄可能占據(jù)位置最下面的部分。
瀏覽器啟動(dòng)時(shí)此窗口可能出現(xiàn)也可能不出現(xiàn)。默認(rèn)情況下它是出現(xiàn)的,但可以通過(guò)清除復(fù)選框 Show window at startup 禁止。如果你愿意也可以關(guān)掉它。隨后,可以在任何時(shí)候按 F12 鍵將其召回。F12 由我們?cè)?SetSite() 中安裝的鍵盤(pán)掛鉤捕獲。
啟動(dòng)設(shè)置完全按照 Microsoft 指示保存在注冊(cè)表里。讀寫(xiě)注冊(cè)表時(shí)我們沒(méi)有使用 Win32 函數(shù),而是使用了新的 Shell Lightweight API (shlwapi.dll),這樣 可以避免打開(kāi)和關(guān)閉相應(yīng)的注冊(cè)表項(xiàng)的麻煩:
DWORD dwType, dwVal;
DWORD dwSize = sizeof(DWORD);
SHGetValue(HKEY_CURRENT_USER, _T("Software\\MSDN\\BHO"),
_T("ShowWindowAtStartup"), &dwType, &dwVal, &dwSize);
這個(gè) DLL 是在 Internet Explorer 4.0 和 Active Desktop 中引入的,從 Windows 98 開(kāi)始成為標(biāo)準(zhǔn)的系統(tǒng)組件。這些函數(shù)比相應(yīng)的 Win32 函數(shù)更直接,適合只進(jìn)行一次讀寫(xiě)時(shí)使用。
注冊(cè)助手對(duì)象 BHO 是 COM 服務(wù)程序,應(yīng)該同時(shí)以 COM 服務(wù)程序和 BHO 注冊(cè)。ATL 模板為你提供了完成第一項(xiàng)注冊(cè)的注冊(cè)腳本代碼 (RGS) 。以下是完成 BHO 注冊(cè)的 RGS 代碼。(CLSID 是從例程序中得到的。)
HKLM {
SOFTWARE {
Microsoft {
Windows {
CurrentVersion {
Explorer {
'Browser Helper Objects' {
ForceRemove {1E1B2879-88FF-11D2-8D96-D7ACAC95951F}
}}}}}}}
注意 ForceRemove 子句使鍵在對(duì)象取消注冊(cè)時(shí)被刪除。
在 Browser Helper Objects 鍵下是所有安裝的助手對(duì)象。瀏覽器從不將這些放入緩存,所以安裝并測(cè)試 BHO 是一個(gè)很快的過(guò)程。
BHO小結(jié) 本文中,我們介紹了瀏覽器助手對(duì)象,一種相對(duì)來(lái)說(shuō)比較新的、在瀏覽器的地址空間內(nèi)直接引入你的代碼的有效方式。你所要做的就是編寫(xiě)一個(gè) COM 服務(wù)程序以支持 IObjectWithSite 接口。這里,你的模塊從所有預(yù)定目的來(lái)看都是瀏覽器機(jī)構(gòu)中的一個(gè)組件。本文中我們建立的例程序還涉及到如 COM 事件、動(dòng)態(tài) HTML 對(duì)象模型以及 WebBrowser 編程接口等內(nèi)容。我們認(rèn)為它演示了 BHO 的功能,同時(shí)提供了一個(gè)創(chuàng)建你自己對(duì)象的實(shí)用平臺(tái)。如果你需要知道瀏覽器正在顯示什么,你一定需要熟悉事件并進(jìn)一步了解 WebBrowser。現(xiàn)在你知道:預(yù)先警告是為了早做準(zhǔn)備。作為結(jié)語(yǔ),我們提醒你 BHO 對(duì) Windows Explorer 非常有用,而且,通過(guò) WebBrowser,它能由你的代碼驅(qū)動(dòng)。
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!