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.
5 // See the corresponding header file for description of the functions in this
8 #include "chrome/installer/util/install_util.h"
16 #include "base/command_line.h"
17 #include "base/environment.h"
18 #include "base/files/file_util.h"
19 #include "base/logging.h"
20 #include "base/memory/scoped_ptr.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/path_service.h"
23 #include "base/process/launch.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/sys_info.h"
27 #include "base/values.h"
28 #include "base/version.h"
29 #include "base/win/metro.h"
30 #include "base/win/registry.h"
31 #include "base/win/windows_version.h"
32 #include "chrome/common/chrome_constants.h"
33 #include "chrome/common/chrome_paths.h"
34 #include "chrome/installer/util/browser_distribution.h"
35 #include "chrome/installer/util/google_update_constants.h"
36 #include "chrome/installer/util/helper.h"
37 #include "chrome/installer/util/installation_state.h"
38 #include "chrome/installer/util/l10n_string_util.h"
39 #include "chrome/installer/util/util_constants.h"
40 #include "chrome/installer/util/work_item_list.h"
42 using base::win::RegKey
;
43 using installer::ProductState
;
47 const wchar_t kStageBinaryPatching
[] = L
"binary_patching";
48 const wchar_t kStageBuilding
[] = L
"building";
49 const wchar_t kStageConfiguringAutoLaunch
[] = L
"configuring_auto_launch";
50 const wchar_t kStageCopyingPreferencesFile
[] = L
"copying_prefs";
51 const wchar_t kStageCreatingShortcuts
[] = L
"creating_shortcuts";
52 const wchar_t kStageEnsemblePatching
[] = L
"ensemble_patching";
53 const wchar_t kStageExecuting
[] = L
"executing";
54 const wchar_t kStageFinishing
[] = L
"finishing";
55 const wchar_t kStagePreconditions
[] = L
"preconditions";
56 const wchar_t kStageRefreshingPolicy
[] = L
"refreshing_policy";
57 const wchar_t kStageRegisteringChrome
[] = L
"registering_chrome";
58 const wchar_t kStageRemovingOldVersions
[] = L
"removing_old_ver";
59 const wchar_t kStageRollingback
[] = L
"rollingback";
60 const wchar_t kStageUncompressing
[] = L
"uncompressing";
61 const wchar_t kStageUnpacking
[] = L
"unpacking";
62 const wchar_t kStageUpdatingChannels
[] = L
"updating_channels";
63 const wchar_t kStageCreatingVisualManifest
[] = L
"creating_visual_manifest";
64 const wchar_t kStageDeferringToHigherVersion
[] = L
"deferring_to_higher_version";
65 const wchar_t kStageUninstallingBinaries
[] = L
"uninstalling_binaries";
66 const wchar_t kStageUninstallingChromeFrame
[] = L
"uninstalling_chrome_frame";
68 const wchar_t* const kStages
[] = {
72 kStageEnsemblePatching
,
78 kStageRefreshingPolicy
,
79 kStageUpdatingChannels
,
80 kStageCopyingPreferencesFile
,
81 kStageCreatingShortcuts
,
82 kStageRegisteringChrome
,
83 kStageRemovingOldVersions
,
85 kStageConfiguringAutoLaunch
,
86 kStageCreatingVisualManifest
,
87 kStageDeferringToHigherVersion
,
88 kStageUninstallingBinaries
,
89 kStageUninstallingChromeFrame
,
92 COMPILE_ASSERT(installer::NUM_STAGES
== arraysize(kStages
),
93 kStages_disagrees_with_Stage_comma_they_must_match_bang
);
95 // Creates a zero-sized non-decorated foreground window that doesn't appear
96 // in the taskbar. This is used as a parent window for calls to ShellExecuteEx
97 // in order for the UAC dialog to appear in the foreground and for focus
98 // to be returned to this process once the UAC task is dismissed. Returns
99 // NULL on failure, a handle to the UAC window on success.
100 HWND
CreateUACForegroundWindow() {
101 HWND foreground_window
= ::CreateWindowEx(WS_EX_TOOLWINDOW
,
104 WS_POPUP
| WS_VISIBLE
,
107 ::GetModuleHandle(NULL
),
109 if (foreground_window
) {
110 HMONITOR monitor
= ::MonitorFromWindow(foreground_window
,
111 MONITOR_DEFAULTTONEAREST
);
113 MONITORINFO mi
= {0};
114 mi
.cbSize
= sizeof(mi
);
115 ::GetMonitorInfo(monitor
, &mi
);
116 RECT screen_rect
= mi
.rcWork
;
117 int x_offset
= (screen_rect
.right
- screen_rect
.left
) / 2;
118 int y_offset
= (screen_rect
.bottom
- screen_rect
.top
) / 2;
119 ::MoveWindow(foreground_window
,
120 screen_rect
.left
+ x_offset
,
121 screen_rect
.top
+ y_offset
,
124 NOTREACHED() << "Unable to get default monitor";
126 ::SetForegroundWindow(foreground_window
);
128 return foreground_window
;
133 bool InstallUtil::ShouldInstallMetroProperties() {
134 // Metro support in Chrome was dropped in Win10. Although Metro properties are
135 // only meaningful on Win8+, install them on earlier versions of the OS as
136 // well in order for easier transitions on OS upgrade to Win8+.
137 return base::win::GetVersion() < base::win::VERSION_WIN10
;
140 base::string16
InstallUtil::GetActiveSetupPath(BrowserDistribution
* dist
) {
141 static const wchar_t kInstalledComponentsPath
[] =
142 L
"Software\\Microsoft\\Active Setup\\Installed Components\\";
143 return kInstalledComponentsPath
+ dist
->GetActiveSetupGuid();
146 void InstallUtil::TriggerActiveSetupCommand() {
147 base::string16
active_setup_reg(
148 GetActiveSetupPath(BrowserDistribution::GetDistribution()));
149 base::win::RegKey
active_setup_key(
150 HKEY_LOCAL_MACHINE
, active_setup_reg
.c_str(), KEY_QUERY_VALUE
);
151 base::string16 cmd_str
;
152 LONG read_status
= active_setup_key
.ReadValue(L
"StubPath", &cmd_str
);
153 if (read_status
!= ERROR_SUCCESS
) {
154 LOG(ERROR
) << active_setup_reg
<< ", " << read_status
;
155 // This should never fail if Chrome is registered at system-level, but if it
156 // does there is not much else to be done.
160 base::CommandLine
cmd(base::CommandLine::FromString(cmd_str
));
161 // Force creation of shortcuts as the First Run beacon might land between now
162 // and the time setup.exe checks for it.
163 cmd
.AppendSwitch(installer::switches::kForceConfigureUserSettings
);
165 base::LaunchOptions launch_options
;
166 if (base::win::IsMetroProcess())
167 launch_options
.force_breakaway_from_job_
= true;
168 base::Process process
=
169 base::LaunchProcess(cmd
.GetCommandLineString(), launch_options
);
170 if (!process
.IsValid())
171 PLOG(ERROR
) << cmd
.GetCommandLineString();
174 bool InstallUtil::ExecuteExeAsAdmin(const base::CommandLine
& cmd
,
176 base::FilePath::StringType
program(cmd
.GetProgram().value());
177 DCHECK(!program
.empty());
178 DCHECK_NE(program
[0], L
'\"');
180 base::CommandLine::StringType
params(cmd
.GetCommandLineString());
181 if (params
[0] == '"') {
182 DCHECK_EQ('"', params
[program
.length() + 1]);
183 DCHECK_EQ(program
, params
.substr(1, program
.length()));
184 params
= params
.substr(program
.length() + 2);
186 DCHECK_EQ(program
, params
.substr(0, program
.length()));
187 params
= params
.substr(program
.length());
190 base::TrimWhitespace(params
, base::TRIM_ALL
, ¶ms
);
192 HWND uac_foreground_window
= CreateUACForegroundWindow();
194 SHELLEXECUTEINFO info
= {0};
195 info
.cbSize
= sizeof(SHELLEXECUTEINFO
);
196 info
.fMask
= SEE_MASK_NOCLOSEPROCESS
;
197 info
.hwnd
= uac_foreground_window
;
198 info
.lpVerb
= L
"runas";
199 info
.lpFile
= program
.c_str();
200 info
.lpParameters
= params
.c_str();
201 info
.nShow
= SW_SHOW
;
203 bool success
= false;
204 if (::ShellExecuteEx(&info
) == TRUE
) {
205 ::WaitForSingleObject(info
.hProcess
, INFINITE
);
207 if (::GetExitCodeProcess(info
.hProcess
, &ret_val
)) {
210 *exit_code
= ret_val
;
214 if (uac_foreground_window
) {
215 DestroyWindow(uac_foreground_window
);
221 base::CommandLine
InstallUtil::GetChromeUninstallCmd(
223 BrowserDistribution::Type distribution_type
) {
225 if (state
.Initialize(system_install
, distribution_type
)) {
226 return state
.uninstall_command();
228 return base::CommandLine(base::CommandLine::NO_PROGRAM
);
231 void InstallUtil::GetChromeVersion(BrowserDistribution
* dist
,
236 HKEY reg_root
= (system_install
) ? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
237 LONG result
= key
.Open(reg_root
,
238 dist
->GetVersionKey().c_str(),
239 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
);
241 base::string16 version_str
;
242 if (result
== ERROR_SUCCESS
)
243 result
= key
.ReadValue(google_update::kRegVersionField
, &version_str
);
245 *version
= Version();
246 if (result
== ERROR_SUCCESS
&& !version_str
.empty()) {
247 VLOG(1) << "Existing " << dist
->GetDisplayName() << " version found "
249 *version
= Version(base::UTF16ToASCII(version_str
));
251 DCHECK_EQ(ERROR_FILE_NOT_FOUND
, result
);
252 VLOG(1) << "No existing " << dist
->GetDisplayName()
253 << " install found.";
257 void InstallUtil::GetCriticalUpdateVersion(BrowserDistribution
* dist
,
262 HKEY reg_root
= (system_install
) ? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
263 LONG result
= key
.Open(reg_root
,
264 dist
->GetVersionKey().c_str(),
265 KEY_QUERY_VALUE
| KEY_WOW64_32KEY
);
267 base::string16 version_str
;
268 if (result
== ERROR_SUCCESS
)
269 result
= key
.ReadValue(google_update::kRegCriticalVersionField
,
272 *version
= Version();
273 if (result
== ERROR_SUCCESS
&& !version_str
.empty()) {
274 VLOG(1) << "Critical Update version for " << dist
->GetDisplayName()
275 << " found " << version_str
;
276 *version
= Version(base::UTF16ToASCII(version_str
));
278 DCHECK_EQ(ERROR_FILE_NOT_FOUND
, result
);
279 VLOG(1) << "No existing " << dist
->GetDisplayName()
280 << " install found.";
284 bool InstallUtil::IsOSSupported() {
285 // We do not support Win2K or older, or XP without service pack 2.
286 VLOG(1) << base::SysInfo::OperatingSystemName() << ' '
287 << base::SysInfo::OperatingSystemVersion();
288 base::win::Version version
= base::win::GetVersion();
289 return (version
> base::win::VERSION_XP
) ||
290 ((version
== base::win::VERSION_XP
) &&
291 (base::win::OSInfo::GetInstance()->service_pack().major
>= 2));
294 void InstallUtil::AddInstallerResultItems(
296 const base::string16
& state_key
,
297 installer::InstallStatus status
,
298 int string_resource_id
,
299 const base::string16
* const launch_cmd
,
300 WorkItemList
* install_list
) {
301 DCHECK(install_list
);
302 const HKEY root
= system_install
? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
303 DWORD installer_result
= (GetInstallReturnCode(status
) == 0) ? 0 : 1;
304 install_list
->AddCreateRegKeyWorkItem(root
, state_key
, KEY_WOW64_32KEY
);
305 install_list
->AddSetRegValueWorkItem(root
,
308 installer::kInstallerResult
,
311 install_list
->AddSetRegValueWorkItem(root
,
314 installer::kInstallerError
,
315 static_cast<DWORD
>(status
),
317 if (string_resource_id
!= 0) {
318 base::string16 msg
= installer::GetLocalizedString(string_resource_id
);
319 install_list
->AddSetRegValueWorkItem(root
,
322 installer::kInstallerResultUIString
,
326 if (launch_cmd
!= NULL
&& !launch_cmd
->empty()) {
327 install_list
->AddSetRegValueWorkItem(
331 installer::kInstallerSuccessLaunchCmdLine
,
337 void InstallUtil::UpdateInstallerStage(bool system_install
,
338 const base::string16
& state_key_path
,
339 installer::InstallerStage stage
) {
340 DCHECK_LE(static_cast<installer::InstallerStage
>(0), stage
);
341 DCHECK_GT(installer::NUM_STAGES
, stage
);
342 const HKEY root
= system_install
? HKEY_LOCAL_MACHINE
: HKEY_CURRENT_USER
;
346 state_key_path
.c_str(),
347 KEY_QUERY_VALUE
| KEY_SET_VALUE
| KEY_WOW64_32KEY
);
348 if (result
== ERROR_SUCCESS
) {
349 if (stage
== installer::NO_STAGE
) {
350 result
= state_key
.DeleteValue(installer::kInstallerExtraCode1
);
351 LOG_IF(ERROR
, result
!= ERROR_SUCCESS
&& result
!= ERROR_FILE_NOT_FOUND
)
352 << "Failed deleting installer stage from " << state_key_path
353 << "; result: " << result
;
355 const DWORD extra_code_1
= static_cast<DWORD
>(stage
);
356 result
= state_key
.WriteValue(installer::kInstallerExtraCode1
,
358 LOG_IF(ERROR
, result
!= ERROR_SUCCESS
)
359 << "Failed writing installer stage to " << state_key_path
360 << "; result: " << result
;
362 // TODO(grt): Remove code below here once we're convinced that our use of
363 // Google Update's new InstallerExtraCode1 value is good.
364 installer::ChannelInfo channel_info
;
365 // This will return false if the "ap" value isn't present, which is fine.
366 channel_info
.Initialize(state_key
);
367 if (channel_info
.SetStage(kStages
[stage
]) &&
368 !channel_info
.Write(&state_key
)) {
369 LOG(ERROR
) << "Failed writing installer stage to " << state_key_path
;
372 LOG(ERROR
) << "Failed opening " << state_key_path
373 << " to update installer stage; result: " << result
;
377 bool InstallUtil::IsPerUserInstall(const base::FilePath
& exe_path
) {
378 scoped_ptr
<base::Environment
> env(base::Environment::Create());
380 static const char kEnvProgramFilesPath
[] = "CHROME_PROBED_PROGRAM_FILES_PATH";
381 std::string env_program_files_path
;
382 // Check environment variable to find program files path.
383 base::FilePath program_files_path
;
384 if (env
->GetVar(kEnvProgramFilesPath
, &env_program_files_path
) &&
385 !env_program_files_path
.empty()) {
387 base::FilePath(base::UTF8ToWide(env_program_files_path
));
389 const int kProgramFilesKey
=
391 // TODO(wfh): Revise this when Chrome is/can be installed in the 64-bit
392 // program files directory.
393 base::DIR_PROGRAM_FILESX86
;
395 base::DIR_PROGRAM_FILES
;
397 if (!PathService::Get(kProgramFilesKey
, &program_files_path
)) {
401 env
->SetVar(kEnvProgramFilesPath
,
402 base::WideToUTF8(program_files_path
.value()));
405 // Return true if the program files path is not a case-insensitive prefix of
407 if (exe_path
.value().size() < program_files_path
.value().size())
410 base::saturated_cast
<DWORD
>(program_files_path
.value().size());
411 return ::CompareString(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
,
412 exe_path
.value().data(), prefix_len
,
413 program_files_path
.value().data(), prefix_len
) !=
417 bool InstallUtil::IsMultiInstall(BrowserDistribution
* dist
,
418 bool system_install
) {
421 return state
.Initialize(system_install
, dist
) && state
.is_multi_install();
424 bool CheckIsChromeSxSProcess() {
425 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
428 if (command_line
->HasSwitch(installer::switches::kChromeSxS
))
431 // Also return true if we are running from Chrome SxS installed path.
432 base::FilePath exe_dir
;
433 PathService::Get(base::DIR_EXE
, &exe_dir
);
434 base::string16
chrome_sxs_dir(installer::kGoogleChromeInstallSubDir2
);
435 chrome_sxs_dir
.append(installer::kSxSSuffix
);
437 // This is SxS if current EXE is in or under (possibly multiple levels under)
438 // |chrome_sxs_dir|\|installer::kInstallBinaryDir|
439 std::vector
<base::FilePath::StringType
> components
;
440 exe_dir
.GetComponents(&components
);
441 // We need at least 1 element in the array for the behavior of the following
442 // loop to be defined. This should always be true, since we're splitting the
443 // path to our executable and one of the components will be the drive letter.
444 DCHECK(!components
.empty());
445 typedef std::vector
<base::FilePath::StringType
>::const_reverse_iterator
447 for (ComponentsIterator current
= components
.rbegin(), parent
= current
+ 1;
448 parent
!= components
.rend(); current
= parent
++) {
449 if (base::FilePath::CompareEqualIgnoreCase(
450 *current
, installer::kInstallBinaryDir
) &&
451 base::FilePath::CompareEqualIgnoreCase(*parent
, chrome_sxs_dir
)) {
459 bool InstallUtil::IsChromeSxSProcess() {
460 static bool sxs
= CheckIsChromeSxSProcess();
465 bool InstallUtil::IsFirstRunSentinelPresent() {
466 // TODO(msw): Consolidate with first_run::internal::IsFirstRunSentinelPresent.
467 base::FilePath user_data_dir
;
468 return !PathService::Get(chrome::DIR_USER_DATA
, &user_data_dir
) ||
469 base::PathExists(user_data_dir
.Append(chrome::kFirstRunSentinel
));
473 bool InstallUtil::GetEULASentinelFilePath(base::FilePath
* path
) {
474 base::FilePath user_data_dir
;
475 if (!PathService::Get(chrome::DIR_USER_DATA
, &user_data_dir
))
477 *path
= user_data_dir
.Append(installer::kEULASentinelFile
);
481 // This method tries to delete a registry key and logs an error message
482 // in case of failure. It returns true if deletion is successful (or the key did
483 // not exist), otherwise false.
484 bool InstallUtil::DeleteRegistryKey(HKEY root_key
,
485 const base::string16
& key_path
,
486 REGSAM wow64_access
) {
487 VLOG(1) << "Deleting registry key " << key_path
;
489 LONG result
= target_key
.Open(root_key
, key_path
.c_str(),
490 KEY_READ
| KEY_WRITE
| wow64_access
);
492 if (result
== ERROR_FILE_NOT_FOUND
)
495 if (result
== ERROR_SUCCESS
)
496 result
= target_key
.DeleteKey(L
"");
498 if (result
!= ERROR_SUCCESS
) {
499 LOG(ERROR
) << "Failed to delete registry key: " << key_path
500 << " error: " << result
;
506 // This method tries to delete a registry value and logs an error message
507 // in case of failure. It returns true if deletion is successful (or the key did
508 // not exist), otherwise false.
509 bool InstallUtil::DeleteRegistryValue(HKEY reg_root
,
510 const base::string16
& key_path
,
512 const base::string16
& value_name
) {
514 LONG result
= key
.Open(reg_root
, key_path
.c_str(),
515 KEY_SET_VALUE
| wow64_access
);
516 if (result
== ERROR_SUCCESS
)
517 result
= key
.DeleteValue(value_name
.c_str());
518 if (result
!= ERROR_SUCCESS
&& result
!= ERROR_FILE_NOT_FOUND
) {
519 LOG(ERROR
) << "Failed to delete registry value: " << value_name
520 << " error: " << result
;
527 InstallUtil::ConditionalDeleteResult
InstallUtil::DeleteRegistryKeyIf(
529 const base::string16
& key_to_delete_path
,
530 const base::string16
& key_to_test_path
,
531 const REGSAM wow64_access
,
532 const wchar_t* value_name
,
533 const RegistryValuePredicate
& predicate
) {
535 ConditionalDeleteResult delete_result
= NOT_FOUND
;
537 base::string16 actual_value
;
538 if (key
.Open(root_key
, key_to_test_path
.c_str(),
539 KEY_QUERY_VALUE
| wow64_access
) == ERROR_SUCCESS
&&
540 key
.ReadValue(value_name
, &actual_value
) == ERROR_SUCCESS
&&
541 predicate
.Evaluate(actual_value
)) {
543 delete_result
= DeleteRegistryKey(root_key
,
546 ? DELETED
: DELETE_FAILED
;
548 return delete_result
;
552 InstallUtil::ConditionalDeleteResult
InstallUtil::DeleteRegistryValueIf(
554 const wchar_t* key_path
,
556 const wchar_t* value_name
,
557 const RegistryValuePredicate
& predicate
) {
560 ConditionalDeleteResult delete_result
= NOT_FOUND
;
562 base::string16 actual_value
;
563 if (key
.Open(root_key
, key_path
,
564 KEY_QUERY_VALUE
| KEY_SET_VALUE
| wow64_access
)
566 key
.ReadValue(value_name
, &actual_value
) == ERROR_SUCCESS
&&
567 predicate
.Evaluate(actual_value
)) {
568 LONG result
= key
.DeleteValue(value_name
);
569 if (result
!= ERROR_SUCCESS
) {
570 LOG(ERROR
) << "Failed to delete registry value: "
571 << (value_name
? value_name
: L
"(Default)")
572 << " error: " << result
;
573 delete_result
= DELETE_FAILED
;
575 delete_result
= DELETED
;
577 return delete_result
;
580 bool InstallUtil::ValueEquals::Evaluate(const base::string16
& value
) const {
581 return value
== value_to_match_
;
585 int InstallUtil::GetInstallReturnCode(installer::InstallStatus status
) {
587 case installer::FIRST_INSTALL_SUCCESS
:
588 case installer::INSTALL_REPAIRED
:
589 case installer::NEW_VERSION_UPDATED
:
590 case installer::IN_USE_UPDATED
:
591 case installer::UNUSED_BINARIES_UNINSTALLED
:
599 void InstallUtil::ComposeCommandLine(const base::string16
& program
,
600 const base::string16
& arguments
,
601 base::CommandLine
* command_line
) {
603 base::CommandLine::FromString(L
"\"" + program
+ L
"\" " + arguments
);
607 base::string16
InstallUtil::GetCurrentDate() {
608 static const wchar_t kDateFormat
[] = L
"yyyyMMdd";
609 wchar_t date_str
[arraysize(kDateFormat
)] = {0};
610 int len
= GetDateFormatW(LOCALE_INVARIANT
, 0, NULL
, kDateFormat
,
611 date_str
, arraysize(date_str
));
613 --len
; // Subtract terminating \0.
615 PLOG(DFATAL
) << "GetDateFormat";
618 return base::string16(date_str
, len
);
621 // Open |path| with minimal access to obtain information about it, returning
622 // true and populating |file| on success.
624 bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath
& path
,
627 file
->Initialize(path
, base::File::FLAG_OPEN
);
628 return file
->IsValid();
631 // Populate |info| for |file|, returning true on success.
633 bool InstallUtil::ProgramCompare::GetInfo(const base::File
& file
,
634 BY_HANDLE_FILE_INFORMATION
* info
) {
635 DCHECK(file
.IsValid());
636 return GetFileInformationByHandle(file
.GetPlatformFile(), info
) != 0;
639 InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath
& path_to_match
)
640 : path_to_match_(path_to_match
),
642 DCHECK(!path_to_match_
.empty());
643 if (!OpenForInfo(path_to_match_
, &file_
)) {
644 PLOG(WARNING
) << "Failed opening " << path_to_match_
.value()
645 << "; falling back to path string comparisons.";
646 } else if (!GetInfo(file_
, &file_info_
)) {
647 PLOG(WARNING
) << "Failed getting information for "
648 << path_to_match_
.value()
649 << "; falling back to path string comparisons.";
654 InstallUtil::ProgramCompare::~ProgramCompare() {
657 bool InstallUtil::ProgramCompare::Evaluate(const base::string16
& value
) const {
658 // Suss out the exe portion of the value, which is expected to be a command
659 // line kinda (or exactly) like:
660 // "c:\foo\bar\chrome.exe" -- "%1"
661 base::FilePath
program(base::CommandLine::FromString(value
).GetProgram());
662 if (program
.empty()) {
663 LOG(WARNING
) << "Failed to parse an executable name from command line: \""
668 return EvaluatePath(program
);
671 bool InstallUtil::ProgramCompare::EvaluatePath(
672 const base::FilePath
& path
) const {
673 // Try the simple thing first: do the paths happen to match?
674 if (base::FilePath::CompareEqualIgnoreCase(path_to_match_
.value(),
678 // If the paths don't match and we couldn't open the expected file, we've done
680 if (!file_
.IsValid())
683 // Open the program and see if it references the expected file.
685 BY_HANDLE_FILE_INFORMATION info
= {};
687 return (OpenForInfo(path
, &file
) &&
688 GetInfo(file
, &info
) &&
689 info
.dwVolumeSerialNumber
== file_info_
.dwVolumeSerialNumber
&&
690 info
.nFileIndexHigh
== file_info_
.nFileIndexHigh
&&
691 info
.nFileIndexLow
== file_info_
.nFileIndexLow
);