加入高級功能
本節將展示如何加入高級功能到你的應用程序或者ActiveX控件.將展示如何從一個ActiveX控件中訪問Internet Explorer的 IWebBrowser2,以及如何從一個Web頁面的frame中獲得WebBrowser 對象。將會展示實現一些并非容易實現的功能.本節有一定難度.所有代碼采用C++ 和COM實現,你應當有一定堅實的基礎才可以完成本節的理解
從ActiveX 控件中訪問Internet Explorer 的IWebBrowser2
在Activx控件中訪問IWebBrowser2 接口提供了用戶定制瀏覽器的能力,雖然以此作為自己的控件功能不大光明,且你只能夠在VC編寫的activeX控件中訪問。(盡管可以在VB寫的控件中訪問document和window控件,但是你不可以直接訪問WebBrowser 自身) .
存取? IWebBrowser2 接口經過四個步驟:
1.
???
在類文件中包含 ExDisp.h .
2.
??????
調用控件的站點的
IOleClientSite::GetContainer
方法,該方法返回由
Internet Explorer.
實現的
IOleContainer
接口的指針。
3.
??????
如果步驟
2
成功,用
IOleContainer
指針查詢
IServiceProvider
接口。
.
4.
??????
如果步驟
3
成功
,
調用
IServiceProvider
的方法
QueryService
的到
IWebBrowser2
接口。
QueryService
方法攜帶3個參數. 第一個參數指定你想訪問的服務。為得到IWebBrowser2 指針, 需要指定SID_SInternetExplorer 或者SID_SWebBrowserApp 來指定要訪問的服務. (現階段,他們全部定義為IID_IWebBrowserApp.) 第二個參數指定你想接收的接口的ID。此參數你應當指定為IID_IWebBrowser2. 最后,第三個參數你需要指定哪一個變量接收返回的接口指針。
Call any method or property of IWebBrowser2. 當完成后,確信你已經釋放掉你獲取的接口指針.以下為代碼參數.
////////////////////////////////////////////////////////
// Begin Step 1
?
#include <ExDisp.h>
#include <shlguid.h>
?
// End Step 1
////////////////////////////////////////////////////////
?
CSomeClass::SomeMethod(){
?
////////////////////////////////////////////////////////
// Begin Step 2
?
IOleContainer* pContainer;
?
// m_pClientSite is a pointer to IOleClientSite.
// This is the client site for your control.
//
HRESULT hr = m_pClientSite->GetContainer(&pContainer);
if (FAILED(hr))
?? return hr;
?
// End Step 2
////////////////////////////////////////////////////////
?
////////////////////////////////////////////////////////
// Begin Step 3
?
IServiceProvider* pServiceProvider;
?
hr = pContainer->QueryInterface(IID_IServiceProvider,
??????????????????????????????? (void**)&pServiceProvider);
pContainer->Release();
?
if (FAILED(hr))
?? return hr;
?
// End Step 3
////////////////////////////////////////////////////////
?
?
////////////////////////////////////////////////////////
// Begin Step 4
?
IWebBrowser2* pWebBrowser;
?
hr = pServiceProvider->QueryService(SID_SWebBrowserApp,
?????????????????????????? IID_IWebBrowser2,
?????????????????????????? (void**)&pWebBrowser);
pServiceProvider->Release();
?
if (FAILED(hr))
?? return hr;
?
// End Step 4
////////////////////////////////////////////////////////
?
?
////////////////////////////////////////////////////////
// Begin Step 5
?
// Call some IWebBrowser2 methods and/or properties.
?
// End Step 5
////////////////////////////////////////////////////////
}
|
利用一個控件打印web頁
早于5的Internet Explorer,通常用于ActiveX中 訪問IWebBrowser2 接口以提供打印web頁面的功能。盡管ie5允許你直接在web頁的script中使用 window.print, 但是建立一個提供打印web頁的功能的示范還是比較好教您使用IWebBrowser2接口的入門教程.在此之前,必要知道如何使用ATL建立一個activeX控件以實現打印功能. 關于此點,我將認為您已經知道如何使用ATL創建一個控件.
啟動Visual C++, 新建 ATL DLL工程. 你可將工程命名為 AtlPrint. 下一步, 使用Wizard增加一個Lite Control 到你的工程。 你可以命名你的控件為PrintCtl. 為IPrintCtl 接口添加一個方法Print. 之后你將在腳本中使用此方法打印當前Web page.
在實現Print 方法前,首先包含ExDisp.h 和 shlguid.h 頭文件到PrintCtl.cpp 實現文件.
下一步使用之前給出的獲取IWebBrowser2 接口的方法獲得接口并調用ExecWB 以實現打印當前Web頁. 你可以使用ATL 智能接口指針類—CComPtr 以及 CComQIPtr—以實現查詢接口處理引用次數等艱苦的工作.此處為智能指針示勇代碼:
STDMETHODIMP CPrintCtl::Print()
{
?? HRESULT hr = E_FAIL;
?
?? if (m_spClientSite)
?? {
????? CComPtr<IOleContainer> spContainer;
??
??????hr = m_spClientSite->GetContainer(&spContainer);
????? ATLASSERT(SUCCEEDED(hr));
?
????? if (SUCCEEDED(hr))
????? {
???????? CComQIPtr<IServiceProvider, &IID_IServiceProvider>
??????????? spServiceProvider(spContainer);
?
???????? ATLASSERT(spServiceProvider);
?
???????? if (!spServiceProvider)
??????????
?hr = E_FAIL;
???????? else
???????? {
??????????? CComPtr<IWebBrowser2> spWebBrowser;
?
??????????? hr = spServiceProvider->QueryService(SID_SInternetExplorer,
???????????????????????????????????????????????? IID_IWebBrowser2,
????????????????????????????
????????????????????(void**)&spWebBrowser);
??????????? ATLASSERT(SUCCEEDED(hr));
?
??????????? if (SUCCEEDED(hr))
??????????? {????????
???????????????spWebBrowser->ExecWB(OLECMDID_PRINT,
?????????????????????????????????OLECMDEXECOPT_PROMPTUSER,
??????
??????????????????????????NULL, NULL);
??????????? }
???????? }
????? }
?? }
?
?? return hr;
}
|
現在編譯ATL ActiveX control. 為測試打印功能,你必須加入一些script. Listing 6-2 展示了這部分代碼:
Listing 6-2.
PrintCtl.htm
<HTML>
<HEAD>
?? <TITLE>ATL 3.0 test page for object PrintCtl</TITLE>
?
?? <SCRIPT LANGUAGE="VBS">
????? Sub btnPrint_onclick
???????? PrintCtl.Print
????? End Sub
?? </SCRIPT>
</HEAD>
<BODY>
?? <OBJECT ID="PrintCtl"
????? CLASSID="CLSID:320B04E4-B55B-11D2-A9BA-444553540001">
?? </OBJECT>
?? <P>
?? <BUTTON ID="btnPrint">Print Page</BUTTON>
</BODY>
</HTML>
|
當宿主WebBrowser控件的時候存取幀的IWebBrowser2 接口
如果一個Web 頁包含了多幀,每一幀都包含了一個WebBrowser 對象。 當宿主WebBrowser 控件, 你將可被允許從應用程序訪問WebBrowser 以控制幀.此控允許你控制以在幀中導航, 刷新, 以及諸如此類。一旦你擁有了一個幀中的 WebBrowser 對象的IWebrowser2 接口指針, 你可以掉用IWebrowser2 接口的任何方法和屬性。
在VC++中你可以訪問一個幀中的WebBrowser. VB中,你可在幀中存取document , 你可以訪問WebBrowser 但是不可以訪問裝載于WebBrowser窗口中的HTML文檔對象的 IoleContainer 接口. 存取 IOleContainer 要求訪問幀的 WebBrowser 對象. 本節講述VC++d的標準技術細節,即WebBrowser 控件的宿主能訪問在包含的WebBrowser控件裝載的web頁面的幀窗口的WebBrowser對象模型.
下面的代碼展示如何訪問web頁面的每一個幀的WebBrowser對象以刷新每一幀的內容.其中重要的片斷用于用HTML document 對象的IOleContainer::EnumObjects? 方法枚舉頁面中的embeddings(嵌入)對象. 每一個嵌入對象表現為一個控件. 利用IWebBrowser2 接口查詢每一個控件對象,此代碼可檢測到控件是否是一個子幀.如果為獲得 IWebBrowser2 而成功調用QueryInterface, 其結果為幀中的WebBrowser對象的引用. (數據成員m_webBrowser 是CWebBrowser2 類型的—MFC 包裝類)
// Get the IDispatch of the document.
//
LPDISPATCH lpDisp = NULL;
lpDisp = m_webBrowser.GetDocument();
?
if (lpDisp)
{
?? IOleContainer* pContainer;
?
?? // Get the container.
?? //
?? HRESULT hr = lpDisp->QueryInterface(IID_IOleContainer,
?????????????????????????????????????? (void**)&pContainer);
?? lpDisp->Release();
?
?? if (FAILED(hr))
????? return hr;
?
?? // Get an enumerator for the frames.
?? //
?? IEnumUnknown* pEnumerator;
?
?? hr = pContainer->EnumObjects(OLECONTF_EMBEDDINGS, &pEnumerator);
?? pContainer->Release();
?
?? if (FAILED(hr))
????? return hr;
?
?? IUnknown* pUnk;
?? ULONG uFetched;
?
?? // Enumerate and refresh all the frames.
?? //
?? for (UINT i = 0; S_OK == pEnumerator->Next(1, &pUnk, &uFetched); i++)
?? {
????? // QI for IWebBrowser here to see whether we have
??????// an embedded browser.
????? IWebBrowser2* pWebBrowser;
?
????? hr = pUnk->QueryInterface(IID_IWebBrowser2, (void**)&pWebBrowser);
????? pUnk->Release();
?
????? if (SUCCEEDED(hr))
????? {
???????? // Refresh the frame.
???????? pWebBrowser->Refresh();
???????? pWebBrowser->Release();
????? }
?
?}
?
?? pEnumerator->Release();
}
|
請留意在代碼中我們首先通過GetDocument 方法獲得了文檔的IDispatch 對象指針, 該方法是WebBrowser 包裝類的成員指針.然后我們訪問文檔的IOleContainer 接口. IOleContainer 接口提供了能夠枚舉頁面中全部嵌入對象的功能. 然后我們通過調用IOleContainer 的EnumObjects 方法得到枚舉器(EnumObjects).EnumObjects 返回的 IEnumUnknown 接口的指針可以用于枚舉全部嵌入(embeddings)對象. 代碼的下一步, 我們遍歷全部嵌入對象,查詢每一個對象的IWebBrowser2 接口. 如果查詢成功, 我們已經獲得了幀的IWebBrowser2 接口指針.我們可以調用它的任何方法和屬性,在此例中,我們僅僅調用了每一個幀的Refresh.
忠告
ActiveX
控件宿主于一個
html
頁也有類似情。也許,如果你建立一個訪問
ie
的
WebBrowser
控件或者頁面幀中的
WebBrowser
的控件,不要將你的控件標記為腳本安全和初始化安全。
.
調用 查找, 察看源碼, 以及 Internet 選項
瀏覽WebBrowser 控件的方法和屬性, 你可輕易見到控件提供的功能.但是3個可編程由WebBrowser控件提供的項目不容易發覺到:查找對話框, 察看源代碼菜單項,以及Internet選項對話框. 如果你曾經用過ie,你無疑很熟悉他們. 查找對話框, 允許你在WEB頁中查找文本,通過處理 Ctrl-F 或者選擇edit菜單的Find項來調用.
察看源代碼菜單項,允許你顯示WEB頁的html代碼, 可通過選擇View菜單的Source項來選擇或者右擊web頁(在彈處菜單中)選擇察看源代碼. Internet 選項對話框如圖6-23所示, 可通過選擇Tools菜單的Internet Option項來調用.
Figure 6-23.
Internet Options dialog box.
能夠在你的web應用中提供以上功能的確可以帶來對用戶的友善性,但是調用途徑缺不是通過WebBrowser的方法和屬性可以實現. 實際上你應當從調用IOleCommandTarget 的Exec 方法來調用實現于WebBrowser中的以上功能.當調用Exec, 你傳遞名為 CGID_IWebBrowser 的GUID 作為你想調用的命令的ID.盡管ExecWB 方法是IOleCommandTarget::Exec 方法的包裝, 但是你不能夠通過ExecWB 來調用 Find, View Source, 或者 Internet Options 對話框,以為ExecWB 不允許你指定命令組GUID. 那意味著該項技術僅能夠用于VC++—你不能夠直接從VB中調用。.
忠告
本代簡要展示你未收入文檔的命令組
GUID
,那意味著可以將來改編。盡管代碼已經在
Internet Explorer 3.x, 4.x,
和
5
中測試
,
,但是不保證在將來的版本中成功運行
以下步驟實現 Find, View Source, 以及Internet Options 命令:
1.
???
定義WebBrowser 控件的命令組GUID:
DEFINE_GUID(CGID_IWebBrowser,0xED016940L,0xBD5B,0x11cf,0xBA,
???????? 0x4E,0x00,0xC0,0x4F,0xD7,0x08,0x16);
|
2.
??????
定義用于
Find, View Source,
和
Internet Options
的
ID:
????
?????#define HTMLID_FIND 1
???? #define HTMLID_VIEWSOURCE 2
???? #define HTMLID_OPTIONS 3
|
3.
??????
在需要的時候執行
Find, View Source,
和
Internet Options
命令
.
舉例來說,你可創建工具方法接收命令
ID
并調用
IOleCommandTarget::Exec
,
如下片斷所示
. (
注意在
MFC
代碼中,
m_webBrowser
是
WebBrowser
控件的實例
.
同樣
, nCmdID
是定義的
ID)
HRESULT CYourView::ExecCmdTarget(DWORD nCmdID)
{
?? LPDISPATCH lpDispatch = NULL;
?? LPOLECOMMANDTARGET lpOleCommandTarget = NULL;
?? HRESULT hr = E_FAIL;
?
?? // Get the IDispatch of the document.
?? //
?? lpDispatch = m_webBrowser.GetDocument();
?? ASSERT(lpDispatch);
?
?? if (lpDispatch)
?? {
????? // Get a pointer for the IOleCommandTarget interface.
????? //
????? hr = lpDispatch->QueryInterface(IID_IOleCommandTarget,
????????????????????????????????????? (void**)&lpOleCommandTarget);
????? lpDispatch->Release();
?
????? ASSERT(lpOleCommandTarget);
?
????? if (SUCCEEDED(hr))
????? {
???????? // Invoke the given command id
?????????// for the WebBrowser control.
???????? hr = lpOleCommandTarget->Exec
????????????? (pguidCmdGroup, nCmdID, 0, NULL, NULL);
???????? lpOleCommandTarget->Release();
????? }
?? }
?
?? return hr;
}
|
分發WebBrowser 控件
現在你知道了如何利用WebBrowser控件和IE創建專用應用程序, 你大概感興趣知道那些IE組件你需要打包在你的應用程序以便你的應用程序可以在系統商正常工作即使沒有IE5被安裝.無論你宿主 WebBrowser 控件還是自動化Internet Explorer, 你必須至少打包IE最小化安裝的組件. 為理解為什么,請再一次察看圖6-1展示的體系.你可以看到每一個組件是如何的依賴其他組件—而且這幅圖僅僅展示了其表面.它未能展示webbrowser控件和IE多樣特性的組件間的全部特征.因為由如此眾多的組件調用,為了你的應用程序能夠正常工作,你應當確信至少IE最小化安裝于用戶的操作系統.
別擔心,你不需要打包每一個IE的組件.? Internet Explorer 5 安裝程序允許你定制你的安裝以使你打包你僅需的組件.另外, 如果你使用Internet Explorer Administration Kit (可在 http://ieak.microsoft.com/找到) 建立你的安裝程序, 你可以將IE默認安裝,免掉多個確認安裝步驟的對話框.