IE里的探索
青苹果工作室2001年02月08日 13:28
本节解释Microsoft Internet Explorer 4.0及更高版本的体系结构并提供使用它的组件时用到的信息,包括以下内容:* 体系结构概览;* 选择正确的组件;* 提供附加控制;* 控制上下文相关菜单;* 扩展动态 HTML 对象模型;* 控制下载;* 安全管理。
体系结构概览
Internet Explorer 使用 Microsoft ActiveX 控件和 Active Document 接口来连接各个组件。下图提供了一个高层的概览:
IExplore.exe 在最上层;它是一个在 Internet Explorer 加载时被实例化的一个小应用程序。这个可执行的应用程序使用 Internet Explorer 来完成浏览、历史维护、收藏夹维护、HTML 语法分析和翻译等,同时它为独立的浏览器提供工具栏和框架。IExplorer.exe 直接引用 Shdocvw.dll 组件。
然后 Shdocvw.dll 引用 Mshtml.dll 组件,当用户浏览到特定的类型的文档时,他还可以引用其它可以加载到浏览其中的 Active Document 组件 (如 Microsoft Office 应用程序)。Shdocvw.dll 提供与浏览、内置连接、收藏家和历史管理以及图形支持相关的功能。这个DLL 也向它的宿主提供接口以便可以像一个 ActiveX 控件一样被单独引用。Shdocvw.dll 组件更经常作为 WebBrowser 控件被引用。内置连接是指点击已加载文档中的 HTML 链接,然后在 WebBrowser 控件的同一实例中加载一个新的 HTML 文档的能力。如果只引用了 Mshtml.dll,点击链接导致新的浏览器实例产生。
Mshtml.dll 是在 Internet Explorer 4.0 或更高版本中进行 HTML 语法分析和翻译的组件,同时通过动态 HTML对象模型提供 HTML 文档。这个组件引用脚本引擎、Microsoft 虚拟机、ActiveX 控件、插件以及它加载的 HTML 文档可能引用的其它对象。Mshtml.dll 实现 Active Document 服务器接口,这样,就能通过标准 COM 接口引用它。
使用框架时,Shdocvw.dll 又被 Mshtml.dll 引用,这样就形成了第归。这种情况下,每一个帧都是一个引用 Mshtml.dll 的 Shdocvw.dll 的实例。
由于这是一个基于 OLE 的体系结构,ActiveX 控件常用的周边属性也同样适用于 Internet Explorer 组件。通过这种方式,WebBrowser 控件的宿主可以设置周边属性以过滤加载的文档引用的所有帧和控件。
选择正确的组件控件
WebBrowser 控件提供丰富的宿主需要的典型功能,如内置连接等。这样,对大多数浏览或查看 HTML 文档的应用程序而言,引用 WebBrower 比引用 MSHTML 更合适。只有特殊的,例如,对 HTML 进行语法分析的应用程序我们建议引用 MSHTML。
还需要注意的是,虽然引用 MSHTML 稍微比引用 WebBrowser 控件轻巧一点,但节约的开销很少能补偿那些实现在 WebBrowser 中已有功能时所需的额外工作。WebBrowser很可能已经被加载到内存,并且浏览包含框架的页面也会导致 WebBrowser 作为标准工作集的一部分从而被加载。
提供附加控制
WebBrowser 和 MSHTML 组件的宿主可以控制特定的功能。对 WebBrowser 控件而言,这包括浏览并且在加载文档时接受事件。这两个组件都能通过实现 IDocHostUIHandler 和 IDocHostShowUI 接口提供附加控制。这些接口通常用于替代浏览期默认提供的上下文相关菜单。它们的用途还包括设置三维边框、替代保存选项的注册表项的位置和扩展动态 HTML 对象模型。
组件通过在宿主应用程序实现的 IOleClientSite 上调用 QueryInterface 从宿主获得这些接口。
控制上下文相关菜单
引用 WebBrowser 控件时有一个常见的要求,就是能够替代或添加在浏览器窗口中点击右键时显示的上下文相关菜单。那些使用 WebBrowser 控键查看丰富内容而不希望用户知道他们在查看 HTML 的应用程序特别需要这种能力。同时,这种能力对不希望用户能看到内容的 HTML 源代码的应用程序很有用处。
有两种技术可以完成这一工作。第一种涉及到使用 IDocHostUIHandler 接口并允许应用程序禁止或替代上下文相关菜单。第二种技术使用注册表并允许扩展已有的上下文相关菜单。
●替代上下文相关菜单
通过实现 IDocHostUIHandler::ShowContextMenu 方法能将 WebBrowser 控件的 上下文相关菜单整体替换。从此方法中返回 E_NOTIMPL 或 S_FALSE 告诉 WebBrowser 它应该显示它自己的标准上下文相关菜单。然而,返回 S_OK 致使 WebBrowser 不显示 它的菜单,并假定宿主程序采取了适当的行动。宿主程序可以禁止所有上下文相关菜单 或使用它自己的上下文相关菜单。向实现这一方法的宿主程序提供的参数使宿主程序 能够识别 WebBrowser 将显示的默认菜单和菜单的坐标。这样就向宿主程序提供了全部 上下文相关菜单。例如,宿主程序可以选择仅仅替代图形的上下文相关菜单而保留标准 的上下文相关菜单。
●添加标准上下文相关菜单
通过在注册表里添加项目并将它们连接到执行脚本的 URL,可以向WebBrowser已有的上下文相关菜单中添加菜单项。要向标准上下文相关菜单中添加菜单项,创建或打开:
HKEY_CURRENT_USER
Software
Microsoft
Internet Explorer
MenuExt
在此位置,以你所希望在菜单中显示的文本为键名创建新键。键的默认值为将要执行的URL。键名可以包括&字符,表示紧跟在 & 之后的字符将带有下划线。URL 将被加载到一个隐藏的 HTML 对话框,所有嵌入脚本将被执行,然后关闭对话框。隐藏的 HTML 对话框的 external.menuArguments 属性包含了执行上下文相关菜单项的窗口的 window 对象。
以下注册表内容向 WebBrowser 的上下文相关菜单中添加标题为“My Menu Item”的菜单项,它执行包含文件 c:\myscript.htm 中的嵌入脚本。
HKEY_CURRENT_USER
Software
Microsoft
Internet Explorer
MenuExt
My Menu Item = "file://c:\myscript.htm"
c:\myscript.htm 的内容如下:
<SCRIPT LANGUAGE="JavaScript" defer>
var parentwin = external.menuArguments;
var doc = parentwin.document;
var sel = doc.selection;
var rng = sel.createRange();
var str = new String(rng.text);
if(str.length == 0)
rng.text = "MY INSERTED TEXT";
else
rng.text = str.toUpperCase();
</SCRIPT>
这个脚本通过 external.menuArguments 获得上级窗口对象。上级窗口对象时执行上下文相关菜单的 WebBrowser。然后,脚本获得当前选中区域,随后,如果当前选中区域不存在,就在执行上下文相关文档的位置插入文本“MY INSERTED TEXT”。如果当前选中区域存在,将选中的文本变成大写。
●可选注册表项
在我们刚才创建的注册表项下有一对可选值。其中之一指明此菜单项将出现在哪一个上下文相关菜单中。另一个指明将以对话框方式运行脚本。
DWORD 键值 “Contexts” 指明在哪一个上下文相关菜单中将出现此菜单项。它是由下列数值 (定义在 Mshtmhst.h 中) 以逻辑“或”方式组合起来的位掩码。这些数值同在 IDocHostUIHandler::ShowContextMenu 调用中传递的常数一致。
(0x1 << CONTEXT_MENU_DEFAULT) (evaluates to 0x1)
(0x1 << CONTEXT_MENU_IMAGE) (evaluates to 0x2)
(0x1 << CONTEXT_MENU_CONTROL) (evaluates to 0x4)
(0x1 << CONTEXT_MENU_TABLE) (evaluates to 0x8)
(0x1 << CONTEXT_MENU_TEXTSELECT) (evaluates to 0x10)
(0x1 << CONTEXT_MENU_ANCHOR) (evaluates to 0x20)
(0x1 << CONTEXT_MENU_UNKNOWN) (evaluates to 0x40)
所以,例如,如果你希望这个简单的扩展只出现在默认菜单和文本选择菜单中,你在注册表象 My Menu Item 下创建一个叫“Contexts”的 DWORD 键值,并将它设置为 0x11。在 C/C++ 代码中,可以使用以下表达式:
(0x1 << CONTEXT_MENU_DEFAULT) | (0x1 << CONTEXT_MENU_TEXTSELECT)
另一个可选的注册表 DWORD 键值是“Flags”。它只有一个有效位 (0x1);在Mshtmhst.h 中被定义为 MENUEXT_SHOWDIALOG。当此位被设置时,将像通过 showModalDialog 调用脚本时那样执行它。运行脚本的窗口不隐藏,并且执行完嵌入和 onload 脚本后不自动关闭对话框。external.menuArguments 的值仍然包含用户选择菜单项的 window 对象。
●上下文相关菜单事件
当上下文相关菜单扩展被触发时,主窗口的事件对象 (external.menuArguments.event)包含有关于用户点击何处和显示哪一个上下文相关菜单的信息。鼠标的坐标和event.srcElement 同时有效。event.type 的值包含以下字符串,指明显示了哪一个上下文相关菜单:
MenuExtDefault
MenuExtImage
MenuExtControl
MenuExtTable
MenuExtTextSelect
MenuExtAnchor
MenuExtUnknown
●另一个例子
本例在默认上下文相关菜单中创建一个新菜单项。新菜单项叫 Show In New Window,用来启动一个显示当前文档的指定部分的新窗口。如果有些东西嵌入在层次很深的框架中,你可以方便地在它自己的窗口中启动它。
以下是一个运行后插入正确的注册表设置的 .reg 文件的内容。此文件名为Example2.reg。在 Windows Explorer 中双击此文件将设置插入注册表。
REGEDIT4
[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\Show in New Window]
@="file://c:\\example2.htm"
"Contexts"=dword:00000001
Here are the contents of c:\example2.htm:
<SCRIPT LANGUAGE="JavaScript" defer>
open(external.menuArguments.location.href);
</SCRIPT>
扩展动态 HTML 对象模型
宿主应用程序可以扩展动态 HTML 对象模型以便脚本可以引用宿主提供的功能。这些脚本通过指定窗口对象可用的外部对象来引用宿主程序。例如,引用“window.external.speech”调用宿主程序以解析名称“speech”。文档里的所有标准脚本将像往常一样地执行。
这种扩展机制就是在宿主程序中为对象模型扩展提供一个 IDispatch 接口,引用外部对象时将在它上面调用 GetIDsofNames 和 Invoke。WebBrowser 或 MSHTML 组件通过调用宿主的 IDocHostUIHandler::GetExternal 方法获得所提供的 IDispatch。
关于扩展动态 HTML 对象模型的例子,参见例程序 Driller。
控制下载
宿主可以在某些方面控制下载—帧、图形、Java等等—通过实现 IOleClientSite 和一个定义为 DISPID_AMBIENT_DLCONTROL 的周边属性。当 dispidMember 设置为DISPID_AMBIENT_DLCONTROL 调用宿主的 IDispatch::Invoke 方法时,它应该将pvarResult 设置为 0 或下列值的组合:
DLCTL_BGSOUNDS 浏览器组件播放同文档相联的背景声音;
DLCTL_DLIMAGES 浏览器组件从服务器下载图形;
DLCTL_DOWNLOADONLY 浏览器组件下载页面但不显示;
DLCTL_FORCEOFFLINE 浏览器组件工作在脱机方式。通过 URLMON 提出请求时, 即使计算机连接了互联网,也设置 BINDF_OFFLINEOPERATION 标志;
DLCTL_NO_BEHAVIORS 浏览器组件不执行任何行为;
DLCTL_NO_CLIENTPULL 浏览器组件不执行任何客户端的 pull 操作;
DLCTL_NO_DLACTIVEXCTLS 浏览器组件不下载文档中的任何 ActiveX 控件;
DLCTL_NO_FRAMEDOWNLOAD 浏览器组件对包含框架的页面进行语法分析但不下载任何帧, 同时忽略框架,不翻译任何 frame 标记;
DLCTL_NO_JAVA 浏览器组件不执行任何 Java applet;
DLCTL_NO_METACHARSET 浏览器组件隐藏文档中的 META 元素指示的字符集;
DLCTL_NO_RUNACTIVEXCTLS 浏览器组件不执行文档中的任何 ActiveX 控件;
DLCTL_NO_SCRIPTS 浏览器组件不执行任何脚本;
DLCTL_OFFLINE 与 DLCTL_OFFLINEIFNOTCONNECTED 相同;
DLCTL_OFFLINEIFNOTCONNECTED 如果未连接互联网,浏览器组件将以脱机方式工作。通过 URLMON 提出请求时,即使计算机连接了互联网,也设置 BINDF_GETFROMCACHE_IF_NET_FAIL 标志;
DLCTL_PRAGMA_NO_CACHE 浏览器组件迫使请求发送给服务器并忽略代理,即使代理指明 数据是最新的也是如此。通过 URLMON 提出请求时,设置 BINDF_PRAGMA_NO_CACHE 标志;
DLCTL_RESYNCHRONIZE 浏览器组件忽略缓存中的数据并向服务器请求更新。如果服务器指明 缓存中的数据是更新了的则使用缓存数据。通过 URLMON 提出 请求时,设置 BINDF_RESYNCHRONIZE 标志;
DLCTL_SILENT 浏览器组件不显示用户界面。通过 URLMON 提出请求时,设置 BINDF_SILENTOPERATION 标志;
DLCTL_URL_ENCODING_DISABLE_UTF8 浏览器组件禁止 UTF-8 编码;
DLCTL_URL_ENCODING_ENABLE_UTF8 浏览器组件允许 UTF-8 编码;
DLCTL_VIDEOS 浏览器组件播放文档中包含的视频片断。
安全管理
浏览器组件的宿主可以实现它们自己的安全管理并替代 WebBrowser 中已有的设置。通过实现 IInternetSecurityManager 接口完成这一功能。浏览器组件以SID_SInternetSecurityManager 参数调用宿主的 IServiceProvider::QueryService方法来获得这个接口