mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +02:00
bootstrapper: use WinAPI progress bar window instead of toast notific… (#8210)
* bootstrapper: use WinAPI progress bar window instead of toast notifications * remove obsolete msi action
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
#include <common/common.h>
|
||||
#include <common/notifications.h>
|
||||
#include <common/RcResource.h>
|
||||
#include <common/updating/dotnet_installation.h>
|
||||
#include <common/updating/installer.h>
|
||||
@@ -12,6 +11,8 @@
|
||||
|
||||
#include <runner/action_runner_utils.h>
|
||||
|
||||
#include "progressbar_window.h"
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
auto Strings = create_notifications_strings();
|
||||
@@ -22,7 +23,7 @@ auto Strings = create_notifications_strings();
|
||||
namespace // Strings in this namespace should not be localized
|
||||
{
|
||||
const wchar_t APPLICATION_ID[] = L"PowerToysInstaller";
|
||||
const wchar_t INSTALLATION_TOAST_TITLE[] = L"PowerToys Installation";
|
||||
const wchar_t INSTALLATION_MSGBOX_TITLE[] = L"PowerToys Installation";
|
||||
const wchar_t TOAST_TAG[] = L"PowerToysInstallerProgress";
|
||||
const char LOG_FILENAME[] = "powertoys-bootstrapper-" STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_REVISION) ".log";
|
||||
const char MSI_LOG_FILENAME[] = "powertoys-bootstrapper-msi-" STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_REVISION) ".log";
|
||||
@@ -44,17 +45,6 @@ std::optional<fs::path> extractEmbeddedInstaller()
|
||||
return executableRes->saveAsFile(installerPath) ? std::make_optional(std::move(installerPath)) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<fs::path> extractIcon()
|
||||
{
|
||||
auto iconRes = RcResource::create(IDR_BIN_ICON, L"BIN");
|
||||
if (!iconRes)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto icoPath = fs::temp_directory_path() / L"PowerToysBootstrappedInstaller.ico";
|
||||
return iconRes->saveAsFile(icoPath) ? std::make_optional(std::move(icoPath)) : std::nullopt;
|
||||
}
|
||||
|
||||
void setup_log(fs::path directory, const spdlog::level::level_enum severity)
|
||||
{
|
||||
try
|
||||
@@ -84,7 +74,15 @@ void setup_log(fs::path directory, const spdlog::level::level_enum severity)
|
||||
}
|
||||
}
|
||||
|
||||
int bootstrapper()
|
||||
void show_error_box(const wchar_t* message, const wchar_t* title)
|
||||
{
|
||||
MessageBoxW(nullptr,
|
||||
message,
|
||||
title,
|
||||
MB_OK | MB_ICONERROR);
|
||||
}
|
||||
|
||||
int bootstrapper(HINSTANCE hInstance)
|
||||
{
|
||||
winrt::init_apartment();
|
||||
cxxopts::Options options{ "PowerToysBootstrapper" };
|
||||
@@ -233,19 +231,6 @@ int bootstrapper()
|
||||
spdlog::error("Couldn't acquire PowerToys global mutex. That means setup couldn't kill PowerToys.exe process");
|
||||
return 1;
|
||||
}
|
||||
notifications::override_application_id(APPLICATION_ID);
|
||||
spdlog::debug("Extracting icon for toast notifications");
|
||||
fs::path iconPath{ L"C:\\" };
|
||||
if (auto extractedIcon = extractIcon())
|
||||
{
|
||||
iconPath = std::move(*extractedIcon);
|
||||
}
|
||||
spdlog::debug("Registering app id for toast notifications");
|
||||
notifications::register_application_id(INSTALLATION_TOAST_TITLE, iconPath.c_str());
|
||||
|
||||
auto removeShortcut = wil::scope_exit([&] {
|
||||
notifications::unregister_application_id();
|
||||
});
|
||||
|
||||
// Check if there's a newer version installed, and launch its installer if so.
|
||||
const VersionHelper myVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
@@ -260,58 +245,13 @@ int bootstrapper()
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex progressLock;
|
||||
notifications::progress_bar_params progressParams;
|
||||
progressParams.progress = 0.0f;
|
||||
progressParams.progress_title = GET_RESOURCE_STRING(IDS_EXTRACTING_INSTALLER);
|
||||
notifications::toast_params params{ TOAST_TAG, false, std::move(progressParams) };
|
||||
if (!silent)
|
||||
{
|
||||
spdlog::debug("Launching progress toast notification");
|
||||
notifications::show_toast_with_activations({}, INSTALLATION_TOAST_TITLE, {}, {}, std::move(params));
|
||||
}
|
||||
|
||||
auto processToasts = wil::scope_exit([&] {
|
||||
spdlog::debug("Processing HWND messages for 2s so toast have time to show up");
|
||||
run_message_loop(true, 2);
|
||||
});
|
||||
|
||||
if (!silent)
|
||||
{
|
||||
// Worker thread to periodically increase progress and keep the progress toast from losing focus
|
||||
std::thread{ [&] {
|
||||
spdlog::debug("Started worker thread for progress bar update");
|
||||
for (;; Sleep(3000))
|
||||
{
|
||||
std::scoped_lock lock{ progressLock };
|
||||
if (progressParams.progress == 1.f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
progressParams.progress = std::min(0.99f, progressParams.progress + 0.001f);
|
||||
notifications::update_toast_progress_bar(TOAST_TAG, progressParams);
|
||||
}
|
||||
} }.detach();
|
||||
}
|
||||
|
||||
auto updateProgressBar = [&](const float value, const wchar_t* title) {
|
||||
if (silent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lock{ progressLock };
|
||||
progressParams.progress = value;
|
||||
progressParams.progress_title = title;
|
||||
notifications::update_toast_progress_bar(TOAST_TAG, progressParams);
|
||||
};
|
||||
|
||||
spdlog::debug("Extracting embedded MSI installer");
|
||||
const auto installerPath = extractEmbeddedInstaller();
|
||||
if (!installerPath)
|
||||
{
|
||||
if (!silent)
|
||||
{
|
||||
notifications::show_toast(GET_RESOURCE_STRING(IDS_INSTALLER_EXTRACT_ERROR), INSTALLATION_TOAST_TITLE);
|
||||
show_error_box(GET_RESOURCE_STRING(IDS_INSTALLER_EXTRACT_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE);
|
||||
}
|
||||
spdlog::error("Couldn't install the MSI installer ({})", GetLastError());
|
||||
return 1;
|
||||
@@ -321,7 +261,6 @@ int bootstrapper()
|
||||
fs::remove(*installerPath, _);
|
||||
});
|
||||
|
||||
updateProgressBar(.25f, GET_RESOURCE_STRING(IDS_UNINSTALLING_PREVIOUS_VERSION).c_str());
|
||||
spdlog::debug("Acquiring existing MSI package path");
|
||||
const auto package_path = updating::get_msi_package_path();
|
||||
if (!package_path.empty())
|
||||
@@ -332,15 +271,18 @@ int bootstrapper()
|
||||
{
|
||||
spdlog::debug("Existing MSI package path not found");
|
||||
}
|
||||
if (!package_path.empty() && !updating::uninstall_msi_version(package_path, Strings) && !silent)
|
||||
if (!package_path.empty() && !updating::uninstall_msi_version(package_path, Strings))
|
||||
{
|
||||
spdlog::error("Couldn't install the existing MSI package ({})", GetLastError());
|
||||
notifications::show_toast(GET_RESOURCE_STRING(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR), INSTALLATION_TOAST_TITLE);
|
||||
if (!silent)
|
||||
{
|
||||
show_error_box(GET_RESOURCE_STRING(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE);
|
||||
}
|
||||
}
|
||||
const bool installDotnet = !skipDotnetInstall;
|
||||
if (installDotnet)
|
||||
if (!silent)
|
||||
{
|
||||
updateProgressBar(.5f, GET_RESOURCE_STRING(IDS_INSTALLING_DOTNET).c_str());
|
||||
open_progressbar_window(hInstance, 0, GET_RESOURCE_STRING(IDS_BOOTSTRAPPER_PROGRESS_TITLE).c_str(), GET_RESOURCE_STRING(IDS_DOWNLOADING_DOTNET).c_str());
|
||||
}
|
||||
|
||||
try
|
||||
@@ -350,11 +292,31 @@ int bootstrapper()
|
||||
spdlog::debug("Detecting if dotnet is installed");
|
||||
const bool dotnetInstalled = updating::dotnet_is_installed();
|
||||
spdlog::debug("Dotnet is installed: {}", dotnetInstalled);
|
||||
if (!dotnetInstalled &&
|
||||
!updating::install_dotnet(silent) &&
|
||||
!silent)
|
||||
if (!dotnetInstalled)
|
||||
{
|
||||
notifications::show_toast(GET_RESOURCE_STRING(IDS_DOTNET_INSTALL_ERROR), INSTALLATION_TOAST_TITLE);
|
||||
bool installed_successfully = false;
|
||||
if (const auto dotnet_installer_path = updating::download_dotnet())
|
||||
{
|
||||
// Dotnet installer has its own progress bar
|
||||
close_progressbar_window();
|
||||
installed_successfully = updating::install_dotnet(*dotnet_installer_path, silent);
|
||||
if (!installed_successfully)
|
||||
{
|
||||
spdlog::error("Couldn't install dotnet");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Couldn't download dotnet");
|
||||
}
|
||||
|
||||
if (!installed_successfully)
|
||||
{
|
||||
if (!silent)
|
||||
{
|
||||
show_error_box(GET_RESOURCE_STRING(IDS_DOTNET_INSTALL_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,19 +326,19 @@ int bootstrapper()
|
||||
MessageBoxW(nullptr, L".NET Core installation", L"Unknown exception encountered!", MB_OK | MB_ICONERROR);
|
||||
}
|
||||
|
||||
updateProgressBar(.75f, GET_RESOURCE_STRING(IDS_INSTALLING_NEW_VERSION).c_str());
|
||||
// At this point, there's no reason to show progress bar window, since MSI installers have their own
|
||||
close_progressbar_window();
|
||||
|
||||
// Always skip dotnet install, because we should've installed it from here earlier
|
||||
std::wstring msiProps = L"SKIPDOTNETINSTALL=1 ";
|
||||
spdlog::debug("Launching MSI installation for new package {}", installerPath->string());
|
||||
const bool installationDone = MsiInstallProductW(installerPath->c_str(), msiProps.c_str()) == ERROR_SUCCESS;
|
||||
updateProgressBar(1.f,
|
||||
installationDone ? GET_RESOURCE_STRING(IDS_NEW_VERSION_INSTALLATION_DONE).c_str() : GET_RESOURCE_STRING(IDS_NEW_VERSION_INSTALLATION_ERROR).c_str());
|
||||
if (!installationDone)
|
||||
{
|
||||
spdlog::error("Couldn't install new MSI package ({})", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
spdlog::debug("Installation completed");
|
||||
|
||||
if (!noStartPT && !silent)
|
||||
@@ -399,11 +361,11 @@ int bootstrapper()
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
int WINAPI WinMain(HINSTANCE hi, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
try
|
||||
{
|
||||
return bootstrapper();
|
||||
return bootstrapper(hi);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user