Переопределение настроек IE при встраивании элемента управления WebBrowser с использованием IE9 - PullRequest
3 голосов
/ 13 января 2012

У меня есть приложение (написанное на C ++ с MFC, но я не думаю, что это особенно актуально), которое встраивает элемент управления Internet Explorer ActiveX WebBrowser с целью отображения некоторых HTML-страниц.Одним требованием всегда было использование имени и размера шрифта приложения в качестве настроек по умолчанию для HTML, а не настроек Internet Exporer.

Для этого приложение реализует COM-интерфейс IDocHostUIHandler2, который он передаетэлемент управления WebBrowser.Это заставляет элемент управления вызывать реализацию приложения GetOptionKeyPath , которая позволяет приложению установить местоположение реестра, из которого элемент управления WebBrowser получает свои настройки.Вооружившись инструментами Sysinternals, чтобы увидеть, какие ключи IE использует для поиска имени и размера шрифта, этого было достаточно, чтобы сделать то, что мне нужно.

Однако появление Internet Explorer 9 стало неприятным сюрпризом:на всех машинах, которые я тестировал, на которых установлен IE9, элемент управления WebBrowser использует свои собственные настройки, игнорируя расположение реестра в приложении.Тестирование с отладчиком показывает, что элемент управления WebBrowser никогда не вызывает предоставленный GetOptionKeyPath.

Немного больше экспериментов показывает, что элемент управления IE9 WebBrowser вызывает аналогичный (но не идентичный) GetOverrideKeyPath метод: этопредположительно предоставляет способ переопределить настройки IE, при этом возвращаясь к фактическим настройкам IE, если ничего не найдено в соответствующей части реестра.К сожалению, у этого есть две проблемы: 1) Это не совсем то, что я хочу, и 2) IE9 не всегда проверяет местоположение реестра GetOverrideKeyPath, прежде чем перейти к настройкам реестра IE по умолчанию.

Просмотр Страница MSDN GetOptionKeyPath Есть несколько жалоб по аналогии, но решений нет.Кто-нибудь нашел чистый способ убедить элемент управления WebBrowser вернуться к поведению до IE9 фактического вызова GetOptionKeyPath, как задокументировано?

1 Ответ

3 голосов
/ 19 января 2012

Я решил взломать эту проблему, но я должен предупредить вас: это не красиво.Хватит читать сейчас, если вы легко обиделись ...

Поскольку, похоже, нет способа заставить IE9 использовать метод IDocHostUIHandler :: GetOptionKeyPath (), я использовал инструменты SysInternals, чтобы увидеть, какие библиотеки IE9 получили доступ ксоответствующие разделы реестра для загрузки настроек IE9.Это выявило единственных виновников, таких как «mshtml.dll» и «iertutil.dll», оба из которых вызывают RegOpenKeyExW ().

Затем планировалось загрузить эти библиотеки DLL перед инициализацией элемента управления WebBrowser и исправить их такэти вызовы перенаправляются в мой код, где я могу лгать о том, какой раздел реестра я открыл, используя dbghelp.dll.Итак, для начала, прежде чем инициализировать элемент управления WebBrowser:

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 и исправлению запрошенной функции (обработка ошибок опущена, чтобы сохранитьразмер кода уменьшен):

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 ():

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);
}

Удивительно, но этоужасный хак на самом деле работает.Но, пожалуйста, Microsoft, если вы слушаете, пожалуйста, исправьте это правильно в IE10.

...