Home | History | Annotate | Download | only in chrome_frame
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // chrome_frame_helper_main.cc : The .exe that bootstraps the
      6 // chrome_frame_helper.dll.
      7 //
      8 // This is a small exe that loads the hook dll to set the event hooks and then
      9 // waits in a message loop. It is intended to be shut down by looking for a
     10 // window with the class and title
     11 // kChromeFrameHelperWindowClassName and kChromeFrameHelperWindowName and then
     12 // sending that window a WM_CLOSE message.
     13 //
     14 
     15 #include <crtdbg.h>
     16 #include <objbase.h>
     17 #include <stdlib.h>
     18 #include <windows.h>
     19 
     20 #include "chrome_frame/chrome_frame_helper_util.h"
     21 #include "chrome_frame/crash_server_init.h"
     22 #include "chrome_frame/registry_watcher.h"
     23 
     24 namespace {
     25 
     26 // Window class and window names.
     27 const wchar_t kChromeFrameHelperWindowClassName[] =
     28     L"ChromeFrameHelperWindowClass";
     29 const wchar_t kChromeFrameHelperWindowName[] =
     30     L"ChromeFrameHelperWindowName";
     31 
     32 const wchar_t kChromeFrameClientStateKey[] =
     33     L"Software\\Google\\Update\\ClientState\\"
     34     L"{8BA986DA-5100-405E-AA35-86F34A02ACBF}";
     35 const wchar_t kChromeFrameUninstallCmdExeValue[] = L"UninstallString";
     36 const wchar_t kChromeFrameUninstallCmdArgsValue[] = L"UninstallArguments";
     37 
     38 const wchar_t kBHORegistrationPath[] =
     39     L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer"
     40     L"\\Browser Helper Objects";
     41 
     42 const wchar_t kStartupArg[] = L"--startup";
     43 const wchar_t kForceUninstall[] = L"--force-uninstall";
     44 
     45 HWND g_hwnd = NULL;
     46 const UINT kRegistryWatcherChangeMessage = WM_USER + 42;
     47 
     48 }  // namespace
     49 
     50 // Small helper class that assists in loading the DLL that contains code
     51 // to register our event hook callback. Automatically removes the hook and
     52 // unloads the DLL on destruction.
     53 class HookDllLoader {
     54  public:
     55   HookDllLoader() : dll_(NULL), start_proc_(NULL), stop_proc_(NULL) {}
     56   ~HookDllLoader() {
     57     if (dll_) {
     58       Unload();
     59     }
     60   }
     61 
     62   bool Load() {
     63     dll_ = LoadLibrary(L"chrome_frame_helper.dll");
     64     if (dll_) {
     65       start_proc_ = GetProcAddress(dll_, "StartUserModeBrowserInjection");
     66       stop_proc_ = GetProcAddress(dll_, "StopUserModeBrowserInjection");
     67     }
     68 
     69     bool result = true;
     70     if (!start_proc_ || !stop_proc_) {
     71       _ASSERTE(L"failed to load hook dll.");
     72       result = false;
     73     } else {
     74       if (FAILED(start_proc_())) {
     75         _ASSERTE(L"failed to initialize hook dll.");
     76         result = false;
     77       }
     78     }
     79     return result;
     80   }
     81 
     82   void Unload() {
     83     if (stop_proc_) {
     84       stop_proc_();
     85     }
     86     if (dll_) {
     87       FreeLibrary(dll_);
     88     }
     89   }
     90 
     91  private:
     92   HMODULE dll_;
     93   PROC start_proc_;
     94   PROC stop_proc_;
     95 };
     96 
     97 // Checks the window title and then class of hwnd. If they match with that
     98 // of a chrome_frame_helper.exe window, then add it to the list of windows
     99 // pointed to by lparam.
    100 BOOL CALLBACK CloseHelperWindowsEnumProc(HWND hwnd, LPARAM lparam) {
    101   _ASSERTE(lparam != 0);
    102 
    103   wchar_t title_buffer[MAX_PATH] = {0};
    104   if (GetWindowText(hwnd, title_buffer, MAX_PATH)) {
    105     if (lstrcmpiW(title_buffer, kChromeFrameHelperWindowName) == 0) {
    106       wchar_t class_buffer[MAX_PATH] = {0};
    107       if (GetClassName(hwnd, class_buffer, MAX_PATH)) {
    108         if (lstrcmpiW(class_buffer, kChromeFrameHelperWindowClassName) == 0) {
    109           if (hwnd != reinterpret_cast<HWND>(lparam)) {
    110             PostMessage(hwnd, WM_CLOSE, 0, 0);
    111           }
    112         }
    113       }
    114     }
    115   }
    116 
    117   return TRUE;
    118 }
    119 
    120 // Enumerates all top level windows, looking for those that look like a
    121 // Chrome Frame helper window. It then closes all of them except for
    122 // except_me.
    123 void CloseAllHelperWindowsApartFrom(HWND except_me) {
    124   EnumWindows(CloseHelperWindowsEnumProc, reinterpret_cast<LPARAM>(except_me));
    125 }
    126 
    127 LRESULT CALLBACK ChromeFrameHelperWndProc(HWND hwnd,
    128                                           UINT message,
    129                                           WPARAM wparam,
    130                                           LPARAM lparam) {
    131   switch (message) {
    132     case WM_CREATE:
    133       CloseAllHelperWindowsApartFrom(hwnd);
    134       break;
    135     case kRegistryWatcherChangeMessage:
    136       // A system level Chrome appeared. Fall through:
    137     case WM_DESTROY:
    138       PostQuitMessage(0);
    139       break;
    140     default:
    141       return ::DefWindowProc(hwnd, message, wparam, lparam);
    142   }
    143   return 0;
    144 }
    145 
    146 HWND RegisterAndCreateWindow(HINSTANCE hinstance) {
    147   WNDCLASSEX wcex = {0};
    148   wcex.cbSize         = sizeof(WNDCLASSEX);
    149   wcex.lpfnWndProc    = ChromeFrameHelperWndProc;
    150   wcex.hInstance      = GetModuleHandle(NULL);
    151   wcex.hbrBackground  = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
    152   wcex.lpszClassName  = kChromeFrameHelperWindowClassName;
    153   RegisterClassEx(&wcex);
    154 
    155   HWND hwnd = CreateWindow(kChromeFrameHelperWindowClassName,
    156       kChromeFrameHelperWindowName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
    157       CW_USEDEFAULT, 0, NULL, NULL, hinstance, NULL);
    158 
    159   return hwnd;
    160 }
    161 
    162 
    163 // This method runs the user-level Chrome Frame uninstall command. This is
    164 // intended to allow it to delegate to an existing system-level install.
    165 bool UninstallUserLevelChromeFrame() {
    166   bool success = false;
    167   HKEY reg_handle = NULL;
    168   wchar_t reg_path_buffer[MAX_PATH] = {0};
    169   LONG result = RegOpenKeyEx(HKEY_CURRENT_USER,
    170                              kChromeFrameClientStateKey,
    171                              0,
    172                              KEY_QUERY_VALUE,
    173                              &reg_handle);
    174   if (result == ERROR_SUCCESS) {
    175     wchar_t exe_buffer[MAX_PATH] = {0};
    176     wchar_t args_buffer[MAX_PATH] = {0};
    177     LONG exe_result = ReadValue(reg_handle,
    178                                 kChromeFrameUninstallCmdExeValue,
    179                                 MAX_PATH,
    180                                 exe_buffer);
    181     LONG args_result = ReadValue(reg_handle,
    182                                  kChromeFrameUninstallCmdArgsValue,
    183                                  MAX_PATH,
    184                                  args_buffer);
    185     RegCloseKey(reg_handle);
    186     reg_handle = NULL;
    187 
    188     if (exe_result == ERROR_SUCCESS && args_result == ERROR_SUCCESS) {
    189       STARTUPINFO startup_info = {0};
    190       startup_info.cb = sizeof(startup_info);
    191       startup_info.dwFlags = STARTF_USESHOWWINDOW;
    192       startup_info.wShowWindow = SW_SHOW;
    193       PROCESS_INFORMATION process_info = {0};
    194 
    195       // Quote the command string in the args.
    196       wchar_t argument_string[MAX_PATH * 3] = {0};
    197       int arg_len = _snwprintf(argument_string,
    198                                _countof(argument_string) - 1,
    199                                L"\"%s\" %s %s",
    200                                exe_buffer,
    201                                args_buffer,
    202                                kForceUninstall);
    203 
    204       if (arg_len > 0 && CreateProcess(exe_buffer, argument_string,
    205                                        NULL, NULL, FALSE, 0, NULL, NULL,
    206                                        &startup_info, &process_info)) {
    207         // Close handles.
    208         CloseHandle(process_info.hThread);
    209         CloseHandle(process_info.hProcess);
    210         success = true;
    211       }
    212     }
    213   }
    214 
    215   return success;
    216 }
    217 
    218 void WaitCallback() {
    219   // Check if the Chrome Frame BHO is now in the list of registered BHOs:
    220   if (IsBHOLoadingPolicyRegistered()) {
    221     PostMessage(g_hwnd, kRegistryWatcherChangeMessage, 0, 0);
    222   }
    223 }
    224 
    225 int APIENTRY wWinMain(HINSTANCE hinstance, HINSTANCE, wchar_t*, int show_cmd) {
    226   google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
    227     InitializeCrashReporting(NORMAL));
    228 
    229   if (IsSystemLevelChromeFrameInstalled()) {
    230     // If we're running at startup, check for system-level Chrome Frame
    231     // installations. If we have one, time
    232     // to bail, also schedule user-level CF to be uninstalled at next logon.
    233     const wchar_t* cmd_line = ::GetCommandLine();
    234     if (cmd_line && wcsstr(cmd_line, kStartupArg) != NULL) {
    235       bool uninstalled = UninstallUserLevelChromeFrame();
    236       _ASSERTE(uninstalled);
    237     }
    238     return 0;
    239   }
    240 
    241   // Create a window with a known class and title just to listen for WM_CLOSE
    242   // messages that will shut us down.
    243   g_hwnd = RegisterAndCreateWindow(hinstance);
    244   _ASSERTE(IsWindow(g_hwnd));
    245 
    246   // Load the hook dll, and set the event hooks.
    247   HookDllLoader loader;
    248   bool loaded = loader.Load();
    249   _ASSERTE(loaded);
    250 
    251   // Start up the registry watcher
    252   RegistryWatcher registry_watcher(HKEY_LOCAL_MACHINE, kBHORegistrationPath,
    253                                    WaitCallback);
    254   bool watching = registry_watcher.StartWatching();
    255   _ASSERTE(watching);
    256 
    257   if (loaded) {
    258     MSG msg;
    259     BOOL ret;
    260     // Main message loop:
    261     while ((ret = GetMessage(&msg, NULL, 0, 0))) {
    262       if (ret == -1) {
    263         break;
    264       } else {
    265         TranslateMessage(&msg);
    266         DispatchMessage(&msg);
    267       }
    268     }
    269   }
    270 
    271   UnregisterClass(kChromeFrameHelperWindowClassName, hinstance);
    272   return 0;
    273 }
    274