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