加入高级功能本节将展示如何加入高级功能到你的应用程序或者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默认安装,免掉多个确认安装步骤的对话框.