From 7ed03c8b908b25683147eec1f3063170edd68bbe Mon Sep 17 00:00:00 2001 From: Andrey Nekrasov Date: Thu, 18 Jun 2020 13:43:09 +0300 Subject: [PATCH] Runner: autoupdate should prefer .exe installer instead of .msi (#4216) --- src/action_runner/action_runner.cpp | 53 ++++++++++++++++++----------- src/common/updating/updating.cpp | 44 +++++++++++++----------- src/common/updating/updating.h | 4 +-- src/runner/main.cpp | 23 +++++++++---- src/runner/update_state.cpp | 2 ++ src/runner/update_state.h | 1 + src/runner/update_utils.cpp | 7 +++- 7 files changed, 86 insertions(+), 48 deletions(-) diff --git a/src/action_runner/action_runner.cpp b/src/action_runner/action_runner.cpp index 9977fe72fa..0b4cb6bc4e 100644 --- a/src/action_runner/action_runner.cpp +++ b/src/action_runner/action_runner.cpp @@ -56,18 +56,11 @@ std::optional copy_self_to_temp_dir() return std::move(dst_path); } -bool install_new_version_stage_1(const bool must_restart = false) +bool install_new_version_stage_1(const std::wstring_view installer_filename, const bool must_restart = false) { - std::optional installer; - for (auto path : fs::directory_iterator{ updating::get_pending_updates_path() }) - { - if (path.path().native().find(updating::installer_filename_pattern) != std::wstring::npos) - { - installer.emplace(std::move(path)); - break; - } - } - if (!installer) + const fs::path installer{ updating::get_pending_updates_path() / installer_filename }; + + if (!fs::is_regular_file(installer)) { return false; } @@ -84,7 +77,7 @@ bool install_new_version_stage_1(const bool must_restart = false) std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2_CMDARG }; arguments += L" \""; - arguments += installer->c_str(); + arguments += installer.c_str(); arguments += L"\" \""; arguments += get_module_folderpath(); arguments += L"\" "; @@ -103,15 +96,35 @@ bool install_new_version_stage_1(const bool must_restart = false) } } -bool install_new_version_stage_2(std::wstring_view installer_path, std::wstring_view install_path, const bool launch_powertoys) +bool install_new_version_stage_2(std::wstring installer_path, std::wstring_view install_path, const bool launch_powertoys) { - if (MsiInstallProductW(installer_path.data(), nullptr) != ERROR_SUCCESS) + std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower); + + bool success = true; + + if (installer_path.ends_with(L".msi")) { - return false; + success = MsiInstallProductW(installer_path.data(), nullptr) == ERROR_SUCCESS; + } + else + { + // If it's not .msi, then it's our .exe installer + SHELLEXECUTEINFOW sei{ sizeof(sei) }; + sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC }; + sei.lpFile = installer_path.c_str(); + sei.nShow = SW_SHOWNORMAL; + + success = ShellExecuteExW(&sei) == TRUE; } std::error_code _; fs::remove(installer_path, _); + + if (!success) + { + return false; + } + if (launch_powertoys) { std::wstring new_pt_path{ install_path }; @@ -230,7 +243,7 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) } run_same_elevation(target.data(), params, pidBuffer); - + // cleanup if (!pidFile.empty()) { @@ -261,11 +274,13 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) } else if (action == UPDATE_NOW_LAUNCH_STAGE1_CMDARG) { - return !install_new_version_stage_1(); + std::wstring_view installerFilename{ args[2] }; + return !install_new_version_stage_1(installerFilename); } else if (action == UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG) { - return !install_new_version_stage_1(true); + std::wstring_view installerFilename{ args[2] }; + return !install_new_version_stage_1(installerFilename, true); } else if (action == UPDATE_NOW_LAUNCH_STAGE2_CMDARG) { @@ -274,4 +289,4 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) } return 0; -} \ No newline at end of file +} diff --git a/src/common/updating/updating.cpp b/src/common/updating/updating.cpp index dc081acfcc..e929fba9d5 100644 --- a/src/common/updating/updating.cpp +++ b/src/common/updating/updating.cpp @@ -93,8 +93,8 @@ namespace updating return package_path; } - bool offer_msi_uninstallation() + { const auto selection = SHMessageBoxCheckW(nullptr, localized_strings::OFFER_UNINSTALL_MSI, localized_strings::OFFER_UNINSTALL_MSI_TITLE, MB_ICONQUESTION | MB_YESNO, IDNO, DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH); return selection == IDYES; @@ -139,22 +139,26 @@ namespace updating if (github_version > current_version) { - const std::wstring_view required_asset_extension = winstore::running_as_packaged() ? L".msix" : L".msi"; const std::wstring_view required_architecture = get_architecture_string(get_current_architecture()); constexpr const std::wstring_view required_filename_pattern = updating::installer_filename_pattern; - for (auto asset_elem : json_body.GetNamedArray(L"assets")) + // Desc-sorted by its priority + const std::array asset_extensions = { L".exe", L".msi" }; + for (const auto asset_extension : asset_extensions) { - auto asset{ asset_elem.GetObjectW() }; - std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str(); - std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower); - - const bool extension_matched = filename_lower.ends_with(required_asset_extension); - const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos; - const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos; - if (extension_matched && architecture_matched && filename_matched) + for (auto asset_elem : json_body.GetNamedArray(L"assets")) { - winrt::Windows::Foundation::Uri msi_download_url{ asset.GetNamedString(L"browser_download_url") }; - co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str(), std::move(msi_download_url), std::move(filename_lower) }; + auto asset{ asset_elem.GetObjectW() }; + std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str(); + std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower); + + const bool extension_matched = filename_lower.ends_with(asset_extension); + const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos; + const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos; + if (extension_matched && architecture_matched && filename_matched) + { + winrt::Windows::Foundation::Uri msi_download_url{ asset.GetNamedString(L"browser_download_url") }; + co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str(), std::move(msi_download_url), std::move(filename_lower) }; + } } } } @@ -216,9 +220,9 @@ namespace updating auto client = create_http_client(); auto response = co_await client.GetAsync(url); (void)response.EnsureSuccessStatusCode(); - auto msi_installer_file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(destination.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways); - co_await response.Content().WriteToStreamAsync(msi_installer_file_stream); - msi_installer_file_stream.Close(); + auto file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(destination.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways); + co_await response.Content().WriteToStreamAsync(file_stream); + file_stream.Close(); } std::future try_autoupdate(const bool download_updates_automatically) @@ -238,14 +242,14 @@ namespace updating auto installer_download_dst = get_pending_updates_path(); std::error_code _; std::filesystem::create_directories(installer_download_dst, _); - installer_download_dst /= new_version->msi_filename; + installer_download_dst /= new_version->installer_filename; bool download_success = false; for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i) { try { - co_await try_download_file(installer_download_dst, new_version->msi_download_url); + co_await try_download_file(installer_download_dst, new_version->installer_download_url); download_success = true; break; } @@ -271,8 +275,8 @@ namespace updating notifications::show_toast_with_activations(std::move(new_version_ready), {}, - { notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" }, - notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" }, + { notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" + new_version->installer_filename }, + notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" + new_version->installer_filename }, notifications::snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } }, std::move(toast_params)); } diff --git a/src/common/updating/updating.h b/src/common/updating/updating.h index 1f76288a74..2f0716e1f2 100644 --- a/src/common/updating/updating.h +++ b/src/common/updating/updating.h @@ -21,8 +21,8 @@ namespace updating { winrt::Windows::Foundation::Uri release_page_uri; std::wstring version_string; - winrt::Windows::Foundation::Uri msi_download_url; - std::wstring msi_filename; + winrt::Windows::Foundation::Uri installer_download_url; + std::wstring installer_filename; }; std::future> get_new_github_version_info_async(); diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 2130eead35..23ef9ffb24 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -130,7 +130,7 @@ int runner(bool isProcessElevated) chdir_current_executable(); // Load Powertoys DLLS // For now only load known DLLs - + std::wstring baseModuleFolder = L"modules/"; std::unordered_set known_dlls = { @@ -232,21 +232,32 @@ enum class toast_notification_handler_result exit_error }; + toast_notification_handler_result toast_notification_handler(const std::wstring_view param) { - if (param == L"cant_drag_elevated_disable/") + const std::wstring_view cant_drag_elevated_disable = L"cant_drag_elevated_disable/"; + const std::wstring_view update_now = L"update_now/"; + const std::wstring_view schedule_update = L"schedule_update/"; + + if (param == cant_drag_elevated_disable) { return disable_cant_drag_elevated_warning() ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error; } - else if (param == L"update_now/") + else if (param.starts_with(update_now)) { - launch_action_runner(UPDATE_NOW_LAUNCH_STAGE1_CMDARG); + std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_CMDARG }; + const auto installerFilename = param.data() + size(update_now); + args += L' '; + args += installerFilename; + launch_action_runner(args.c_str()); return toast_notification_handler_result::exit_success; } - else if (param == L"schedule_update/") + else if (param.starts_with(schedule_update)) { - UpdateState::store([](UpdateState& state) { + const auto installerFilename = param.data() + size(schedule_update); + UpdateState::store([=](UpdateState& state) { state.pending_update = true; + state.pending_installer_filename = installerFilename; }); return toast_notification_handler_result::exit_success; diff --git a/src/runner/update_state.cpp b/src/runner/update_state.cpp index d52b5f86d0..9f91eb41d6 100644 --- a/src/runner/update_state.cpp +++ b/src/runner/update_state.cpp @@ -17,6 +17,7 @@ UpdateState deserialize(const json::JsonObject& json) result.github_update_last_checked_date = timeutil::from_string(json.GetNamedString(L"github_update_last_checked_date", L"invalid").c_str()); result.pending_update = json.GetNamedBoolean(L"pending_update", false); + result.pending_installer_filename = json.GetNamedString(L"pending_installer_filename", L""); return result; } @@ -29,6 +30,7 @@ json::JsonObject serialize(const UpdateState& state) json.SetNamedValue(L"github_update_last_checked_date", json::value(timeutil::to_string(*state.github_update_last_checked_date))); } json.SetNamedValue(L"pending_update", json::value(state.pending_update)); + json.SetNamedValue(L"pending_installer_filename", json::value(state.pending_installer_filename)); return json; } diff --git a/src/runner/update_state.h b/src/runner/update_state.h index d88bf1323d..8f24de3a7c 100644 --- a/src/runner/update_state.h +++ b/src/runner/update_state.h @@ -9,6 +9,7 @@ struct UpdateState { std::optional github_update_last_checked_date; bool pending_update = false; + std::wstring pending_installer_filename; // To prevent concurrent modification of the file, we enforce this interface, which locks the file while // the state_modifier is active. diff --git a/src/runner/update_utils.cpp b/src/runner/update_utils.cpp index 8ab13a19b8..266782afe1 100644 --- a/src/runner/update_utils.cpp +++ b/src/runner/update_utils.cpp @@ -75,8 +75,13 @@ bool launch_pending_update() { UpdateState::store([](UpdateState& state) { state.pending_update = false; + state.pending_installer_filename = {}; }); - launch_action_runner(UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG); + std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG }; + args += L' '; + args += update_state.pending_installer_filename; + + launch_action_runner(args.c_str()); return true; } }