使用 IE9 嵌入 WebBrowser 控件时覆盖 IE 设置 [英] Overriding IE settings when embedding a WebBrowser control using IE9

查看:19
本文介绍了使用 IE9 嵌入 WebBrowser 控件时覆盖 IE 设置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序(使用 MFC 用 C++ 编写,但我认为这不是特别相关),它嵌入了 Internet Explorer ActiveX WebBrowser 控件,用于显示一些 HTML 页面.一项要求始终是使用应用程序的字体名称和大小设置作为 HTML 的默认设置,而不是 Internet Explorer 的默认设置.

I have an application (written in C++ with MFC, but I don't think that that's particularly relevant) that embeds the Internet Explorer ActiveX WebBrowser control for the purpose of showing some HTML pages. One requirement has always been to use the application's font name and size settings as the default settings for the HTML, rather than Internet Exporer's defaults.

为此,应用程序实现了 IDocHostUIHandler2 COM 接口,并将其传递给 WebBrowser 控件.这会导致控件调用应用程序的 GetOptionKeyPath,它允许应用程序设置 WebBrowser 控件从中获取其设置的注册表位置.借助 Sysinternals 的工具来查看 IE 使用哪些键来查找字体名称和大小,这足以满足我的需要.

To achieve this, the application implements the IDocHostUIHandler2 COM interface, which it passes to the WebBrowser control. This causes the control to call the application's implementation of GetOptionKeyPath, which lets the application set the registry location that the WebBrowser control gets its settings from. Armed with Sysinternals' tools to see which keys IE uses to find the font name and size, this has been sufficient to do what I need.

然而,Internet Explorer 9 的出现让人大吃一惊:在我测试过的所有安装了 IE9 的机器上,WebBrowser 控件都使用自己的设置,而忽略了应用程序中的注册表位置.使用调试器进行测试表明 WebBrowser 控件从不调用提供的 GetOptionKeyPath.

However, the appeareance of Internet Explorer 9 has come as a nasty surprise: on all machines that I've tested with that have IE9 installed, the WebBrowser control uses its own settings, ignoring the registry location from the application. Testing with a debugger shows that the WebBrowser control never calls the provided GetOptionKeyPath.

更多实验表明 IE9 WebBrowser 控件正在调用类似(但不相同)GetOverrideKeyPath 方法:据称这提供了一种覆盖 IE 设置的方法,如果在注册表的相关部分中找不到任何内容,则返回到 IE 的实际设置.不幸的是,这有两个问题:1) 这不是我想要的,2) IE9 在转到 IE 默认注册表设置之前并不总是检查 GetOverrideKeyPath 注册表位置.

A bit more experimentation shows that the IE9 WebBrowser control is calling the similar (but not identical) GetOverrideKeyPath method: this allegedly provides a way to override IE settings, while falling back to IE's actual settings if nothing is found in the relevant part of the registry. Unfortunately this has two problems: 1) It's not quite what I want, and 2) IE9 doesn't always check under the GetOverrideKeyPath registry location before going to the IE default registry settings.

查看 GetOptionKeyPath MSDN 页面 有一些类似的投诉,但没有解决方案.有没有人找到一个干净的方法来说服 WebBrowser 控件恢复到实际调用 GetOptionKeyPath 的 IE9 之前的行为?

Looking at the GetOptionKeyPath MSDN page there are a few complaints along similar lines, but no solutions. Has anyone found a clean way to persuade the WebBrowser control to revert to the pre-IE9 behaviour of actually calling GetOptionKeyPath as documented?

推荐答案

我想出了一个技巧来解决这个问题,但我应该警告你:它并不漂亮.如果您很容易被冒犯,请立即停止阅读...

I've come up with a hack to solve this problem, but I should warn you: it's not pretty. Stop reading now if you've easily offended ...

由于似乎无法让 IE9 使用 IDocHostUIHandler::GetOptionKeyPath() 方法,我使用 SysInternals 的工具查看哪些 IE9 DLL 访问了注册表的相关部分以加载 IE9 设置.这揭示了唯一的罪魁祸首是mshtml.dll"和iertutil.dll",它们都调用了 RegOpenKeyExW().

Since there seems to be no way of making IE9 use the IDocHostUIHandler::GetOptionKeyPath() method, I used SysInternals' tools to see which IE9 DLLs accessed the relevant parts of the registry to load the IE9 settings. This revealed the only culprits as "mshtml.dll" and "iertutil.dll", both of which call RegOpenKeyExW().

然后计划是在初始化 WebBrowser 控件之前加载这些 DLL,并对它们进行修补,以便将调用重定向到我的代码,在那里我可以使用 dbghelp.dll 对我打开的注册表项撒谎.因此,首先,在初始化 WebBrowser 控件之前:

The plan was then to load these DLLs before initializing the WebBrowser control, and patch them so that calls are redirected to my code, where I can lie about what registry key I've opened, using dbghelp.dll. So, to start, before initializing the WebBrowser control:

if (theApp.GetIEVersion() >= 9.0)
{
  HMODULE advadi = ::LoadLibrary("advapi32.dll");
  HMODULE mshtml = ::LoadLibrary("mshtml.dll");
  HookApiFunction(mshtml,advadi,"advapi32.dll","RegOpenKeyExW",(PROC)HookRegOpenKeyExW);
  HMODULE iertutil = ::LoadLibrary("iertutil.dll");
  HookApiFunction(iertutil,advadi,"advapi32.dll","RegOpenKeyExW",(PROC)HookRegOpenKeyExW);
}

现在,执行扫描 DLL 导入地址表和修补请求的函数的邪恶工作的代码(省略了错误处理以减小代码大小):

And now, the code that does the evil work of scanning the DLLs import address tables, and patching the requested function (error handling omitted to keep the code size down):

void HookApiFunction(HMODULE callingDll, HMODULE calledDll, const char* calledDllName, const char* functionName, PROC newFunction)
{
  // Get the pointer to the 'real' function
  PROC realFunction = ::GetProcAddress(calledDll,functionName);

  // Get the import section of the DLL, using dbghelp.dll's ImageDirectoryEntryToData()
  ULONG sz;
  PIMAGE_IMPORT_DESCRIPTOR import = (PIMAGE_IMPORT_DESCRIPTOR)
    ImageDirectoryEntryToData(callingDll,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&sz);

  // Find the import section matching the named DLL
  while (import->Name)
  {
    PSTR dllName = (PSTR)((PBYTE)callingDll + import->Name);
    {
      if (stricmp(dllName,calledDllName) == 0)
       break;
    }
    import++;
  }
  if (import->Name == NULL)
    return;

  // Scan the IAT for this DLL
  PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)((PBYTE)callingDll + import->FirstThunk);
  while (thunk->u1.Function)
  {
    PROC* function = (PROC*)&(thunk->u1.Function);
    if (*function == realFunction)
    {
      // Make the function pointer writable and hook the function
      MEMORY_BASIC_INFORMATION mbi;
      ::VirtualQuery(function,&mbi,sizeof mbi);
      if (::VirtualProtect(mbi.BaseAddress,mbi.RegionSize,PAGE_READWRITE,&mbi.Protect))
      {
        *function = newFunction;
        DWORD protect;
        ::VirtualProtect(mbi.BaseAddress,mbi.RegionSize,mbi.Protect,&protect);
        return;
      }
    }
    thunk++;
  }

最后,我修补了 DLL 以在我的代码中调用的函数,代替 RegOpenKeyExW():

Finally, the function that I have patched the DLLs to call in my code, in place of RegOpenKeyExW():

LONG WINAPI HookRegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
{
  static const wchar_t* ieKey = L"Software\Microsoft\Internet Explorer";

  // Never redirect any of the FeatureControl settings
  if (wcsstr(lpSubKey,L"FeatureControl") != NULL)
    return ::RegOpenKeyExW(hKey,lpSubKey,ulOptions,samDesired,phkResult);

  if (wcsnicmp(lpSubKey,ieKey,wcslen(ieKey)) == 0)
  {
    // Redirect the IE settings to our registry key
    CStringW newSubKey(m_registryPath);
    newSubKey.Append(lpSubKey+wcslen(ieKey));
    return ::RegOpenKeyExW(hKey,newSubKey,ulOptions,samDesired,phkResult);
 }
 else
   return ::RegOpenKeyExW(hKey,lpSubKey,ulOptions,samDesired,phkResult);
}

令人惊讶的是,这个可怕的 hack 确实有效.但是,微软,如果你在听,请在 IE10 中正确修复这个问题.

Amazingly enough, this horrible hack actually works. But please, Microsoft, if you're listening, please fix this properly in IE10.

这篇关于使用 IE9 嵌入 WebBrowser 控件时覆盖 IE 设置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆