Index: chrome/app/client_util.cc |
=================================================================== |
--- chrome/app/client_util.cc (revision 29840) |
+++ chrome/app/client_util.cc (working copy) |
@@ -2,96 +2,201 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <windows.h> |
+#include <shlwapi.h> |
+ |
+#include "chrome/app/breakpad_win.h" |
#include "chrome/app/client_util.h" |
+#include "chrome/common/result_codes.h" |
#include "chrome/installer/util/install_util.h" |
- |
#include "chrome/installer/util/google_update_constants.h" |
#include "chrome/installer/util/util_constants.h" |
namespace { |
-bool ReadStrValueFromRegistry(const HKEY reg_key, |
- const wchar_t* const value_name, |
- wchar_t** value) { |
- DWORD size = 0; |
- if (::RegQueryValueEx(reg_key, value_name, NULL, NULL, NULL, |
- &size) != ERROR_SUCCESS) { |
- return false; |
- } |
+// The entry point signature of chrome.dll. |
+typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*, wchar_t*); |
- *value = new wchar_t[1 + (size / sizeof(wchar_t))]; |
- if (::RegQueryValueEx(reg_key, value_name, NULL, NULL, |
- reinterpret_cast<BYTE*>(*value), |
- &size) != ERROR_SUCCESS) { |
- delete[] *value; |
+// Not generic, we only handle strings up to 128 chars. |
+bool ReadRegistryStr(HKEY key, const wchar_t* name, std::wstring* value) { |
+ BYTE out[128 * sizeof(wchar_t)]; |
+ DWORD size = sizeof(out); |
+ DWORD type = 0; |
+ if (ERROR_SUCCESS != ::RegQueryValueExW(key, name, NULL, &type, out, &size)) |
return false; |
- } |
- |
+ if (type != REG_SZ) |
+ return false; |
+ value->assign(reinterpret_cast<wchar_t*>(out)); |
return true; |
} |
-} |
-namespace client_util { |
-bool FileExists(const std::wstring& file_path) { |
- WIN32_FILE_ATTRIBUTE_DATA attrs; |
- return ::GetFileAttributesEx( |
- file_path.c_str(), GetFileExInfoStandard, &attrs) != 0; |
-} |
- |
-bool GetChromiumVersion(const wchar_t* const exe_path, |
- const wchar_t* const reg_key_path, |
- wchar_t** version) { |
+// Gets chrome version according to the load path. |exe_path| must be the |
+// backslash terminated directory of the current chrome.exe. |
+bool GetVersion(const wchar_t* exe_path, const wchar_t* key_path, |
+ std::wstring* version) { |
HKEY reg_root = InstallUtil::IsPerUserInstall(exe_path) ? HKEY_CURRENT_USER : |
HKEY_LOCAL_MACHINE; |
- HKEY reg_key; |
- if (::RegOpenKeyEx(reg_root, reg_key_path, 0, |
- KEY_READ, ®_key) != ERROR_SUCCESS) { |
+ HKEY key; |
+ if (::RegOpenKeyEx(reg_root, key_path, 0, KEY_READ, &key) != ERROR_SUCCESS) |
return false; |
- } |
+ // If 'new_chrome.exe' is present it means chrome was auto-updated while |
+ // running. We need to consult the opv value so we can load the old dll. |
+ // TODO(cpu) : This is solving the same problem as the environment variable |
+ // so one of them will eventually be deprecated. |
std::wstring new_chrome_exe(exe_path); |
new_chrome_exe.append(installer_util::kChromeNewExe); |
- if (FileExists(new_chrome_exe) && |
- ReadStrValueFromRegistry(reg_key, google_update::kRegOldVersionField, |
- version)) { |
- ::RegCloseKey(reg_key); |
+ if (::PathFileExistsW(new_chrome_exe.c_str()) && |
+ ReadRegistryStr(key, google_update::kRegOldVersionField, version)) { |
+ ::RegCloseKey(key); |
return true; |
} |
- bool ret = ReadStrValueFromRegistry(reg_key, |
- google_update::kRegVersionField, |
- version); |
- ::RegCloseKey(reg_key); |
+ bool ret = ReadRegistryStr(key, google_update::kRegVersionField, version); |
+ ::RegCloseKey(key); |
return ret; |
} |
-std::wstring GetDLLPath(const std::wstring& dll_name, |
- const std::wstring& dll_path) { |
- if (!dll_path.empty() && FileExists(dll_path)) |
- return dll_path + L"\\" + dll_name; |
- |
- // This is not an official build. Find the dll using the default |
- // path order in LoadLibrary. |
- wchar_t path[MAX_PATH] = {0}; |
- wchar_t* file_part = NULL; |
- DWORD result = ::SearchPath(NULL, dll_name.c_str(), NULL, MAX_PATH, |
- path, &file_part); |
- if (result == 0 || result > MAX_PATH) |
+// Gets the path of the current exe with a trailing backslash. |
+std::wstring GetExecutablePath() { |
+ wchar_t path[MAX_PATH]; |
+ ::GetModuleFileNameW(NULL, path, MAX_PATH); |
+ if (!::PathRemoveFileSpecW(path)) |
return std::wstring(); |
+ std::wstring exe_path(path); |
+ return exe_path.append(L"\\"); |
+} |
- return path; |
+// Not generic, we only handle strings up to 128 chars. |
+bool EnvQueryStr(const wchar_t* key_name, std::wstring* value) { |
+ wchar_t out[128]; |
+ DWORD count = sizeof(out)/sizeof(out[0]); |
+ DWORD rv = ::GetEnvironmentVariableW(key_name, out, count); |
+ if ((rv == 0) || (rv >= count)) |
+ return false; |
+ *value = out; |
+ return true; |
} |
-std::wstring GetExecutablePath() { |
- wchar_t exe_path[MAX_PATH]; |
- DWORD len = ::GetModuleFileName(NULL, exe_path, MAX_PATH); |
- wchar_t* tmp = exe_path + len - 1; |
- while (tmp >= exe_path && *tmp != L'\\') |
- tmp--; |
- if (tmp > exe_path) { |
- tmp++; |
- *tmp = 0; |
+// Expects that |dir| has a trailing backslash. |dir| is modified so it |
+// contains the full path that was tried. Caller must check for the return |
+// value not being null to dermine if this path contains a valid dll. |
+HMODULE LoadChromeWithDirectory(std::wstring* dir) { |
+ ::SetCurrentDirectoryW(dir->c_str()); |
+ dir->append(installer_util::kChromeDll); |
+ return ::LoadLibraryExW(dir->c_str(), NULL, |
+ LOAD_WITH_ALTERED_SEARCH_PATH); |
+} |
+ |
+// record did_run "dr" in client state. |
+bool RecordDidRun(const wchar_t* guid) { |
+ std::wstring key_path(google_update::kRegPathClientState); |
+ key_path.append(L"\\").append(guid); |
+ HKEY reg_key; |
+ if (::RegCreateKeyExW(HKEY_CURRENT_USER, key_path.c_str(), 0, NULL, |
+ REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, |
+ ®_key, NULL) == ERROR_SUCCESS) { |
+ const wchar_t kVal[] = L"1"; |
+ ::RegSetValueExW(reg_key, google_update::kRegDidRunField, 0, REG_SZ, |
+ reinterpret_cast<const BYTE *>(kVal), sizeof(kVal)); |
+ ::RegCloseKey(reg_key); |
+ return true; |
} |
- return exe_path; |
+ return false; |
} |
+} |
-} // namespace client_util |
+//============================================================================= |
+ |
+MainDllLoader::MainDllLoader() : dll_(NULL) { |
+} |
+ |
+MainDllLoader::~MainDllLoader() { |
+#ifdef PURIFY |
+ // We should never unload the dll. There is only risk and no gain from |
+ // doing so. The singleton dtors have been already run by AtExitManager. |
+ ::FreeLibrary(dll_); |
+#endif |
+} |
+ |
+// Loading chrome is an interesting afair. First we try loading from the current |
+// directory to support run-what-you-compile and other development scenarios. |
+// If that fails then we look at the 'CHROME_VERSION' env variable to determine |
+// if we should stick with an older dll version even if a new one is available |
+// to support upgrade-in-place scenarios, and if that fails we finally we look |
+// at the registry which should point us to the latest version. |
+HMODULE MainDllLoader::Load(std::wstring* version, std::wstring* file) { |
+ std::wstring dir(GetExecutablePath()); |
+ *file = dir; |
+ HMODULE dll = LoadChromeWithDirectory(file); |
+ if (dll) |
+ return dll; |
+ |
+ if (!EnvQueryStr(google_update::kEnvProductVersionKey, version)) { |
+ std::wstring reg_path(GetRegistryPath()); |
+ // Look into the registry to find the latest version. |
+ if (!GetVersion(dir.c_str(), reg_path.c_str(), version)) |
+ return NULL; |
+ } |
+ |
+ *file = dir; |
+ file->append(*version); |
+ return LoadChromeWithDirectory(file); |
+} |
+ |
+// Launching is a matter of loading the right dll, setting the CHROME_VERSION |
+// environment variable and just calling the entry point. Derived classes can |
+// add custom code in the OnBeforeLaunch callback. |
+int MainDllLoader::Launch(HINSTANCE instance, |
+ sandbox::SandboxInterfaceInfo* sbox_info) { |
+ std::wstring version; |
+ std::wstring file; |
+ dll_ = Load(&version, &file); |
+ if (!dll_) |
+ return ResultCodes::MISSING_DATA; |
+ |
+ ::SetEnvironmentVariableW(google_update::kEnvProductVersionKey, |
+ version.c_str()); |
+ |
+ InitCrashReporterWithDllPath(file); |
+ |
+ OnBeforeLaunch(version); |
+ |
+ DLL_MAIN entry_point = |
+ reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain")); |
+ if (!entry_point) |
+ return ResultCodes::BAD_PROCESS_TYPE; |
+ |
+ return entry_point(instance, sbox_info, ::GetCommandLineW()); |
+} |
+ |
+//============================================================================= |
+ |
+class ChromeDllLoader : public MainDllLoader { |
+ public: |
+ virtual std::wstring GetRegistryPath() { |
+ std::wstring key(google_update::kRegPathClients); |
+ key.append(L"\\").append(google_update::kChromeGuid); |
+ return key; |
+ } |
+ |
+ virtual void OnBeforeLaunch(const std::wstring& version) { |
+ RecordDidRun(google_update::kChromeGuid); |
+ } |
+}; |
+ |
+//============================================================================= |
+ |
+class ChromiumDllLoader : public MainDllLoader { |
+ public: |
+ virtual std::wstring GetRegistryPath() { |
+ return L"Software\\Chromium"; |
+ } |
+}; |
+ |
+MainDllLoader* MakeMainDllLoader() { |
+#if defined(GOOGLE_CHROME_BUILD) |
+ return new ChromeDllLoader(); |
+#else |
+ return new ChromiumDllLoader(); |
+#endif |
+} |