summaryrefslogtreecommitdiffstats
path: root/chrome/installer
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-16 18:43:57 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-16 18:43:57 +0000
commit1cf1e69b1ea029d5d99a9b25d9e505cb56170630 (patch)
treecb9b84377780775f594d4185daac2f9067bb3a21 /chrome/installer
parentbf55163937acf03a29219a7b9dd5f280a55643c4 (diff)
downloadchromium_src-1cf1e69b1ea029d5d99a9b25d9e505cb56170630.zip
chromium_src-1cf1e69b1ea029d5d99a9b25d9e505cb56170630.tar.gz
chromium_src-1cf1e69b1ea029d5d99a9b25d9e505cb56170630.tar.bz2
Add support for --multi-install to mini_installer, fixed a memory leak, fixed use of Win32 registry API, and refactored SetFullInstallerFlag so that it can be exercised in unit tests (tests to follow).
When --multi-install is not on command-line, "-full" is still added to the appropriate product "ap" value in official builds. When --multi-install is present, "-full" is added to the multi installer's "ap" value unless a product is being migrated from single-install to multi-install, in which case it is put on the product's "ap" value as before. BUG=61609 TEST=Manual for now: run mini_installer.exe in various configurations, inspecting "ap" values as you go. Review URL: http://codereview.chromium.org/5875003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69428 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r--chrome/installer/mini_installer/appid.h1
-rw-r--r--chrome/installer/mini_installer/chrome_appid.cc2
-rw-r--r--chrome/installer/mini_installer/mini_installer.cc195
-rw-r--r--chrome/installer/mini_installer/mini_installer.h5
4 files changed, 162 insertions, 41 deletions
diff --git a/chrome/installer/mini_installer/appid.h b/chrome/installer/mini_installer/appid.h
index bd23b19..07f54a8 100644
--- a/chrome/installer/mini_installer/appid.h
+++ b/chrome/installer/mini_installer/appid.h
@@ -11,6 +11,7 @@ namespace google_update {
extern const wchar_t kAppGuid[];
extern const wchar_t kSxSAppGuid[];
extern const wchar_t kChromeFrameAppGuid[];
+extern const wchar_t kMultiInstallAppGuid[];
}
#endif // CHROME_INSTALLER_MINI_INSTALLER_APPID_H_
diff --git a/chrome/installer/mini_installer/chrome_appid.cc b/chrome/installer/mini_installer/chrome_appid.cc
index 8b3c283..3162523 100644
--- a/chrome/installer/mini_installer/chrome_appid.cc
+++ b/chrome/installer/mini_installer/chrome_appid.cc
@@ -8,4 +8,6 @@ namespace google_update {
const wchar_t kAppGuid[] = L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
const wchar_t kSxSAppGuid[] = L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}";
const wchar_t kChromeFrameAppGuid[] = L"{8BA986DA-5100-405E-AA35-86F34A02ACBF}";
+const wchar_t kMultiInstallAppGuid[] =
+ L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
}
diff --git a/chrome/installer/mini_installer/mini_installer.cc b/chrome/installer/mini_installer/mini_installer.cc
index a838fed..bbbcf65 100644
--- a/chrome/installer/mini_installer/mini_installer.cc
+++ b/chrome/installer/mini_installer/mini_installer.cc
@@ -56,6 +56,83 @@ struct Context {
size_t setup_resource_path_size;
};
+// A helper class used to manipulate the Windows registry. Typically, members
+// return Windows last-error codes a la the Win32 registry API.
+class RegKey {
+ public:
+ RegKey() : key_(NULL) { }
+ ~RegKey() { Close(); }
+
+ // Opens the key named |sub_key| with given |access| rights. Returns
+ // ERROR_SUCCESS or some other error.
+ LONG Open(HKEY key, const wchar_t* sub_key, REGSAM access);
+
+ // Returns true if the is open.
+ bool IsOpen() const { return key_ != NULL; }
+
+ // Read a REG_SZ value from the registry into the memory indicated by |value|
+ // (of |value_size| wchar_t units). Returns ERROR_SUCCESS,
+ // ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, or some other error. |value| is
+ // guaranteed to be null-terminated on success.
+ LONG ReadValue(const wchar_t* value_name,
+ wchar_t* value,
+ size_t value_size) const;
+
+ // Write a REG_SZ value to the registry. |value| must be null-terminated.
+ // Returns ERROR_SUCCESS or an error code.
+ LONG WriteValue(const wchar_t* value_name, const wchar_t* value);
+
+ // Closes the key if it was open.
+ void Close();
+
+ private:
+ RegKey(const RegKey&);
+ RegKey& operator=(const RegKey&);
+
+ HKEY key_;
+}; // class RegKey
+
+LONG RegKey::Open(HKEY key, const wchar_t* sub_key, REGSAM access) {
+ Close();
+ return ::RegOpenKeyEx(key, sub_key, NULL, access, &key_);
+}
+
+LONG RegKey::ReadValue(const wchar_t* value_name,
+ wchar_t* value,
+ size_t value_size) const {
+ DWORD type;
+ DWORD byte_length = static_cast<DWORD>(value_size * sizeof(wchar_t));
+ LONG result = ::RegQueryValueEx(key_, value_name, NULL, &type,
+ reinterpret_cast<BYTE*>(value),
+ &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)) < value_size)
+ value[byte_length / sizeof(wchar_t)] = L'\0';
+ else
+ result = ERROR_MORE_DATA;
+ }
+ }
+ return result;
+}
+
+LONG RegKey::WriteValue(const wchar_t* value_name, const wchar_t* value) {
+ return ::RegSetValueEx(key_, value_name, 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(value),
+ (lstrlen(value) + 1) * sizeof(wchar_t));
+}
+
+void RegKey::Close() {
+ if (key_ != NULL) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ }
+}
+
// Returns true if the given two ASCII characters are same (ignoring case).
bool EqualASCIICharI(wchar_t a, wchar_t b) {
if (a >= L'A' && a <= L'Z')
@@ -129,60 +206,86 @@ bool StrStartsWith(const wchar_t *str, const wchar_t *start_str) {
// 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) {
- 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)) {
- ::RegCloseKey(key);
+ RegKey key;
+
+ if (key.Open(root_key, sub_key, KEY_QUERY_VALUE) == ERROR_SUCCESS &&
+ key.ReadValue(value_name, value, size) == ERROR_SUCCESS) {
return true;
}
return false;
}
-// 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,
+ RegKey* 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) &&
+ (key->Open(root_key, client_state_key, access) == 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;
+ RegKey key;
+ const REGSAM key_access = KEY_QUERY_VALUE | KEY_SET_VALUE;
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_access, &key)) {
+ // The app is installed. See if it's a single-install.
+ ret = key.ReadValue(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.
+ key.Close();
+ 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.Close();
+ 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.IsOpen()) {
+ if (!OpenClientStateKey(root_key, app_guid, key_access, &key))
+ return;
+
+ ret = key.ReadValue(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
@@ -193,18 +296,29 @@ void SetFullInstallerFlag() {
value[0] = L'\0';
if (!StrEndsWith(value, kFullInstallerSuffix) &&
- (SafeStrCat(value, buf_size, kFullInstallerSuffix)))
- ::RegSetValueEx(key, kApRegistryValueName, 0, REG_SZ,
- reinterpret_cast<LPBYTE>(value),
- lstrlen(value) * sizeof(wchar_t));
+ (SafeStrCat(value, _countof(value), kFullInstallerSuffix)))
+ key.WriteValue(kApRegistryValueName, value);
}
+}
+
+// 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);
- ::RegCloseKey(key);
+ ::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 +662,10 @@ int WMain(HMODULE module) {
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);
diff --git a/chrome/installer/mini_installer/mini_installer.h b/chrome/installer/mini_installer/mini_installer.h
index 99742a7..64c6ef7 100644
--- a/chrome/installer/mini_installer/mini_installer.h
+++ b/chrome/installer/mini_installer/mini_installer.h
@@ -25,8 +25,11 @@ const wchar_t kCmdNewSetupExe[] = L" --new-setup-exe";
// Temp directory prefix that this process creates
const wchar_t kTempPrefix[] = L"CR_";
-// Google Update will use full installer if this suffix is found in ap key.
+// Google Update will use the full installer if this suffix is found in the ap
+// value.
const wchar_t kFullInstallerSuffix[] = L"-full";
+// ap value tag for a multi-install product.
+const wchar_t kMultiInstallTag[] = L"-multi";
// The resource types that would be unpacked from the mini installer.
// 'BN' is uncompressed binary and 'BL' is LZ compressed binary.