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