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