配置 Internet Explorer WebBrowser 控件的模拟模式






4.96/5 (19投票s)
用于配置 Windows Forms 或 WPF 应用程序中托管的 WebBrowser 控件使用哪个版本的 Internet Explorer 的助手类。
背景
有时,我需要在我的应用程序中嵌入 HTML。如果只是显示一些带有基本交互的简单布局,我可能会使用像 HtmlRenderer 这样的组件。然而,在大多数情况下,我需要更复杂的布局、JavaScript,或者我可能想显示来自互联网的真实页面——在这种情况下,我就只能使用 WebBrowser
控件了。
我知道存在其他可嵌入的浏览器,但除非应用程序大量使用 HTML 界面,否则引入额外的多 MB 依赖项是没有意义的。
WebBrowser
控件在许多方面都让我感到恼火,但它确实能完成任务。偶尔让我沮丧的一件事是,默认情况下,它本质上是 Internet Explorer 7 的嵌入式版本——或者是在现代 IE 会话中启用兼容模式。随着越来越多的网站使用 HTML5 和其他好东西,这就不太好了。
然而,幸运的是,微软提供了配置应用程序将使用的仿真模式的功能。这不像在控件上设置一些属性那么简单,因为它涉及到设置一些注册表值和其他注意事项,但它仍然是一个合理的过程。
关于浏览器仿真版本
下表(来源)列出了撰写本文时当前支持的仿真版本。如您所见,可以通过两种方式模拟所有“最近”的 Internet Explorer 版本——要么强制使用标准模式,要么允许 !DOCTYPE
指令控制模式。这种双重行为的例外是版本 7,它保持不变。
根据文档,如果安装了 IE10(10000),IE8(8000)和 IE9(9000)模式将切换到 IE10(10000)模式。文档没有提及 IE11 是否仍然如此,所以我不知道这方面的行为。
值 | 描述 |
11001 | Internet Explorer 11。网页以 IE11 边缘模式显示,无论 !DOCTYPE 指令如何。 |
11000 | IE11。包含基于标准的 !DOCTYPE 指令的网页以 IE11 边缘模式显示。IE11 的默认值。 |
10001 | Internet Explorer 10。网页以 IE10 标准模式显示,无论 !DOCTYPE 指令如何。 |
10000 | Internet Explorer 10。包含基于标准的 !DOCTYPE 指令的网页以 IE10 标准模式显示。Internet Explorer 10 的默认值。 |
9999 | Windows Internet Explorer 9。网页以 IE9 标准模式显示,无论 !DOCTYPE 指令如何。 |
9000 | Internet Explorer 9。包含基于标准的 !DOCTYPE 指令的网页以 IE9 模式显示。Internet Explorer 9 的默认值。 |
8888 | 网页以 IE8 标准模式显示,无论 !DOCTYPE 指令如何。 |
8000 | 包含基于标准的 !DOCTYPE 指令的网页以 IE8 模式显示。Internet Explorer 8 的默认值 |
7000 | 包含基于标准的 !DOCTYPE 指令的网页以 IE7 标准模式显示。托管 WebBrowser 控件的应用程序的默认值。 |
设置浏览器仿真版本
设置仿真版本非常简单——在下面的注册表项中添加一个值,其中包含您的可执行文件名称和上表中的值。
HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
SOFTWARE
Microsoft
Internet Explorer
Main
FeatureControl
FEATURE_BROWSER_EMULATION
yourapp.exe = (DWORD) version
注意:如果您使用 Visual Studio 调试应用程序并启用了 Visual Studio 托管进程选项,您可能会发现可执行文件名称与您预期的不同。启用时,将使用名称略有修改的存根进程。例如,如果您的应用程序名为 calc.exe
,
您需要添加值 calc.vshost.exe 才能为正确的进程设置仿真版本。
获取 Internet Explorer 版本
由于检测用户计算机上安装的 IE 版本并设置匹配的仿真版本更有意义,因此首先我们需要一种检测 IE 版本的方法。
获取已安装 IE 版本的方法有很多,但明智的方法是从注册表中读取值,因为本文中我们所做的其他所有事情都以某种方式涉及注册表。
HKEY_LOCAL_MACHINE
SOFTWARE
Microsoft
Internet Explorer
svcVersion or Version
较旧的 IE 版本使用 Version
值,而较新的版本使用 svcVersion
。在这两种情况下,此值都包含版本 string
。
我们可以使用以下代码提取主数字。
private const string InternetExplorerRootKey = @"Software\Microsoft\Internet Explorer";
public static int GetInternetExplorerMajorVersion()
{
int result;
result = 0;
try
{
RegistryKey key;
key = Registry.LocalMachine.OpenSubKey(InternetExplorerRootKey);
if (key != null)
{
object value;
value = key.GetValue("svcVersion", null) ?? key.GetValue("Version", null);
if (value != null)
{
string version;
int separator;
version = value.ToString();
separator = version.IndexOf('.');
if (separator != -1)
{
int.TryParse(version.Substring(0, separator), out result);
}
}
}
}
catch (SecurityException)
{
// The user does not have the permissions required to read from the registry key.
}
catch (UnauthorizedAccessException)
{
// The user does not have the necessary registry rights.
}
return result;
}
注意事项
- 我返回的是包含主版本组件的
int
而不是Version
类。在此示例中,我不需要完整的版本来启动,并且如果版本string
无效,可以避免崩溃。 - 出于同样的原因,我明确捕获(并忽略)
SecurityException
和UnauthorizedAccessException
异常,如果用户没有权限访问这些键,这些异常将被抛出。同样,我真的不想让函数因此而崩溃。
您可以随时删除 try
块,以便抛出所有异常,而不是忽略访问异常。
获取浏览器仿真版本
获取和设置仿真版本的函数使用
HKEY_CURRENT_USER
,使其按用户而非整个机器生效。
首先,我们将创建一个枚举来处理上面描述的不同版本,这样我们就不必处理魔术数字了。
public enum BrowserEmulationVersion
{
Default = 0,
Version7 = 7000,
Version8 = 8000,
Version8Standards = 8888,
Version9 = 9000,
Version9Standards = 9999,
Version10 = 10000,
Version10Standards = 10001,
Version11 = 11000,
Version11Edge = 11001
}
接下来,一个函数用于检测我们的应用程序当前正在使用的仿真版本,另一个函数用于快速判断之前是否已设置了仿真版本。
private const string BrowserEmulationKey =
InternetExplorerRootKey + @"\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
public static BrowserEmulationVersion GetBrowserEmulationVersion()
{
BrowserEmulationVersion result;
result = BrowserEmulationVersion.Default;
try
{
RegistryKey key;
key = Registry.CurrentUser.OpenSubKey(BrowserEmulationKey, true);
if (key != null)
{
string programName;
object value;
programName = Path.GetFileName(Environment.GetCommandLineArgs()[0]);
value = key.GetValue(programName, null);
if (value != null)
{
result = (BrowserEmulationVersion)Convert.ToInt32(value);
}
}
}
catch (SecurityException)
{
// The user does not have the permissions required to read from the registry key.
}
catch (UnauthorizedAccessException)
{
// The user does not have the necessary registry rights.
}
return result;
}
public static bool IsBrowserEmulationSet()
{
return GetBrowserEmulationVersion() != BrowserEmulationVersion.Default;
}
设置仿真版本
最后,我们需要能够设置仿真版本。我为此提供了两个函数,一个允许您显式设置值,另一个使用与已安装的 Internet Explorer 版本最匹配的值。
public static bool SetBrowserEmulationVersion(BrowserEmulationVersion browserEmulationVersion)
{
bool result;
result = false;
try
{
RegistryKey key;
key = Registry.CurrentUser.OpenSubKey(BrowserEmulationKey, true);
if (key != null)
{
string programName;
programName = Path.GetFileName(Environment.GetCommandLineArgs()[0]);
if (browserEmulationVersion != BrowserEmulationVersion.Default)
{
// if it's a valid value, update or create the value
key.SetValue(programName, (int)browserEmulationVersion, RegistryValueKind.DWord);
}
else
{
// otherwise, remove the existing value
key.DeleteValue(programName, false);
}
result = true;
}
}
catch (SecurityException)
{
// The user does not have the permissions required to read from the registry key.
}
catch (UnauthorizedAccessException)
{
// The user does not have the necessary registry rights.
}
return result;
}
public static bool SetBrowserEmulationVersion()
{
int ieVersion;
BrowserEmulationVersion emulationCode;
ieVersion = GetInternetExplorerMajorVersion();
if (ieVersion >= 11)
{
emulationCode = BrowserEmulationVersion.Version11;
}
else
{
switch (ieVersion)
{
case 10:
emulationCode = BrowserEmulationVersion.Version10;
break;
case 9:
emulationCode = BrowserEmulationVersion.Version9;
break;
case 8:
emulationCode = BrowserEmulationVersion.Version8;
break;
default:
emulationCode = BrowserEmulationVersion.Version7;
break;
}
}
return SetBrowserEmulationVersion(emulationCode);
}
如前所述,我真的不希望这些函数因预期原因而崩溃,因此这些函数还将捕获并忽略 SecurityException
和 UnauthorizedAccessException
异常。如果值已更新,SetBrowserEmulationVersion
函数将返回 true
。
简单用法
如果您只想“即发即弃”地更新浏览器仿真版本,可以使用以下几行。
if (!InternetExplorerBrowserEmulation.IsBrowserEmulationSet())
{
InternetExplorerBrowserEmulation.SetBrowserEmulationVersion();
}
如果未设置仿真版本,这将应用最匹配的 IE 版本。然而,这意味着如果用户将 IE 更新到更新的版本,您的应用程序可能会继续使用旧版本。我将把它留作另一天的练习!
注意事项
在应用程序运行时更改仿真版本
在试验这段代码时,我确实遇到了一个主要问题。
在我编写此代码的原始应用程序中,我是在加载包含 WebBrowser
控件的第一个窗口之前应用仿真版本的,并且这完美地工作了。
然而,如果您的应用程序中已经创建了 WebBrowser
控件的实例,设置仿真版本似乎不起作用。我尝试了各种方法,例如重新创建 WebBrowser
控件或重新加载托管该控件的 Form
,但在不重新启动应用程序的情况下无法使新实例遵循设置。
附带的演示程序采用了“做出选择后重新启动”的临时解决方案——请不要在生产应用程序中这样做!
我应该更改应用程序的仿真版本吗?
您应该仔细考虑是否更改应用程序的仿真版本。如果它目前运行良好,那么最好保持原样。但是,如果您希望使用符合现代标准的 HTML、CSS 或 JavaScript,那么设置适当的仿真版本将为您节省很多麻烦。
延伸阅读
您可以对 Internet Explorer 和 WebBrowser
控件应用许多不同的选项。这些选项允许您更改行为、支持的功能以及更多。本文仅涉及了更常见的需求之一,但还有许多其他选项值得在高级应用程序场景中进行研究。
所有可用配置选项的索引可以在 MSDN 上找到。
历史
- 2014年6月28日 - 首次发布于 cyotek.com
- 2014年7月6日 - 发布于 CodeProject