diff --git a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp index c05accfa94..7f118100f3 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp +++ b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp @@ -17,7 +17,6 @@ #include "progressbar_window.h" - auto Strings = create_notifications_strings(); #define STR_HELPER(x) #x @@ -37,14 +36,14 @@ namespace // Strings in this namespace should not be localized namespace fs = std::filesystem; -std::optional extractEmbeddedInstaller() +std::optional extractEmbeddedInstaller(const fs::path extractPath) { auto executableRes = RcResource::create(IDR_BIN_MSIINSTALLER, L"BIN"); if (!executableRes) { return std::nullopt; } - auto installerPath = fs::temp_directory_path() / L"PowerToysBootstrappedInstaller-" PRODUCT_VERSION_STRING L".msi"; + auto installerPath = extractPath / L"PowerToysBootstrappedInstaller-" PRODUCT_VERSION_STRING L".msi"; return executableRes->saveAsFile(installerPath) ? std::make_optional(std::move(installerPath)) : std::nullopt; } @@ -106,7 +105,8 @@ int bootstrapper(HINSTANCE hInstance) ("skip_dotnet_install", "Skip dotnet 3.X installation even if it's not detected") ("log_level", "Log level. Possible values: off|debug|error", cxxopts::value()->default_value("off")) ("log_dir", "Log directory", cxxopts::value()->default_value(".")) - ("install_dir", "Installation directory", cxxopts::value()->default_value(defaultInstallDir)); + ("install_dir", "Installation directory", cxxopts::value()->default_value(defaultInstallDir)) + ("extract_msi", "Extract MSI to the working directory and exit. Use only if you must access MSI directly."); // clang-format on cxxopts::ParseResult cmdArgs; bool showHelp = false; @@ -127,6 +127,7 @@ int bootstrapper(HINSTANCE hInstance) MessageBoxA(nullptr, helpMsg.str().c_str(), "Help", MB_OK | MB_ICONINFORMATION); return 0; } + const bool noFullUI = cmdArgs["no_full_ui"].as(); const bool silent = cmdArgs["silent"].as(); const bool skipDotnetInstall = cmdArgs["skip_dotnet_install"].as(); @@ -134,6 +135,8 @@ int bootstrapper(HINSTANCE hInstance) const auto logLevel = cmdArgs["log_level"].as(); const auto logDirArg = cmdArgs["log_dir"].as(); const auto installDirArg = cmdArgs["install_dir"].as(); + const bool extract_msi_only = cmdArgs["extract_msi"].as(); + spdlog::level::level_enum severity = spdlog::level::off; std::wstring installFolderProp; @@ -176,8 +179,23 @@ int bootstrapper(HINSTANCE hInstance) severity = spdlog::level::err; } setup_log(logDir, severity); - spdlog::debug("PowerToys Bootstrapper is launched!\nnoFullUI: {}\nsilent: {}\nno_start_pt: {}\nskip_dotnet_install: {}\nlog_level: {}\ninstall_dir: {}", noFullUI, silent, noStartPT, skipDotnetInstall, logLevel, installDirArg); + spdlog::debug("PowerToys Bootstrapper is launched!\nnoFullUI: {}\nsilent: {}\nno_start_pt: {}\nskip_dotnet_install: {}\nlog_level: {}\ninstall_dir: {}\nextract_msi: {}\n", noFullUI, silent, noStartPT, skipDotnetInstall, logLevel, installDirArg, extract_msi_only); + + // If a user requested an MSI -> extract it and exit + if (extract_msi_only) + { + if (const auto installerPath = extractEmbeddedInstaller(fs::current_path())) + { + spdlog::info("MSI installer was extracted to {}", installerPath->string()); + } + else + { + spdlog::error("MSI installer couldn't be extracted"); + } + return 0; + } + // Setup MSI UI visibility and restart as elevated if required if (!noFullUI) { MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr); @@ -236,13 +254,12 @@ int bootstrapper(HINSTANCE hInstance) } } - // Try killing PowerToys and prevent future processes launch + // Try killing PowerToys and prevent future processes launch by acquiring app mutex for (auto& handle : getProcessHandlesByName(L"PowerToys.exe", PROCESS_TERMINATE)) { TerminateProcess(handle.get(), 0); } auto powerToysMutex = createAppMutex(POWERTOYS_MSI_MUTEX_NAME); - auto instanceMutex = createAppMutex(POWERTOYS_BOOTSTRAPPER_MUTEX_NAME); if (!instanceMutex) { @@ -264,7 +281,7 @@ int bootstrapper(HINSTANCE hInstance) } spdlog::debug("Extracting embedded MSI installer"); - const auto installerPath = extractEmbeddedInstaller(); + const auto installerPath = extractEmbeddedInstaller(fs::temp_directory_path()); if (!installerPath) { if (!silent) @@ -309,7 +326,7 @@ int bootstrapper(HINSTANCE hInstance) { spdlog::debug("Detecting if dotnet is installed"); const bool dotnetInstalled = updating::dotnet_is_installed(); - spdlog::debug("Dotnet is installed: {}", dotnetInstalled); + spdlog::debug("Dotnet is already installed: {}", dotnetInstalled); if (!dotnetInstalled) { bool installed_successfully = false; @@ -347,8 +364,7 @@ int bootstrapper(HINSTANCE hInstance) // 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 " + installFolderProp; + const std::wstring msiProps = installFolderProp; spdlog::debug("Launching MSI installation for new package {}", installerPath->string()); const bool installationDone = MsiInstallProductW(installerPath->c_str(), msiProps.c_str()) == ERROR_SUCCESS; if (!installationDone) diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index bd976f35e8..da2270e918 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -70,7 +70,6 @@ - diff --git a/src/action_runner/action_runner.cpp b/src/action_runner/action_runner.cpp index a7157efd79..f1501ec2c9 100644 --- a/src/action_runner/action_runner.cpp +++ b/src/action_runner/action_runner.cpp @@ -173,7 +173,7 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) } std::wstring_view action{ args[1] }; - if (action == L"-run-non-elevated") + if (action == RUN_NONELEVATED_CMDARG) { int nextArg = 2; @@ -235,7 +235,7 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) } } } - else if (action == L"-uninstall_msi") + else if (action == UNINSTALL_MSI_CMDARG) { return uninstall_msi_action(); } diff --git a/src/runner/action_runner_utils.h b/src/runner/action_runner_utils.h index 67ef54b8ec..13e5353869 100644 --- a/src/runner/action_runner_utils.h +++ b/src/runner/action_runner_utils.h @@ -12,4 +12,8 @@ const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE2_CMDARG = L"-update_now_stage_2"; const inline wchar_t* UPDATE_STAGE2_RESTART_PT_CMDARG = L"restart"; const inline wchar_t* UPDATE_STAGE2_DONT_START_PT_CMDARG = L"dont_start"; +const inline wchar_t * UNINSTALL_MSI_CMDARG = L"-uninstall_msi"; +const inline wchar_t * RUN_NONELEVATED_CMDARG = L"-run-non-elevated"; + const inline wchar_t* UPDATE_REPORT_SUCCESS = L"-report_update_success"; +