65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (19投票s)

2014年7月6日

MIT

6分钟阅读

viewsIcon

205182

downloadIcon

4627

用于配置 Windows Forms 或 WPF 应用程序中托管的 WebBrowser 控件使用哪个版本的 Internet Explorer 的助手类。

背景

有时,我需要在我的应用程序中嵌入 HTML。如果只是显示一些带有基本交互的简单布局,我可能会使用像 HtmlRenderer 这样的组件。然而,在大多数情况下,我需要更复杂的布局、JavaScript,或者我可能想显示来自互联网的真实页面——在这种情况下,我就只能使用 WebBrowser 控件了。

我知道存在其他可嵌入的浏览器,但除非应用程序大量使用 HTML 界面,否则引入额外的多 MB 依赖项是没有意义的。

WebBrowser 控件在许多方面都让我感到恼火,但它确实能完成任务。偶尔让我沮丧的一件事是,默认情况下,它本质上是 Internet Explorer 7 的嵌入式版本——或者是在现代 IE 会话中启用兼容模式。随着越来越多的网站使用 HTML5 和其他好东西,这就不太好了。

然而,幸运的是,微软提供了配置应用程序将使用的仿真模式的功能。这不像在控件上设置一些属性那么简单,因为它涉及到设置一些注册表值和其他注意事项,但它仍然是一个合理的过程。

Do you really want to greet your users with script errors when displaying modern websites in the WebBrowser control?

关于浏览器仿真版本

下表(来源)列出了撰写本文时当前支持的仿真版本。如您所见,可以通过两种方式模拟所有“最近”的 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 控件的应用程序的默认值。

设置浏览器仿真版本

Browser emulation support is configured in the Registry

设置仿真版本非常简单——在下面的注册表项中添加一个值,其中包含您的可执行文件名称和上表中的值。

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 无效,可以避免崩溃。
  • 出于同样的原因,我明确捕获(并忽略)SecurityExceptionUnauthorizedAccessException 异常,如果用户没有权限访问这些键,这些异常将被抛出。同样,我真的不想让函数因此而崩溃。

您可以随时删除 try 块,以便抛出所有异常,而不是忽略访问异常。

获取浏览器仿真版本

By using Internet Explorer's browser emulation support, we can choose how the WebControl behaves

获取和设置仿真版本的函数使用 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);
}

如前所述,我真的不希望这些函数因预期原因而崩溃,因此这些函数还将捕获并忽略 SecurityExceptionUnauthorizedAccessException 异常。如果值已更新,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
© . All rights reserved.