Chromium Code Reviews| Index: chrome/installer/mini_installer/mini_installer.cc |
| =================================================================== |
| --- chrome/installer/mini_installer/mini_installer.cc (revision 69281) |
| +++ chrome/installer/mini_installer/mini_installer.cc (working copy) |
| @@ -127,63 +127,121 @@ |
| return true; |
| } |
| +// Helper function to read a REG_SZ value from the registry. Returns |
| +// ERROR_SUCCESS, ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, or some other error. |
| +// |size| is the size of |value| in wchar_t units. |value| is guaranteed to be |
| +// null-terminated when ERROR_SUCCESS is returned. |
| +LONG ReadValueFromRegistry(HKEY key, const wchar_t* value_name, wchar_t* value, |
|
tommi (sloooow) - chröme
2010/12/16 06:32:25
nice
|
| + size_t size) { |
| + DWORD type; |
| + DWORD byte_length = static_cast<DWORD>(size * sizeof(wchar_t)); |
| + LONG result = ::RegQueryValueEx(key, value_name, NULL, &type, |
| + reinterpret_cast<LPBYTE>(value), |
|
tommi (sloooow) - chröme
2010/12/16 06:32:25
nit: styleguide prefers BYTE* over LPBYTE iirc
grt (UTC plus 2)
2010/12/16 17:36:34
Done.
|
| + &byte_length); |
| + if (result == ERROR_SUCCESS) { |
| + if (type != REG_SZ) { |
| + result = ERROR_NOT_SUPPORTED; |
| + } else if (byte_length == 0) { |
| + *value = L'\0'; |
| + } else if (value[byte_length/sizeof(wchar_t) - 1] != L'\0') { |
| + if (byte_length / sizeof(wchar_t) < size) |
|
tommi (sloooow) - chröme
2010/12/16 06:32:25
nit: use () for readability
grt (UTC plus 2)
2010/12/16 17:36:34
Done.
|
| + value[byte_length / sizeof(wchar_t)] = L'\0'; |
| + else |
| + result = ERROR_MORE_DATA; |
| + } |
| + } |
| + return result; |
| +} |
| + |
| // Helper function to read a value from registry. Returns true if value |
| // is read successfully and stored in parameter value. Returns false otherwise. |
| +// |size| is measured in wchar_t units. |
| bool ReadValueFromRegistry(HKEY root_key, const wchar_t *sub_key, |
| const wchar_t *value_name, wchar_t *value, |
| size_t size) { |
| + bool result = false; |
| HKEY key; |
| - if ((::RegOpenKeyEx(root_key, sub_key, NULL, |
| - KEY_READ, &key) == ERROR_SUCCESS) && |
| - (::RegQueryValueEx(key, value_name, NULL, NULL, |
| - reinterpret_cast<LPBYTE>(value), |
| - reinterpret_cast<LPDWORD>(&size)) == ERROR_SUCCESS)) { |
| + if (::RegOpenKeyEx(root_key, sub_key, NULL, |
| + KEY_READ, &key) == ERROR_SUCCESS) { |
| + result = |
| + (ReadValueFromRegistry(key, value_name, value, size) == ERROR_SUCCESS); |
| ::RegCloseKey(key); |
| - return true; |
| } |
| - return false; |
| + return result; |
| } |
| -// This function sets the flag in registry to indicate that Google Update |
| -// should try full installer next time. If the current installer works, this |
| -// flag is cleared by setup.exe at the end of install. The flag will by default |
| -// be written to HKCU, but if --system-level is included in the command line, |
| -// it will be written to HKLM instead. |
| -void SetFullInstallerFlag() { |
| - HKEY key; |
| - wchar_t ap_registry_key[128]; |
| +// Opens the Google Update ClientState key for a product. |
| +bool OpenClientStateKey(HKEY root_key, const wchar_t* app_guid, REGSAM access, |
| + HKEY* key) { |
| + wchar_t client_state_key[128]; |
| + |
| + return SafeStrCopy(client_state_key, _countof(client_state_key), |
| + kApRegistryKeyBase) && |
| + SafeStrCat(client_state_key, _countof(client_state_key), app_guid) && |
| + (::RegOpenKeyEx(root_key, client_state_key, NULL, access, key) == |
| + ERROR_SUCCESS); |
| +} |
| + |
| +// TODO(grt): Write a unit test for this that uses registry virtualization. |
| +void SetFullInstallerFlagHelper(int args_num, const wchar_t* const* args) { |
| + bool multi_install = false; |
| + HKEY key = NULL; |
| const wchar_t* app_guid = google_update::kAppGuid; |
| HKEY root_key = HKEY_CURRENT_USER; |
| + wchar_t value[128]; |
| + LONG ret; |
| - int args_num; |
| - wchar_t* cmd_line = ::GetCommandLine(); |
| - wchar_t** args = ::CommandLineToArgvW(cmd_line, &args_num); |
| for (int i = 1; i < args_num; ++i) { |
| if (0 == ::lstrcmpi(args[i], L"--chrome-sxs")) |
| app_guid = google_update::kSxSAppGuid; |
| else if (0 == ::lstrcmpi(args[i], L"--chrome-frame")) |
| app_guid = google_update::kChromeFrameAppGuid; |
| + else if (0 == ::lstrcmpi(args[i], L"--multi-install")) |
| + multi_install = true; |
| else if (0 == ::lstrcmpi(args[i], L"--system-level")) |
| root_key = HKEY_LOCAL_MACHINE; |
| } |
| - if (!SafeStrCopy(ap_registry_key, _countof(ap_registry_key), |
| - kApRegistryKeyBase) || |
| - !SafeStrCat(ap_registry_key, _countof(ap_registry_key), |
| - app_guid)) { |
| - return; |
| + // When multi_install is true, we are potentially: |
| + // 1. Performing a multi-install of some product(s) on a clean machine. |
| + // Neither the product(s) nor the multi-installer will have a ClientState |
| + // key in the registry, so there is nothing to be done. |
| + // 2. Upgrading an existing multi-install. The multi-installer will have a |
| + // ClientState key in the registry. Only it need be modified. |
| + // 3. Migrating a single-install into a multi-install. The product will have |
| + // a ClientState key in the registry. Only it need be modified. |
| + // To handle all cases, we inspect the product's ClientState to see if it |
| + // exists and its "ap" value does not contain "-multi". This is case 3, so we |
| + // modify the product's ClientState. Otherwise, we check the |
| + // multi-installer's ClientState and modify it if it exists. |
| + if (multi_install) { |
| + if (OpenClientStateKey(root_key, app_guid, KEY_READ | KEY_SET_VALUE, |
| + &key)) { |
| + // The app is installed. See if it's a single-install. |
| + ret = ReadValueFromRegistry(key, kApRegistryValueName, value, |
| + _countof(value)); |
| + if (ret != ERROR_FILE_NOT_FOUND && |
| + (ret != ERROR_SUCCESS || StrStr(value, kMultiInstallTag) != NULL)) { |
| + // Error or case 2: add "-full" to the multi-installer's value. |
| + ::RegCloseKey(key); |
|
tommi (sloooow) - chröme
2010/12/16 06:32:25
it's almost worth it to write a poor man's RegKey
grt (UTC plus 2)
2010/12/16 17:36:34
I was thinking along the same lines in the shower
|
| + key = NULL; |
| + app_guid = google_update::kMultiInstallAppGuid; |
| + } // else case 3: add "-full" to this value. |
| + } else { |
| + // case 1 or 2: add "-full" to the multi-installer's value. |
| + key = NULL; |
| + app_guid = google_update::kMultiInstallAppGuid; |
| + } |
| } |
| - if (::RegOpenKeyEx(root_key, ap_registry_key, NULL, |
| - KEY_READ | KEY_SET_VALUE, &key) != ERROR_SUCCESS) |
| - return; |
| - wchar_t value[128]; |
| - size_t size = _countof(value); |
| - size_t buf_size = size; |
| - LONG ret = ::RegQueryValueEx(key, kApRegistryValueName, NULL, NULL, |
| - reinterpret_cast<LPBYTE>(value), |
| - reinterpret_cast<LPDWORD>(&size)); |
| + if (key == NULL) { |
| + if (!OpenClientStateKey(root_key, app_guid, KEY_READ | KEY_SET_VALUE, &key)) |
| + return; |
| + ret = ReadValueFromRegistry(key, kApRegistryValueName, value, |
| + _countof(value)); |
| + } |
| + |
| // The conditions below are handling two cases: |
| // 1. When ap key is present, we want to make sure it doesn't already end |
| // in -full and then append -full to it. |
| @@ -193,18 +251,33 @@ |
| value[0] = L'\0'; |
| if (!StrEndsWith(value, kFullInstallerSuffix) && |
| - (SafeStrCat(value, buf_size, kFullInstallerSuffix))) |
| + (SafeStrCat(value, _countof(value), kFullInstallerSuffix))) |
| ::RegSetValueEx(key, kApRegistryValueName, 0, REG_SZ, |
| reinterpret_cast<LPBYTE>(value), |
| - lstrlen(value) * sizeof(wchar_t)); |
| + (lstrlen(value) + 1) * sizeof(wchar_t)); |
| } |
| ::RegCloseKey(key); |
| } |
| +// This function sets the flag in registry to indicate that Google Update |
| +// should try full installer next time. If the current installer works, this |
| +// flag is cleared by setup.exe at the end of install. The flag will by default |
| +// be written to HKCU, but if --system-level is included in the command line, |
| +// it will be written to HKLM instead. |
| +void SetFullInstallerFlag() { |
| + int args_num; |
| + wchar_t* cmd_line = ::GetCommandLine(); |
| + wchar_t** args = ::CommandLineToArgvW(cmd_line, &args_num); |
| + |
| + SetFullInstallerFlagHelper(args_num, args); |
| + |
| + ::LocalFree(args); |
| +} |
| + |
| // Gets the setup.exe path from Registry by looking the value of Uninstall |
| // string, strips the arguments for uninstall and returns only the full path |
| -// to setup.exe. |
| +// to setup.exe. |size| is measured in wchar_t units. |
| bool GetSetupExePathFromRegistry(wchar_t *path, size_t size) { |
| if (!ReadValueFromRegistry(HKEY_CURRENT_USER, kUninstallRegistryKey, |
| kUninstallRegistryValueName, path, size)) { |
| @@ -548,9 +621,10 @@ |
| if (!RunSetup(archive_path, setup_path, &exit_code)) |
| return exit_code; |
| - wchar_t value[4]; |
| + wchar_t value[2]; |
| if ((!ReadValueFromRegistry(HKEY_CURRENT_USER, kCleanupRegistryKey, |
| - kCleanupRegistryValueName, value, 4)) || |
| + kCleanupRegistryValueName, value, |
| + _countof(value))) || |
| (value[0] != L'0')) |
| DeleteExtractedFiles(base_path, archive_path, setup_path); |