// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// chrome_frame_helper_main.cc : The .exe that bootstraps the
// chrome_frame_helper.dll.
//
// This is a small exe that loads the hook dll to set the event hooks and then
// waits in a message loop. It is intended to be shut down by looking for a
// window with the class and title
// kChromeFrameHelperWindowClassName and kChromeFrameHelperWindowName and then
// sending that window a WM_CLOSE message.
//
#include <crtdbg.h>
#include <objbase.h>
#include <stdlib.h>
#include <windows.h>
#include "chrome_frame/chrome_frame_helper_util.h"
#include "chrome_frame/crash_server_init.h"
#include "chrome_frame/registry_watcher.h"
namespace {
// Window class and window names.
const wchar_t kChromeFrameHelperWindowClassName[] =
L"ChromeFrameHelperWindowClass";
const wchar_t kChromeFrameHelperWindowName[] =
L"ChromeFrameHelperWindowName";
const wchar_t kChromeFrameClientStateKey[] =
L"Software\\Google\\Update\\ClientState\\"
L"{8BA986DA-5100-405E-AA35-86F34A02ACBF}";
const wchar_t kChromeFrameUninstallCmdExeValue[] = L"UninstallString";
const wchar_t kChromeFrameUninstallCmdArgsValue[] = L"UninstallArguments";
const wchar_t kBHORegistrationPath[] =
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer"
L"\\Browser Helper Objects";
const wchar_t kStartupArg[] = L"--startup";
const wchar_t kForceUninstall[] = L"--force-uninstall";
HWND g_hwnd = NULL;
const UINT kRegistryWatcherChangeMessage = WM_USER + 42;
} // namespace
// Small helper class that assists in loading the DLL that contains code
// to register our event hook callback. Automatically removes the hook and
// unloads the DLL on destruction.
class HookDllLoader {
public:
HookDllLoader() : dll_(NULL), start_proc_(NULL), stop_proc_(NULL) {}
~HookDllLoader() {
if (dll_) {
Unload();
}
}
bool Load() {
dll_ = LoadLibrary(L"chrome_frame_helper.dll");
if (dll_) {
start_proc_ = GetProcAddress(dll_, "StartUserModeBrowserInjection");
stop_proc_ = GetProcAddress(dll_, "StopUserModeBrowserInjection");
}
bool result = true;
if (!start_proc_ || !stop_proc_) {
_ASSERTE(L"failed to load hook dll.");
result = false;
} else {
if (FAILED(start_proc_())) {
_ASSERTE(L"failed to initialize hook dll.");
result = false;
}
}
return result;
}
void Unload() {
if (stop_proc_) {
stop_proc_();
}
if (dll_) {
FreeLibrary(dll_);
}
}
private:
HMODULE dll_;
PROC start_proc_;
PROC stop_proc_;
};
// Checks the window title and then class of hwnd. If they match with that
// of a chrome_frame_helper.exe window, then add it to the list of windows
// pointed to by lparam.
BOOL CALLBACK CloseHelperWindowsEnumProc(HWND hwnd, LPARAM lparam) {
_ASSERTE(lparam != 0);
wchar_t title_buffer[MAX_PATH] = {0};
if (GetWindowText(hwnd, title_buffer, MAX_PATH)) {
if (lstrcmpiW(title_buffer, kChromeFrameHelperWindowName) == 0) {
wchar_t class_buffer[MAX_PATH] = {0};
if (GetClassName(hwnd, class_buffer, MAX_PATH)) {
if (lstrcmpiW(class_buffer, kChromeFrameHelperWindowClassName) == 0) {
if (hwnd != reinterpret_cast<HWND>(lparam)) {
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
}
}
}
}
return TRUE;
}
// Enumerates all top level windows, looking for those that look like a
// Chrome Frame helper window. It then closes all of them except for
// except_me.
void CloseAllHelperWindowsApartFrom(HWND except_me) {
EnumWindows(CloseHelperWindowsEnumProc, reinterpret_cast<LPARAM>(except_me));
}
LRESULT CALLBACK ChromeFrameHelperWndProc(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
switch (message) {
case WM_CREATE:
CloseAllHelperWindowsApartFrom(hwnd);
break;
case kRegistryWatcherChangeMessage:
// A system level Chrome appeared. Fall through:
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return ::DefWindowProc(hwnd, message, wparam, lparam);
}
return 0;
}
HWND RegisterAndCreateWindow(HINSTANCE hinstance) {
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = ChromeFrameHelperWndProc;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
wcex.lpszClassName = kChromeFrameHelperWindowClassName;
RegisterClassEx(&wcex);
HWND hwnd = CreateWindow(kChromeFrameHelperWindowClassName,
kChromeFrameHelperWindowName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0, NULL, NULL, hinstance, NULL);
return hwnd;
}
// This method runs the user-level Chrome Frame uninstall command. This is
// intended to allow it to delegate to an existing system-level install.
bool UninstallUserLevelChromeFrame() {
bool success = false;
HKEY reg_handle = NULL;
wchar_t reg_path_buffer[MAX_PATH] = {0};
LONG result = RegOpenKeyEx(HKEY_CURRENT_USER,
kChromeFrameClientStateKey,
0,
KEY_QUERY_VALUE,
®_handle);
if (result == ERROR_SUCCESS) {
wchar_t exe_buffer[MAX_PATH] = {0};
wchar_t args_buffer[MAX_PATH] = {0};
LONG exe_result = ReadValue(reg_handle,
kChromeFrameUninstallCmdExeValue,
MAX_PATH,
exe_buffer);
LONG args_result = ReadValue(reg_handle,
kChromeFrameUninstallCmdArgsValue,
MAX_PATH,
args_buffer);
RegCloseKey(reg_handle);
reg_handle = NULL;
if (exe_result == ERROR_SUCCESS && args_result == ERROR_SUCCESS) {
STARTUPINFO startup_info = {0};
startup_info.cb = sizeof(startup_info);
startup_info.dwFlags = STARTF_USESHOWWINDOW;
startup_info.wShowWindow = SW_SHOW;
PROCESS_INFORMATION process_info = {0};
// Quote the command string in the args.
wchar_t argument_string[MAX_PATH * 3] = {0};
int arg_len = _snwprintf(argument_string,
_countof(argument_string) - 1,
L"\"%s\" %s %s",
exe_buffer,
args_buffer,
kForceUninstall);
if (arg_len > 0 && CreateProcess(exe_buffer, argument_string,
NULL, NULL, FALSE, 0, NULL, NULL,
&startup_info, &process_info)) {
// Close handles.
CloseHandle(process_info.hThread);
CloseHandle(process_info.hProcess);
success = true;
}
}
}
return success;
}
void WaitCallback() {
// Check if the Chrome Frame BHO is now in the list of registered BHOs:
if (IsBHOLoadingPolicyRegistered()) {
PostMessage(g_hwnd, kRegistryWatcherChangeMessage, 0, 0);
}
}
int APIENTRY wWinMain(HINSTANCE hinstance, HINSTANCE, wchar_t*, int show_cmd) {
google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
InitializeCrashReporting(NORMAL));
if (IsSystemLevelChromeFrameInstalled()) {
// If we're running at startup, check for system-level Chrome Frame
// installations. If we have one, time
// to bail, also schedule user-level CF to be uninstalled at next logon.
const wchar_t* cmd_line = ::GetCommandLine();
if (cmd_line && wcsstr(cmd_line, kStartupArg) != NULL) {
bool uninstalled = UninstallUserLevelChromeFrame();
_ASSERTE(uninstalled);
}
return 0;
}
// Create a window with a known class and title just to listen for WM_CLOSE
// messages that will shut us down.
g_hwnd = RegisterAndCreateWindow(hinstance);
_ASSERTE(IsWindow(g_hwnd));
// Load the hook dll, and set the event hooks.
HookDllLoader loader;
bool loaded = loader.Load();
_ASSERTE(loaded);
// Start up the registry watcher
RegistryWatcher registry_watcher(HKEY_LOCAL_MACHINE, kBHORegistrationPath,
WaitCallback);
bool watching = registry_watcher.StartWatching();
_ASSERTE(watching);
if (loaded) {
MSG msg;
BOOL ret;
// Main message loop:
while ((ret = GetMessage(&msg, NULL, 0, 0))) {
if (ret == -1) {
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
UnregisterClass(kChromeFrameHelperWindowClassName, hinstance);
return 0;
}