自定义浏览器(一)
自定义浏览器
转自http://blog.csdn.net/jiangsheng/archive/2004/11/07/170742.aspx
本教程提供了自定义浏览器控件的行为和外观的一些方法。你将看到高级的宿主接口,IDocHostUIHandler, IDocHostUIHandler2, IDocHostShowUI, 和ICustomDoc。本文也讨论其他自定义方法,例如在宿主的IDispatch实现中处理DISPID_AMBIENT_DLCONTROL来进行下载控制;以及使用IHostDialogHelper。
本文分为如下章节
- 前提和需求
- 介绍
- 浏览器自定义架构
- IDocHostUIHandler
- IDocHostUIHandler2
- GetOptionKeyPath 和 GetOverrideKeyPath的比较
- 控制导航
- IDocHostShowUI
- 控制下载和执行
- IHostDialogHelper
- 控制新的窗口
- 显示证书对话框(New!)
- 信息栏(New!)
- 结论
前提和需求
为了理解和使用本教程,你需要
- 对C++和COM的深入了解
- 熟悉活动模板库 (ATL)
- 安装了Microsoft(R) Internet Explorer (IE)6 或更高版本
- 开发环境具有用于IE6或更高版本的头文件和库文件;特别是Mshtmhst.h.(译者注:可以在http://www.microsoft.com/msdownload/platformsdk/sdkupdate/ 这里下载最新的Internet Development SDK)
许多自定义特性是在IE5或者5.5版本就可以使用的,但是有几个特性需要IE6。(New!) 一些特性需要IE6的Windows XP SP2版本。使用某个特性之前,应该检查参考文档以获得版本信息。
介绍
集成浏览器控件是快速软件开发的强有力的工具。通过成为浏览器的宿主,你可以利用便于使用的Dynamic HTML (DHTML), HTML, 和Extensible Markup Language (XML)来显示信息和开发一个用户界面。但是,浏览器控件的行为可能不确切符合你的需求。例如,默认的状态允许用户通过快捷菜单的查看源代码选项查看一个显示的页面的源代码,你可能需要禁用或者干脆去掉这个选项。你可能更进一步,需要用你自己的快捷菜单替换默认的快捷菜单。
在刚刚提到的自定义特性之外,高级宿主特性允许
- 在显示的页面上的按钮和其他控件可以调用你的应用程序的内建方法,有效地扩展DHTML对象模型(DOM)。
- 改变拖放的行为
- 限制浏览器的导航,例如,限制于指定的页面/域,或者站点
- 捕获用户键入,并且在需要的时候处理。比如说,你可能需要捕获CTRL+O来阻止用户在新的IE中打开网页而不是使用你的程序打开,
- 改变默认字体和显示设置
- 控制下载内容,以及当下载完成之后浏览器的处理。例如,你可能禁用视频的播放,脚本的执行,点击链接时打开新的窗口,或者Microsoft(R) ActiveX 控件的下载和执行。
- 限制查看源代码
- 捕获搜索
- 捕获导航错误
- 替代/修改快捷菜单或者禁用,替代,自定义,或者添加快捷菜单项
- 为你的应用程序改变注册表设定
- 控制和修改浏览器控件显示的消息框
- 控制新窗口的创建方式
在下列节中,我们将会看到多数,但是不是全部的这些可能性而且讨论该如何实现他们。
浏览器自定义架构
介绍 IDocHostUIHandler , IDocHostUIHander2 , IDocHostShowUI 和 ICustomDoc
下面三个接口是浏览器控件用户界面的自定义的核心:IDocHostUIHandler ,IDocHostUIHandler2 和 IDocHostShowUI。当你修改浏览器控件的时候 , 这些是你在你的应用程序中实现的接口。也有一些服务接口。 ICustomDoc 被MSHTML实现并且提供一个方法在某些情况下能够自定义浏览器控件。IHostDialogHelper提供一个方法打开可信的对话框,没有像IE对话框那样为他们(译者注:在标题栏上)作标记。
除了使用这些接口,你还可以做其他两件事。其一,你能通过在IDispatch实现中拦截环境特性的变化来控制下载;其次,你能通过在IDispatch实现中拦截DISPID_NEWWINDOW2来控制窗口的创建方式。
译者注:MFC7中的DHTML类,例如CHtmlView和CDHtmlDialog实现了这些接口,但是对于使用其他的类库的程序员,可能需要自己实现这些接口。
如何工作
当一个容器提供对ActiveX 控件支持的时候 , 浏览器控件自定义机制被设计为被自动化。当浏览器控件被实例化的时候,如果可能的话,它尝试找来自宿主的 IDocHostUIHandler , IDocHostUIHandler2 和 IDocHostShowUI 实现。浏览器控件通过调用宿主的IOleClientSite接口的一个QueryInterface方法来查找。
译者注:IE5.5有个Bug,没有查询IDocHostUIHandler2 接口的实现,这使得宿主程序不能覆盖默认的参数。需要更多信息的话,参考微软知识库文章 Q272968 BUG:IDocHostUIHandler2 没有在浏览器控件中调用。
这一个结构为一个实现一个IOleClientSite接口的应用程序自动地工作,通过调用浏览器的IOleObject::SetClientSite方法传递给浏览器控件一个IOleClientSite接口。浏览器控件的一个典型的实例化可能看起来像这样:
例子
//为了明确起见,省略错误检查
CComPtr<IOleObject> spOleObj;
//创建 WebBrowser--在类成员变量 m_spWebBrowser中保存指针
CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC, IID_IWebBrowser2, (void**)&m_spWebBrowser);
// 查询WebBrowser的IOleObject接口
m_spWebBrowser->QueryInterface(IID_IOleObject, (void**)&spOleObj);
//设置用户站点
spOleObj->SetClientSite(this);
//本地激活浏览器控件
RECT rcClient
GetClientRect(&rcClient);
spOleObj->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, GetTopLevelWindow(), &rcClient);
//容器拦截浏览器事件的注册
AtlAdvise(m_spWebBrowser,GetUnknown(), DIID_DWebBrowserEvents2,&m_dwCookie);
//导航到启动页
m_spWebBrowser->Navigate(L"res://webhost.exe/startpage.htm", NULL, NULL, NULL, NULL);
然而,如果你的应用程序没有一个IOleClientSite接口,你并没失去全部希望。IE提供ICustomDoc接口,这样你能自己传递你的IDocHostUIHandler接口给浏览器。你不能使用IDocHostUIHandler2和 IDocHostShowUI接口而不提供一个浏览器控件宿主的IOleClientSite接口。
译者注:
MFC7中引入的类COleControlContainer和一大堆DHTML类曾经搞得我晕头转向,我的另一篇文章浏览器集成教学--在浏览器程序中添加宏支持中描述了该接口的实现方法。)
当浏览器控件获得了对这些接口之中的任何一个的一个指针的时候,接口的方法在适当的时候在浏览器控件的生命期中被调用。举例来说, 当用户右击在浏览器控件的客户区的任何地点时,在IE显示它的默认快捷菜单之前,你的IDocHostUIHandler::ShowContextMenu的实现将会被调用。这给你一个机会显示你自己的快捷菜单而且取消IE的快捷菜单显示。
译者注:一些屏蔽快捷菜单的示例可以在CSDN文档中心找到,网址是http://www.csdn.net/develop/article/18/18541.shtm
当初始化浏览器控件的时候 ,记住几个重点。你的应用程序应该使用 OleInitialize而不是CoInitialize启动COM。OleInitialize启用剪贴簿支持,拖放,对象连接与嵌入(OLE)和本地激活。当你的应用程序结束的时候使用OleUninitialize关闭COM库。
ATL COM 向导使用 CoInitialize而不是OleInitialize打开COM库。 如果你使用这一个向导建立一个可运行的程序,你需要将 CoInitialize 和 CoUninitialize 调用换成 OleInitialize 和 OleUninitialize。对于一个微软基础类 (MFC) 应用程序, 确定你的应用程序调用 AfxOleInit, 它在它的初始化程序中调用OleInitialize。
如果你不需要在你的应用程序中支持拖放,你可以调用IWebBrowser2::RegisterAsDropTarget,传递VARIANT_TRUE(译者注:原文如此,按照接口的文档,似乎应该传递VARIANT_FALSE), 避免任何在你的浏览器控件实例上的拖放操作。
一个浏览器控件宿主应用程序也需要IOleInPlaceSite的一个实现, 由于 IOleInPlaceSite派生自IOleWindow,应用程序将需要IOleWindow的一个实现。你需要这些实现使得你的应用程序具有一个窗口,显示浏览器控件,以及处理它的显示设置。
这些接口和IOleClientSite的实现在许多情况可能是最小的或不存在的。IOleClientSite的所有方法都可以返回E_NOTIMPL。 一些IOleInPlaceSite和IOleWindow的方法需要一个实现来覆盖返回值。可以在示例代码中查看IOleInPlaceSite和IOleWindow的最小实现的样例代码。
既然我们已经完成了初始化的准备,让我们看一看浏览器控件自定义的每一个接口。
IDocHostUIHandler
IDocHostUIHandler自IE5以后已经是可用的。它提供15个方法。大体上,一些较重要的方法是IDocHostUIHandler::GetExternal, IDocHostUIHandler::GetHostInfo, IDocHostUIHandler::GetOptionKeyPath, IDocHostUIHandler::ShowContextMenu, 和 IDocHostUIHandler::TranslateAccelerator。当然,方法对你的重要性将会依赖于你的应用程序。
IDocHostUIHandler::GetHostInfo
你使用IDocHostUIHandler::GetHostInfo告诉MSHTML有关你的应用程序的能力和需求。通过它你能控制很多东西, 举例来说:
- 你能禁用浏览器的3D的边缘。
- 你能避免滚动条或改变他们的外观。
- 你能禁用脚本。
- 你能定义双击处理的方式。
- 你能禁用浏览器的自动完成功能
IDocHostUIHandler::GetHostInfo有一个参数,被 MSHTML分配的DOCHOSTUIINFO 结构的一个指针。你的工作是要将在结构中填充你传给MSHTML的信息。
DOCHOSTUIINFO结构有四个成员。第一个成员是 cbSize,是结构的大小。你应该自己像下面的示例代码那样设置。第二个成员是dwFlags,由来自DOCHOSTUIFLAG枚举的数值位与组成。第三个成员是dwDoubleClick,来自DOCHOSTUIDBLCLK枚举的一个数值。第四个成员是pchHostCss。你可以将pchHostCss设定为浏览器控件显示的页面中应用的全局样式表(CSS)规则的一个字符串的指针。DOCHOSTUIINFO 的最后一个成员是pchHostNs。你可以设置为你提供的分号分隔的命名空间列表字符串。在你正在浏览器控件中显示的页上使用自定义标签的时候使用这一个成员。这样你能声明一个全局的命名空间列表,而不需要在每个显示的页面上声明他们。
确定使用CoTaskMemAlloc为pchHostCss或pchHostNS分配字符串。(译者注:看起来调用者用CoTaskMemFree释放这些字符串)。
例子
HRESULT GetHostInfo( DOCHOSTUIINFO* pInfo)
{
WCHAR* szCSS = L"BODY {background-color:#ffcccc}";
WCHAR* szNS = L"IE;MyTags;MyTags2='www.microsoft.com'";
#define CCHMAX 256
size_t cchLengthCSS,cchLengthszNS;
HRESULT hr=StringCchLengthW(szCSS, CCHMAX,&cchLengthCSS)
//TODO: 在这里处理错误。
OLECHAR* pCSSBuffer=(OLECHAR*) CoTaskMemAlloc((cchLengthCSS+1)*sizeof(OLECHAR));
//TODO: 在这里处理错误,确定内存成功地被分配。
hr=StringCchLengthW(szNS, CCHMAX,&cchLengthszNS)
//TODO: 在这里处理错误。
OLECHAR* pNSBuffer=(OLECHAR*) CoTaskMemAlloc((cchLengthszNS+1)*sizeof(OLECHAR));
//TODO: 在这里处理错误,确定内存成功地被分配。
hr=StringCchCopyW(pCSSBuffer , cchLengthCSS+1,szCSS)
//TODO: 在这里处理错误。
hr=StringCchCopyW(pNSBuffer , cchLengthszNS+1,szNS)
//TODO: 在这里处理错误。
pInfo-> cbSize= sizeof(DOCHOSTUIINFO)
pInfo-> dwFlags= DOCHOSTUIFLAG_NO3DBORDER|DOCHOSTUIFLAG_SCROLL_NO|DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE;
pInfo-> dwDoubleClick= DOCHOSTUIDBLCLK_DEFAULT;
pInfo-> pchHostCss= pCSSBuffer;
pInfo-> pchHostNS= pNSBuffer;
return S_OK;
}
如果你没有什么需要告诉MSHTML的,你可以在这个方法中返回E_NOTIMPL 。
IDocHostUIHandler::ShowContextMenu
通过实现这一个方法, 你获得在当一个用户右击时被浏览器控件显示的快捷菜单的控制。你能通过在这个方法中返回S_OK 阻止IE显示它的默认快捷菜单。返回一些其他的数值 , 像S_FALSE或E_NOTIMPL,允许IE继续执行它的默认快捷菜单行为。
如果你仅仅在这个方法中返回S_OK, 你能避免任何浏览器控件的右击行为。 这可能是你在许多场合中的全部需求,但是你能做到更多。通常,你使用这一个方法在返回 S_OK 之前产生并且显示你自己的快捷菜单。如果你知道浏览器控件显示的菜单的资源,而且它如何选择他们,你能也有效地自定义默认的浏览器控件快捷菜单。让我们看看它如何工作。
浏览器控件由Shdoclc.dll获得它的快捷菜单资源。这个知识和一些 #define给予你一个机会操纵浏览器的菜单。让我们举例来说,假定你对默认菜单感到满意,除了你想要除去查看源代码项之外。下列代码载入来自Shdoclc.dll的浏览器控件快捷菜单资源,根据环境选择正确的菜单,移除IDM_VIEWSOURCE命令对应的菜单项,然后显示菜单。
例子
HRESULT CBrowserHost::ShowContextMenu(DWORD dwID,
POINT *ppt,