Roll src/third_party/WebKit 116cf7f:79abaa8 (svn 189234:189235)
[chromium-blink-merge.git] / chrome / installer / util / install_util.cc
blob7a11c81d7406812811d7b83838186167f6fa5c53
1 // Copyright (c) 2012 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 // See the corresponding header file for description of the functions in this
6 // file.
8 #include "chrome/installer/util/install_util.h"
10 #include <shellapi.h>
11 #include <shlobj.h>
12 #include <shlwapi.h>
14 #include <algorithm>
16 #include "base/command_line.h"
17 #include "base/files/file_util.h"
18 #include "base/logging.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/path_service.h"
21 #include "base/process/launch.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/sys_info.h"
25 #include "base/values.h"
26 #include "base/version.h"
27 #include "base/win/metro.h"
28 #include "base/win/registry.h"
29 #include "base/win/windows_version.h"
30 #include "chrome/common/chrome_constants.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/installer/util/browser_distribution.h"
33 #include "chrome/installer/util/google_update_constants.h"
34 #include "chrome/installer/util/helper.h"
35 #include "chrome/installer/util/installation_state.h"
36 #include "chrome/installer/util/l10n_string_util.h"
37 #include "chrome/installer/util/util_constants.h"
38 #include "chrome/installer/util/work_item_list.h"
40 using base::win::RegKey;
41 using installer::ProductState;
43 namespace {
45 const wchar_t kStageBinaryPatching[] = L"binary_patching";
46 const wchar_t kStageBuilding[] = L"building";
47 const wchar_t kStageConfiguringAutoLaunch[] = L"configuring_auto_launch";
48 const wchar_t kStageCopyingPreferencesFile[] = L"copying_prefs";
49 const wchar_t kStageCreatingShortcuts[] = L"creating_shortcuts";
50 const wchar_t kStageEnsemblePatching[] = L"ensemble_patching";
51 const wchar_t kStageExecuting[] = L"executing";
52 const wchar_t kStageFinishing[] = L"finishing";
53 const wchar_t kStagePreconditions[] = L"preconditions";
54 const wchar_t kStageRefreshingPolicy[] = L"refreshing_policy";
55 const wchar_t kStageRegisteringChrome[] = L"registering_chrome";
56 const wchar_t kStageRemovingOldVersions[] = L"removing_old_ver";
57 const wchar_t kStageRollingback[] = L"rollingback";
58 const wchar_t kStageUncompressing[] = L"uncompressing";
59 const wchar_t kStageUnpacking[] = L"unpacking";
60 const wchar_t kStageUpdatingChannels[] = L"updating_channels";
61 const wchar_t kStageCreatingVisualManifest[] = L"creating_visual_manifest";
62 const wchar_t kStageDeferringToHigherVersion[] = L"deferring_to_higher_version";
63 const wchar_t kStageUninstallingBinaries[] = L"uninstalling_binaries";
64 const wchar_t kStageUninstallingChromeFrame[] = L"uninstalling_chrome_frame";
66 const wchar_t* const kStages[] = {
67 NULL,
68 kStagePreconditions,
69 kStageUncompressing,
70 kStageEnsemblePatching,
71 kStageBinaryPatching,
72 kStageUnpacking,
73 kStageBuilding,
74 kStageExecuting,
75 kStageRollingback,
76 kStageRefreshingPolicy,
77 kStageUpdatingChannels,
78 kStageCopyingPreferencesFile,
79 kStageCreatingShortcuts,
80 kStageRegisteringChrome,
81 kStageRemovingOldVersions,
82 kStageFinishing,
83 kStageConfiguringAutoLaunch,
84 kStageCreatingVisualManifest,
85 kStageDeferringToHigherVersion,
86 kStageUninstallingBinaries,
87 kStageUninstallingChromeFrame,
90 COMPILE_ASSERT(installer::NUM_STAGES == arraysize(kStages),
91 kStages_disagrees_with_Stage_comma_they_must_match_bang);
93 // Creates a zero-sized non-decorated foreground window that doesn't appear
94 // in the taskbar. This is used as a parent window for calls to ShellExecuteEx
95 // in order for the UAC dialog to appear in the foreground and for focus
96 // to be returned to this process once the UAC task is dismissed. Returns
97 // NULL on failure, a handle to the UAC window on success.
98 HWND CreateUACForegroundWindow() {
99 HWND foreground_window = ::CreateWindowEx(WS_EX_TOOLWINDOW,
100 L"STATIC",
101 NULL,
102 WS_POPUP | WS_VISIBLE,
103 0, 0, 0, 0,
104 NULL, NULL,
105 ::GetModuleHandle(NULL),
106 NULL);
107 if (foreground_window) {
108 HMONITOR monitor = ::MonitorFromWindow(foreground_window,
109 MONITOR_DEFAULTTONEAREST);
110 if (monitor) {
111 MONITORINFO mi = {0};
112 mi.cbSize = sizeof(mi);
113 ::GetMonitorInfo(monitor, &mi);
114 RECT screen_rect = mi.rcWork;
115 int x_offset = (screen_rect.right - screen_rect.left) / 2;
116 int y_offset = (screen_rect.bottom - screen_rect.top) / 2;
117 ::MoveWindow(foreground_window,
118 screen_rect.left + x_offset,
119 screen_rect.top + y_offset,
120 0, 0, FALSE);
121 } else {
122 NOTREACHED() << "Unable to get default monitor";
124 ::SetForegroundWindow(foreground_window);
126 return foreground_window;
129 } // namespace
131 base::string16 InstallUtil::GetActiveSetupPath(BrowserDistribution* dist) {
132 static const wchar_t kInstalledComponentsPath[] =
133 L"Software\\Microsoft\\Active Setup\\Installed Components\\";
134 return kInstalledComponentsPath + dist->GetActiveSetupGuid();
137 void InstallUtil::TriggerActiveSetupCommand() {
138 base::string16 active_setup_reg(
139 GetActiveSetupPath(BrowserDistribution::GetDistribution()));
140 base::win::RegKey active_setup_key(
141 HKEY_LOCAL_MACHINE, active_setup_reg.c_str(), KEY_QUERY_VALUE);
142 base::string16 cmd_str;
143 LONG read_status = active_setup_key.ReadValue(L"StubPath", &cmd_str);
144 if (read_status != ERROR_SUCCESS) {
145 LOG(ERROR) << active_setup_reg << ", " << read_status;
146 // This should never fail if Chrome is registered at system-level, but if it
147 // does there is not much else to be done.
148 return;
151 base::CommandLine cmd(base::CommandLine::FromString(cmd_str));
152 // Force creation of shortcuts as the First Run beacon might land between now
153 // and the time setup.exe checks for it.
154 cmd.AppendSwitch(installer::switches::kForceConfigureUserSettings);
156 base::LaunchOptions launch_options;
157 if (base::win::IsMetroProcess())
158 launch_options.force_breakaway_from_job_ = true;
159 base::Process process =
160 base::LaunchProcess(cmd.GetCommandLineString(), launch_options);
161 if (!process.IsValid())
162 PLOG(ERROR) << cmd.GetCommandLineString();
165 bool InstallUtil::ExecuteExeAsAdmin(const base::CommandLine& cmd,
166 DWORD* exit_code) {
167 base::FilePath::StringType program(cmd.GetProgram().value());
168 DCHECK(!program.empty());
169 DCHECK_NE(program[0], L'\"');
171 base::CommandLine::StringType params(cmd.GetCommandLineString());
172 if (params[0] == '"') {
173 DCHECK_EQ('"', params[program.length() + 1]);
174 DCHECK_EQ(program, params.substr(1, program.length()));
175 params = params.substr(program.length() + 2);
176 } else {
177 DCHECK_EQ(program, params.substr(0, program.length()));
178 params = params.substr(program.length());
181 base::TrimWhitespace(params, base::TRIM_ALL, &params);
183 HWND uac_foreground_window = CreateUACForegroundWindow();
185 SHELLEXECUTEINFO info = {0};
186 info.cbSize = sizeof(SHELLEXECUTEINFO);
187 info.fMask = SEE_MASK_NOCLOSEPROCESS;
188 info.hwnd = uac_foreground_window;
189 info.lpVerb = L"runas";
190 info.lpFile = program.c_str();
191 info.lpParameters = params.c_str();
192 info.nShow = SW_SHOW;
194 bool success = false;
195 if (::ShellExecuteEx(&info) == TRUE) {
196 ::WaitForSingleObject(info.hProcess, INFINITE);
197 DWORD ret_val = 0;
198 if (::GetExitCodeProcess(info.hProcess, &ret_val)) {
199 success = true;
200 if (exit_code)
201 *exit_code = ret_val;
205 if (uac_foreground_window) {
206 DestroyWindow(uac_foreground_window);
209 return success;
212 base::CommandLine InstallUtil::GetChromeUninstallCmd(
213 bool system_install,
214 BrowserDistribution::Type distribution_type) {
215 ProductState state;
216 if (state.Initialize(system_install, distribution_type)) {
217 return state.uninstall_command();
219 return base::CommandLine(base::CommandLine::NO_PROGRAM);
222 void InstallUtil::GetChromeVersion(BrowserDistribution* dist,
223 bool system_install,
224 Version* version) {
225 DCHECK(dist);
226 RegKey key;
227 HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
228 LONG result = key.Open(reg_root,
229 dist->GetVersionKey().c_str(),
230 KEY_QUERY_VALUE | KEY_WOW64_32KEY);
232 base::string16 version_str;
233 if (result == ERROR_SUCCESS)
234 result = key.ReadValue(google_update::kRegVersionField, &version_str);
236 *version = Version();
237 if (result == ERROR_SUCCESS && !version_str.empty()) {
238 VLOG(1) << "Existing " << dist->GetDisplayName() << " version found "
239 << version_str;
240 *version = Version(base::UTF16ToASCII(version_str));
241 } else {
242 DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
243 VLOG(1) << "No existing " << dist->GetDisplayName()
244 << " install found.";
248 void InstallUtil::GetCriticalUpdateVersion(BrowserDistribution* dist,
249 bool system_install,
250 Version* version) {
251 DCHECK(dist);
252 RegKey key;
253 HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
254 LONG result = key.Open(reg_root,
255 dist->GetVersionKey().c_str(),
256 KEY_QUERY_VALUE | KEY_WOW64_32KEY);
258 base::string16 version_str;
259 if (result == ERROR_SUCCESS)
260 result = key.ReadValue(google_update::kRegCriticalVersionField,
261 &version_str);
263 *version = Version();
264 if (result == ERROR_SUCCESS && !version_str.empty()) {
265 VLOG(1) << "Critical Update version for " << dist->GetDisplayName()
266 << " found " << version_str;
267 *version = Version(base::UTF16ToASCII(version_str));
268 } else {
269 DCHECK_EQ(ERROR_FILE_NOT_FOUND, result);
270 VLOG(1) << "No existing " << dist->GetDisplayName()
271 << " install found.";
275 bool InstallUtil::IsOSSupported() {
276 // We do not support Win2K or older, or XP without service pack 2.
277 VLOG(1) << base::SysInfo::OperatingSystemName() << ' '
278 << base::SysInfo::OperatingSystemVersion();
279 base::win::Version version = base::win::GetVersion();
280 return (version > base::win::VERSION_XP) ||
281 ((version == base::win::VERSION_XP) &&
282 (base::win::OSInfo::GetInstance()->service_pack().major >= 2));
285 void InstallUtil::AddInstallerResultItems(
286 bool system_install,
287 const base::string16& state_key,
288 installer::InstallStatus status,
289 int string_resource_id,
290 const base::string16* const launch_cmd,
291 WorkItemList* install_list) {
292 DCHECK(install_list);
293 const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
294 DWORD installer_result = (GetInstallReturnCode(status) == 0) ? 0 : 1;
295 install_list->AddCreateRegKeyWorkItem(root, state_key, KEY_WOW64_32KEY);
296 install_list->AddSetRegValueWorkItem(root,
297 state_key,
298 KEY_WOW64_32KEY,
299 installer::kInstallerResult,
300 installer_result,
301 true);
302 install_list->AddSetRegValueWorkItem(root,
303 state_key,
304 KEY_WOW64_32KEY,
305 installer::kInstallerError,
306 static_cast<DWORD>(status),
307 true);
308 if (string_resource_id != 0) {
309 base::string16 msg = installer::GetLocalizedString(string_resource_id);
310 install_list->AddSetRegValueWorkItem(root,
311 state_key,
312 KEY_WOW64_32KEY,
313 installer::kInstallerResultUIString,
314 msg,
315 true);
317 if (launch_cmd != NULL && !launch_cmd->empty()) {
318 install_list->AddSetRegValueWorkItem(
319 root,
320 state_key,
321 KEY_WOW64_32KEY,
322 installer::kInstallerSuccessLaunchCmdLine,
323 *launch_cmd,
324 true);
328 void InstallUtil::UpdateInstallerStage(bool system_install,
329 const base::string16& state_key_path,
330 installer::InstallerStage stage) {
331 DCHECK_LE(static_cast<installer::InstallerStage>(0), stage);
332 DCHECK_GT(installer::NUM_STAGES, stage);
333 const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
334 RegKey state_key;
335 LONG result =
336 state_key.Open(root,
337 state_key_path.c_str(),
338 KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_32KEY);
339 if (result == ERROR_SUCCESS) {
340 if (stage == installer::NO_STAGE) {
341 result = state_key.DeleteValue(installer::kInstallerExtraCode1);
342 LOG_IF(ERROR, result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
343 << "Failed deleting installer stage from " << state_key_path
344 << "; result: " << result;
345 } else {
346 const DWORD extra_code_1 = static_cast<DWORD>(stage);
347 result = state_key.WriteValue(installer::kInstallerExtraCode1,
348 extra_code_1);
349 LOG_IF(ERROR, result != ERROR_SUCCESS)
350 << "Failed writing installer stage to " << state_key_path
351 << "; result: " << result;
353 // TODO(grt): Remove code below here once we're convinced that our use of
354 // Google Update's new InstallerExtraCode1 value is good.
355 installer::ChannelInfo channel_info;
356 // This will return false if the "ap" value isn't present, which is fine.
357 channel_info.Initialize(state_key);
358 if (channel_info.SetStage(kStages[stage]) &&
359 !channel_info.Write(&state_key)) {
360 LOG(ERROR) << "Failed writing installer stage to " << state_key_path;
362 } else {
363 LOG(ERROR) << "Failed opening " << state_key_path
364 << " to update installer stage; result: " << result;
368 bool InstallUtil::IsPerUserInstall(const base::FilePath& exe_path) {
369 const int kProgramFilesKey =
370 #if defined(_WIN64)
371 // TODO(wfh): Revise this when Chrome is/can be installed in the 64-bit
372 // program files directory.
373 base::DIR_PROGRAM_FILESX86;
374 #else
375 base::DIR_PROGRAM_FILES;
376 #endif
377 base::FilePath program_files_path;
378 if (!PathService::Get(kProgramFilesKey, &program_files_path)) {
379 NOTREACHED();
380 return true;
382 return !StartsWith(exe_path.value(), program_files_path.value(), false);
385 bool InstallUtil::IsMultiInstall(BrowserDistribution* dist,
386 bool system_install) {
387 DCHECK(dist);
388 ProductState state;
389 return state.Initialize(system_install, dist) && state.is_multi_install();
392 bool CheckIsChromeSxSProcess() {
393 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
394 CHECK(command_line);
396 if (command_line->HasSwitch(installer::switches::kChromeSxS))
397 return true;
399 // Also return true if we are running from Chrome SxS installed path.
400 base::FilePath exe_dir;
401 PathService::Get(base::DIR_EXE, &exe_dir);
402 base::string16 chrome_sxs_dir(installer::kGoogleChromeInstallSubDir2);
403 chrome_sxs_dir.append(installer::kSxSSuffix);
405 // This is SxS if current EXE is in or under (possibly multiple levels under)
406 // |chrome_sxs_dir|\|installer::kInstallBinaryDir|
407 std::vector<base::FilePath::StringType> components;
408 exe_dir.GetComponents(&components);
409 // We need at least 1 element in the array for the behavior of the following
410 // loop to be defined. This should always be true, since we're splitting the
411 // path to our executable and one of the components will be the drive letter.
412 DCHECK(!components.empty());
413 typedef std::vector<base::FilePath::StringType>::const_reverse_iterator
414 ComponentsIterator;
415 for (ComponentsIterator current = components.rbegin(), parent = current + 1;
416 parent != components.rend(); current = parent++) {
417 if (base::FilePath::CompareEqualIgnoreCase(
418 *current, installer::kInstallBinaryDir) &&
419 base::FilePath::CompareEqualIgnoreCase(*parent, chrome_sxs_dir)) {
420 return true;
424 return false;
427 bool InstallUtil::IsChromeSxSProcess() {
428 static bool sxs = CheckIsChromeSxSProcess();
429 return sxs;
432 // static
433 bool InstallUtil::IsFirstRunSentinelPresent() {
434 // TODO(msw): Consolidate with first_run::internal::IsFirstRunSentinelPresent.
435 base::FilePath user_data_dir;
436 return !PathService::Get(chrome::DIR_USER_DATA, &user_data_dir) ||
437 base::PathExists(user_data_dir.Append(chrome::kFirstRunSentinel));
440 // static
441 bool InstallUtil::GetEULASentinelFilePath(base::FilePath* path) {
442 base::FilePath user_data_dir;
443 if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
444 return false;
445 *path = user_data_dir.Append(installer::kEULASentinelFile);
446 return true;
449 // This method tries to delete a registry key and logs an error message
450 // in case of failure. It returns true if deletion is successful (or the key did
451 // not exist), otherwise false.
452 bool InstallUtil::DeleteRegistryKey(HKEY root_key,
453 const base::string16& key_path,
454 REGSAM wow64_access) {
455 VLOG(1) << "Deleting registry key " << key_path;
456 RegKey target_key;
457 LONG result = target_key.Open(root_key, key_path.c_str(),
458 KEY_READ | KEY_WRITE | wow64_access);
460 if (result == ERROR_FILE_NOT_FOUND)
461 return true;
463 if (result == ERROR_SUCCESS)
464 result = target_key.DeleteKey(L"");
466 if (result != ERROR_SUCCESS) {
467 LOG(ERROR) << "Failed to delete registry key: " << key_path
468 << " error: " << result;
469 return false;
471 return true;
474 // This method tries to delete a registry value and logs an error message
475 // in case of failure. It returns true if deletion is successful (or the key did
476 // not exist), otherwise false.
477 bool InstallUtil::DeleteRegistryValue(HKEY reg_root,
478 const base::string16& key_path,
479 REGSAM wow64_access,
480 const base::string16& value_name) {
481 RegKey key;
482 LONG result = key.Open(reg_root, key_path.c_str(),
483 KEY_SET_VALUE | wow64_access);
484 if (result == ERROR_SUCCESS)
485 result = key.DeleteValue(value_name.c_str());
486 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
487 LOG(ERROR) << "Failed to delete registry value: " << value_name
488 << " error: " << result;
489 return false;
491 return true;
494 // static
495 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryKeyIf(
496 HKEY root_key,
497 const base::string16& key_to_delete_path,
498 const base::string16& key_to_test_path,
499 const REGSAM wow64_access,
500 const wchar_t* value_name,
501 const RegistryValuePredicate& predicate) {
502 DCHECK(root_key);
503 ConditionalDeleteResult delete_result = NOT_FOUND;
504 RegKey key;
505 base::string16 actual_value;
506 if (key.Open(root_key, key_to_test_path.c_str(),
507 KEY_QUERY_VALUE | wow64_access) == ERROR_SUCCESS &&
508 key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
509 predicate.Evaluate(actual_value)) {
510 key.Close();
511 delete_result = DeleteRegistryKey(root_key,
512 key_to_delete_path,
513 wow64_access)
514 ? DELETED : DELETE_FAILED;
516 return delete_result;
519 // static
520 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryValueIf(
521 HKEY root_key,
522 const wchar_t* key_path,
523 REGSAM wow64_access,
524 const wchar_t* value_name,
525 const RegistryValuePredicate& predicate) {
526 DCHECK(root_key);
527 DCHECK(key_path);
528 ConditionalDeleteResult delete_result = NOT_FOUND;
529 RegKey key;
530 base::string16 actual_value;
531 if (key.Open(root_key, key_path,
532 KEY_QUERY_VALUE | KEY_SET_VALUE | wow64_access)
533 == ERROR_SUCCESS &&
534 key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
535 predicate.Evaluate(actual_value)) {
536 LONG result = key.DeleteValue(value_name);
537 if (result != ERROR_SUCCESS) {
538 LOG(ERROR) << "Failed to delete registry value: "
539 << (value_name ? value_name : L"(Default)")
540 << " error: " << result;
541 delete_result = DELETE_FAILED;
543 delete_result = DELETED;
545 return delete_result;
548 bool InstallUtil::ValueEquals::Evaluate(const base::string16& value) const {
549 return value == value_to_match_;
552 // static
553 int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) {
554 switch (status) {
555 case installer::FIRST_INSTALL_SUCCESS:
556 case installer::INSTALL_REPAIRED:
557 case installer::NEW_VERSION_UPDATED:
558 case installer::IN_USE_UPDATED:
559 case installer::UNUSED_BINARIES_UNINSTALLED:
560 return 0;
561 default:
562 return status;
566 // static
567 void InstallUtil::MakeUninstallCommand(const base::string16& program,
568 const base::string16& arguments,
569 base::CommandLine* command_line) {
570 *command_line =
571 base::CommandLine::FromString(L"\"" + program + L"\" " + arguments);
574 // static
575 base::string16 InstallUtil::GetCurrentDate() {
576 static const wchar_t kDateFormat[] = L"yyyyMMdd";
577 wchar_t date_str[arraysize(kDateFormat)] = {0};
578 int len = GetDateFormatW(LOCALE_INVARIANT, 0, NULL, kDateFormat,
579 date_str, arraysize(date_str));
580 if (len) {
581 --len; // Subtract terminating \0.
582 } else {
583 PLOG(DFATAL) << "GetDateFormat";
586 return base::string16(date_str, len);
589 // Open |path| with minimal access to obtain information about it, returning
590 // true and populating |file| on success.
591 // static
592 bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath& path,
593 base::File* file) {
594 DCHECK(file);
595 file->Initialize(path, base::File::FLAG_OPEN);
596 return file->IsValid();
599 // Populate |info| for |file|, returning true on success.
600 // static
601 bool InstallUtil::ProgramCompare::GetInfo(const base::File& file,
602 BY_HANDLE_FILE_INFORMATION* info) {
603 DCHECK(file.IsValid());
604 return GetFileInformationByHandle(file.GetPlatformFile(), info) != 0;
607 InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match)
608 : path_to_match_(path_to_match),
609 file_info_() {
610 DCHECK(!path_to_match_.empty());
611 if (!OpenForInfo(path_to_match_, &file_)) {
612 PLOG(WARNING) << "Failed opening " << path_to_match_.value()
613 << "; falling back to path string comparisons.";
614 } else if (!GetInfo(file_, &file_info_)) {
615 PLOG(WARNING) << "Failed getting information for "
616 << path_to_match_.value()
617 << "; falling back to path string comparisons.";
618 file_.Close();
622 InstallUtil::ProgramCompare::~ProgramCompare() {
625 bool InstallUtil::ProgramCompare::Evaluate(const base::string16& value) const {
626 // Suss out the exe portion of the value, which is expected to be a command
627 // line kinda (or exactly) like:
628 // "c:\foo\bar\chrome.exe" -- "%1"
629 base::FilePath program(base::CommandLine::FromString(value).GetProgram());
630 if (program.empty()) {
631 LOG(WARNING) << "Failed to parse an executable name from command line: \""
632 << value << "\"";
633 return false;
636 return EvaluatePath(program);
639 bool InstallUtil::ProgramCompare::EvaluatePath(
640 const base::FilePath& path) const {
641 // Try the simple thing first: do the paths happen to match?
642 if (base::FilePath::CompareEqualIgnoreCase(path_to_match_.value(),
643 path.value()))
644 return true;
646 // If the paths don't match and we couldn't open the expected file, we've done
647 // our best.
648 if (!file_.IsValid())
649 return false;
651 // Open the program and see if it references the expected file.
652 base::File file;
653 BY_HANDLE_FILE_INFORMATION info = {};
655 return (OpenForInfo(path, &file) &&
656 GetInfo(file, &info) &&
657 info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber &&
658 info.nFileIndexHigh == file_info_.nFileIndexHigh &&
659 info.nFileIndexLow == file_info_.nFileIndexLow);