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 ®_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