mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-06-07 09:28:03 +08: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:
parent
9757a4574f
commit
9735459552
10
.github/actions/spell-check/expect.txt
vendored
10
.github/actions/spell-check/expect.txt
vendored
@ -16,6 +16,7 @@ Acceleratorkeys
|
|||||||
ACCEPTFILES
|
ACCEPTFILES
|
||||||
accessibile
|
accessibile
|
||||||
accessibilityinsights
|
accessibilityinsights
|
||||||
|
acf
|
||||||
Acl
|
Acl
|
||||||
aclapi
|
aclapi
|
||||||
AColumn
|
AColumn
|
||||||
@ -154,6 +155,7 @@ bc
|
|||||||
bcc
|
bcc
|
||||||
bck
|
bck
|
||||||
Bcl
|
Bcl
|
||||||
|
bdaa
|
||||||
bddac
|
bddac
|
||||||
BEGINLABELEDIT
|
BEGINLABELEDIT
|
||||||
betadele
|
betadele
|
||||||
@ -955,6 +957,7 @@ IBackground
|
|||||||
IBeam
|
IBeam
|
||||||
IBind
|
IBind
|
||||||
icase
|
icase
|
||||||
|
iccex
|
||||||
IClass
|
IClass
|
||||||
ico
|
ico
|
||||||
ICollection
|
ICollection
|
||||||
@ -1045,6 +1048,7 @@ Infotip
|
|||||||
ingbuffer
|
ingbuffer
|
||||||
inheritdoc
|
inheritdoc
|
||||||
ini
|
ini
|
||||||
|
INITCOMMONCONTROLSEX
|
||||||
INITDIALOG
|
INITDIALOG
|
||||||
INITGUID
|
INITGUID
|
||||||
inl
|
inl
|
||||||
@ -1328,6 +1332,7 @@ makeappx
|
|||||||
MAKEINTRESOURCE
|
MAKEINTRESOURCE
|
||||||
MAKEINTRESOURCEW
|
MAKEINTRESOURCEW
|
||||||
MAKELANGID
|
MAKELANGID
|
||||||
|
MAKELPARAM
|
||||||
makepri
|
makepri
|
||||||
malloc
|
malloc
|
||||||
manifestdependency
|
manifestdependency
|
||||||
@ -1424,6 +1429,7 @@ MSIRESTARTMANAGERCONTROL
|
|||||||
msix
|
msix
|
||||||
msixbundle
|
msixbundle
|
||||||
MSIXVERSION
|
MSIXVERSION
|
||||||
|
MSGBOX
|
||||||
MSLLHOOKSTRUCT
|
MSLLHOOKSTRUCT
|
||||||
Mso
|
Mso
|
||||||
msp
|
msp
|
||||||
@ -1450,6 +1456,7 @@ NAMECHANGE
|
|||||||
nameof
|
nameof
|
||||||
NAMEONLY
|
NAMEONLY
|
||||||
namespace
|
namespace
|
||||||
|
NATIVEFNTCTL
|
||||||
nc
|
nc
|
||||||
NCACTIVATE
|
NCACTIVATE
|
||||||
ncc
|
ncc
|
||||||
@ -1979,8 +1986,10 @@ SETICON
|
|||||||
setings
|
setings
|
||||||
setlocal
|
setlocal
|
||||||
setnt
|
setnt
|
||||||
|
SETRANGE
|
||||||
Setrect
|
Setrect
|
||||||
SETREDRAW
|
SETREDRAW
|
||||||
|
SETSTEP
|
||||||
SETTEXT
|
SETTEXT
|
||||||
SETTINGCHANGE
|
SETTINGCHANGE
|
||||||
settingsheader
|
settingsheader
|
||||||
@ -2130,6 +2139,7 @@ STDMETHODIMP
|
|||||||
stdout
|
stdout
|
||||||
Stdout
|
Stdout
|
||||||
stefan
|
stefan
|
||||||
|
STEPIT
|
||||||
stgm
|
stgm
|
||||||
STGMEDIUM
|
STGMEDIUM
|
||||||
stoi
|
stoi
|
||||||
|
@ -58,6 +58,9 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<data name="BOOTSTRAPPER_PROGRESS_TITLE" xml:space="preserve">
|
||||||
|
<value>PowerToys installer</value>
|
||||||
|
</data>
|
||||||
<data name="DOTNET_CORE_DOWNLOAD_FAILURE" xml:space="preserve">
|
<data name="DOTNET_CORE_DOWNLOAD_FAILURE" xml:space="preserve">
|
||||||
<value>Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.</value>
|
<value>Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.</value>
|
||||||
</data>
|
</data>
|
||||||
@ -130,24 +133,15 @@
|
|||||||
<data name="EXTRACTING_INSTALLER" xml:space="preserve">
|
<data name="EXTRACTING_INSTALLER" xml:space="preserve">
|
||||||
<value>Extracting PowerToys MSI...</value>
|
<value>Extracting PowerToys MSI...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="UNINSTALLING_PREVIOUS_VERSION" xml:space="preserve">
|
|
||||||
<value>Uninstalling previous PowerToys version...</value>
|
|
||||||
</data>
|
|
||||||
<data name="UNINSTALL_PREVIOUS_VERSION_ERROR" xml:space="preserve">
|
<data name="UNINSTALL_PREVIOUS_VERSION_ERROR" xml:space="preserve">
|
||||||
<value>Couldn't uninstall previous PowerToys version!</value>
|
<value>Couldn't uninstall previous PowerToys version!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="INSTALLING_DOTNET" xml:space="preserve">
|
<data name="DOWNLOADING_DOTNET" xml:space="preserve">
|
||||||
<value>Installing dotnet...</value>
|
<value>Downloading .NET Core Runtime...</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="DOTNET_INSTALL_ERROR" xml:space="preserve">
|
<data name="DOTNET_INSTALL_ERROR" xml:space="preserve">
|
||||||
<value>Couldn't install dotnet!</value>
|
<value>Couldn't install dotnet!</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="INSTALLING_NEW_VERSION" xml:space="preserve">
|
|
||||||
<value>Installing new PowerToys version...</value>
|
|
||||||
</data>
|
|
||||||
<data name="NEW_VERSION_INSTALLATION_DONE" xml:space="preserve">
|
|
||||||
<value>PowerToys installation complete!</value>
|
|
||||||
</data>
|
|
||||||
<data name="NEW_VERSION_INSTALLATION_ERROR" xml:space="preserve">
|
<data name="NEW_VERSION_INSTALLATION_ERROR" xml:space="preserve">
|
||||||
<value>Couldn't install new PowerToys version.</value>
|
<value>Couldn't install new PowerToys version.</value>
|
||||||
</data>
|
</data>
|
||||||
@ -160,5 +154,5 @@
|
|||||||
</data>
|
</data>
|
||||||
<data name="GITHUB_NEW_VERSION_CHECK_ERROR" xml:space="preserve">
|
<data name="GITHUB_NEW_VERSION_CHECK_ERROR" xml:space="preserve">
|
||||||
<value>Failed to connect to the server. Check your network connection or retry later.</value>
|
<value>Failed to connect to the server. Check your network connection or retry later.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "Generated Files/resource.h"
|
#include "Generated Files/resource.h"
|
||||||
|
|
||||||
#include <common/common.h>
|
#include <common/common.h>
|
||||||
#include <common/notifications.h>
|
|
||||||
#include <common/RcResource.h>
|
#include <common/RcResource.h>
|
||||||
#include <common/updating/dotnet_installation.h>
|
#include <common/updating/dotnet_installation.h>
|
||||||
#include <common/updating/installer.h>
|
#include <common/updating/installer.h>
|
||||||
@ -12,6 +11,8 @@
|
|||||||
|
|
||||||
#include <runner/action_runner_utils.h>
|
#include <runner/action_runner_utils.h>
|
||||||
|
|
||||||
|
#include "progressbar_window.h"
|
||||||
|
|
||||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||||
|
|
||||||
auto Strings = create_notifications_strings();
|
auto Strings = create_notifications_strings();
|
||||||
@ -22,7 +23,7 @@ auto Strings = create_notifications_strings();
|
|||||||
namespace // Strings in this namespace should not be localized
|
namespace // Strings in this namespace should not be localized
|
||||||
{
|
{
|
||||||
const wchar_t APPLICATION_ID[] = L"PowerToysInstaller";
|
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 wchar_t TOAST_TAG[] = L"PowerToysInstallerProgress";
|
||||||
const char LOG_FILENAME[] = "powertoys-bootstrapper-" STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_REVISION) ".log";
|
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";
|
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;
|
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)
|
void setup_log(fs::path directory, const spdlog::level::level_enum severity)
|
||||||
{
|
{
|
||||||
try
|
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();
|
winrt::init_apartment();
|
||||||
cxxopts::Options options{ "PowerToysBootstrapper" };
|
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");
|
spdlog::error("Couldn't acquire PowerToys global mutex. That means setup couldn't kill PowerToys.exe process");
|
||||||
return 1;
|
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.
|
// Check if there's a newer version installed, and launch its installer if so.
|
||||||
const VersionHelper myVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
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");
|
spdlog::debug("Extracting embedded MSI installer");
|
||||||
const auto installerPath = extractEmbeddedInstaller();
|
const auto installerPath = extractEmbeddedInstaller();
|
||||||
if (!installerPath)
|
if (!installerPath)
|
||||||
{
|
{
|
||||||
if (!silent)
|
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());
|
spdlog::error("Couldn't install the MSI installer ({})", GetLastError());
|
||||||
return 1;
|
return 1;
|
||||||
@ -321,7 +261,6 @@ int bootstrapper()
|
|||||||
fs::remove(*installerPath, _);
|
fs::remove(*installerPath, _);
|
||||||
});
|
});
|
||||||
|
|
||||||
updateProgressBar(.25f, GET_RESOURCE_STRING(IDS_UNINSTALLING_PREVIOUS_VERSION).c_str());
|
|
||||||
spdlog::debug("Acquiring existing MSI package path");
|
spdlog::debug("Acquiring existing MSI package path");
|
||||||
const auto package_path = updating::get_msi_package_path();
|
const auto package_path = updating::get_msi_package_path();
|
||||||
if (!package_path.empty())
|
if (!package_path.empty())
|
||||||
@ -332,15 +271,18 @@ int bootstrapper()
|
|||||||
{
|
{
|
||||||
spdlog::debug("Existing MSI package path not found");
|
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());
|
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;
|
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
|
try
|
||||||
@ -350,11 +292,31 @@ int bootstrapper()
|
|||||||
spdlog::debug("Detecting if dotnet is installed");
|
spdlog::debug("Detecting if dotnet is installed");
|
||||||
const bool dotnetInstalled = updating::dotnet_is_installed();
|
const bool dotnetInstalled = updating::dotnet_is_installed();
|
||||||
spdlog::debug("Dotnet is installed: {}", dotnetInstalled);
|
spdlog::debug("Dotnet is installed: {}", dotnetInstalled);
|
||||||
if (!dotnetInstalled &&
|
if (!dotnetInstalled)
|
||||||
!updating::install_dotnet(silent) &&
|
|
||||||
!silent)
|
|
||||||
{
|
{
|
||||||
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);
|
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
|
// Always skip dotnet install, because we should've installed it from here earlier
|
||||||
std::wstring msiProps = L"SKIPDOTNETINSTALL=1 ";
|
std::wstring msiProps = L"SKIPDOTNETINSTALL=1 ";
|
||||||
spdlog::debug("Launching MSI installation for new package {}", installerPath->string());
|
spdlog::debug("Launching MSI installation for new package {}", installerPath->string());
|
||||||
const bool installationDone = MsiInstallProductW(installerPath->c_str(), msiProps.c_str()) == ERROR_SUCCESS;
|
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)
|
if (!installationDone)
|
||||||
{
|
{
|
||||||
spdlog::error("Couldn't install new MSI package ({})", GetLastError());
|
spdlog::error("Couldn't install new MSI package ({})", GetLastError());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
spdlog::debug("Installation completed");
|
spdlog::debug("Installation completed");
|
||||||
|
|
||||||
if (!noStartPT && !silent)
|
if (!noStartPT && !silent)
|
||||||
@ -399,11 +361,11 @@ int bootstrapper()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
int WINAPI WinMain(HINSTANCE hi, HINSTANCE, LPSTR, int)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return bootstrapper();
|
return bootstrapper(hi);
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
|
@ -90,7 +90,7 @@
|
|||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;Comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
@ -110,7 +110,7 @@
|
|||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;Comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -119,10 +119,12 @@
|
|||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="progressbar_window.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\runner\updating.h" />
|
<ClInclude Include="..\runner\updating.h" />
|
||||||
<ClInclude Include="pch.h" />
|
<ClInclude Include="pch.h" />
|
||||||
|
<ClInclude Include="progressbar_window.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <commctrl.h>
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -12,6 +13,7 @@
|
|||||||
#include <Msi.h>
|
#include <Msi.h>
|
||||||
|
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <thread>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -0,0 +1,150 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <common/common.h>
|
||||||
|
|
||||||
|
#include "progressbar_window.h"
|
||||||
|
#include "Generated Files/resource.h"
|
||||||
|
|
||||||
|
const int label_height = 20;
|
||||||
|
|
||||||
|
const int progress_bar_height = 15;
|
||||||
|
const int progress_bar_margin = 10;
|
||||||
|
|
||||||
|
const int window_width = 450;
|
||||||
|
const int title_bar_height = 32;
|
||||||
|
const int window_height = progress_bar_margin * 3 + progress_bar_height + label_height + title_bar_height;
|
||||||
|
|
||||||
|
int progressbar_steps = 0;
|
||||||
|
|
||||||
|
HWND progress_bar;
|
||||||
|
HWND main_window;
|
||||||
|
HWND label;
|
||||||
|
|
||||||
|
std::wstring initial_label;
|
||||||
|
std::mutex ui_thread_is_running;
|
||||||
|
|
||||||
|
namespace nonlocalized
|
||||||
|
{
|
||||||
|
const wchar_t window_class[] = L"PTBProgressBarWnd";
|
||||||
|
const wchar_t label_class[] = L"static";
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||||
|
|
||||||
|
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
switch (Msg)
|
||||||
|
{
|
||||||
|
case WM_CREATE:
|
||||||
|
{
|
||||||
|
ui_thread_is_running.lock();
|
||||||
|
label = CreateWindowW(nonlocalized::label_class, initial_label.c_str(), WS_CHILD | WS_VISIBLE | WS_TABSTOP, progress_bar_margin, 0, window_width - progress_bar_margin * 4, label_height, hWnd, (HMENU)(501), (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE), nullptr);
|
||||||
|
|
||||||
|
progress_bar = CreateWindowExW(0,
|
||||||
|
PROGRESS_CLASS,
|
||||||
|
nullptr,
|
||||||
|
WS_VISIBLE | WS_CHILD | PBS_SMOOTH,
|
||||||
|
progress_bar_margin,
|
||||||
|
progress_bar_margin + label_height,
|
||||||
|
window_width - progress_bar_margin * 4,
|
||||||
|
progress_bar_height,
|
||||||
|
hWnd,
|
||||||
|
(HMENU)(IDR_PROGRESS_BAR),
|
||||||
|
(HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
bool filled_on_start = false;
|
||||||
|
if (progressbar_steps == 0)
|
||||||
|
{
|
||||||
|
progressbar_steps = 1;
|
||||||
|
filled_on_start = true;
|
||||||
|
}
|
||||||
|
SendMessageW(progress_bar, PBM_SETRANGE, 0, MAKELPARAM(0, progressbar_steps));
|
||||||
|
SendMessageW(progress_bar, PBM_SETSTEP, 1, 0);
|
||||||
|
if (filled_on_start)
|
||||||
|
{
|
||||||
|
SendMessageW(progress_bar, PBM_STEPIT, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WM_CLOSE:
|
||||||
|
DestroyWindow(hWnd);
|
||||||
|
PostQuitMessage(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return DefWindowProcW(hWnd, Msg, wParam, lParam);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_progressbar_window(HINSTANCE hInstance, const int n_progressbar_steps, const wchar_t* title, const wchar_t* init_label)
|
||||||
|
{
|
||||||
|
initial_label = init_label;
|
||||||
|
progressbar_steps = n_progressbar_steps;
|
||||||
|
std::wstring window_title{ title };
|
||||||
|
std::thread{
|
||||||
|
[hInstance, window_title = std::move(window_title)] {
|
||||||
|
INITCOMMONCONTROLSEX iccex{ .dwSize = sizeof(iccex), .dwICC = ICC_NATIVEFNTCTL_CLASS | ICC_PROGRESS_CLASS };
|
||||||
|
InitCommonControlsEx(&iccex);
|
||||||
|
|
||||||
|
WNDCLASSEX wc{};
|
||||||
|
wc.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wc.lpfnWndProc = WndProc;
|
||||||
|
wc.hInstance = hInstance;
|
||||||
|
wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON));
|
||||||
|
wc.hIconSm = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON));
|
||||||
|
wc.lpszClassName = nonlocalized::window_class;
|
||||||
|
if (!RegisterClassExW(&wc))
|
||||||
|
{
|
||||||
|
spdlog::warn("Couldn't register main_window class for progress bar.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RECT rect{};
|
||||||
|
GetClientRect(GetDesktopWindow(), &rect);
|
||||||
|
rect.left = rect.right / 2 - window_width / 2;
|
||||||
|
rect.top = rect.bottom / 4 - window_height / 2;
|
||||||
|
main_window = CreateWindowExW(WS_EX_CLIENTEDGE,
|
||||||
|
nonlocalized::window_class,
|
||||||
|
window_title.c_str(),
|
||||||
|
WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX,
|
||||||
|
rect.left,
|
||||||
|
rect.top,
|
||||||
|
window_width,
|
||||||
|
window_height,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
hInstance,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
if (!main_window)
|
||||||
|
{
|
||||||
|
spdlog::warn("Couldn't create progress bar main_window");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ShowWindow(main_window, SW_SHOW);
|
||||||
|
UpdateWindow(main_window);
|
||||||
|
run_message_loop();
|
||||||
|
ui_thread_is_running.unlock();
|
||||||
|
}
|
||||||
|
}.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick_progressbar_window(const wchar_t* new_status)
|
||||||
|
{
|
||||||
|
SetWindowTextW(label, new_status);
|
||||||
|
SendMessageW(progress_bar, PBM_STEPIT, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_progressbar_window()
|
||||||
|
{
|
||||||
|
SendMessageW(main_window, WM_CLOSE, {}, {});
|
||||||
|
{
|
||||||
|
std::unique_lock wait_for_ui_to_exit{ui_thread_is_running};
|
||||||
|
}
|
||||||
|
// Return focus to the current process, since it was lost due to progress bar closing (?)
|
||||||
|
INPUT i = {INPUT_MOUSE, {}};
|
||||||
|
SendInput(1, &i, sizeof(i));
|
||||||
|
SetForegroundWindow(GetActiveWindow());
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
void open_progressbar_window(HINSTANCE hInstance, const int n_progressbar_steps, const wchar_t* title, const wchar_t* init_label);
|
||||||
|
void tick_progressbar_window(const wchar_t* new_status);
|
||||||
|
void close_progressbar_window();
|
@ -14,3 +14,4 @@
|
|||||||
|
|
||||||
#define IDR_BIN_MSIINSTALLER 103
|
#define IDR_BIN_MSIINSTALLER 103
|
||||||
#define IDR_BIN_ICON 104
|
#define IDR_BIN_ICON 104
|
||||||
|
#define IDR_PROGRESS_BAR 105
|
||||||
|
@ -96,10 +96,6 @@
|
|||||||
Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
|
Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
|
||||||
</Custom>
|
</Custom>
|
||||||
|
|
||||||
<Custom Action="InstallDotNet" After="InstallFinalize">
|
|
||||||
NOT Installed and (SKIPDOTNETINSTALL = 0)
|
|
||||||
</Custom>
|
|
||||||
|
|
||||||
<Custom Action="TerminateProcesses" Before="InstallValidate" />
|
<Custom Action="TerminateProcesses" Before="InstallValidate" />
|
||||||
|
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
@ -112,14 +108,6 @@
|
|||||||
BinaryKey="PTCustomActions"
|
BinaryKey="PTCustomActions"
|
||||||
DllEntry="TerminateProcessesCA" />
|
DllEntry="TerminateProcessesCA" />
|
||||||
|
|
||||||
<CustomAction
|
|
||||||
Id="InstallDotNet"
|
|
||||||
FileKey="action_runner.exe"
|
|
||||||
ExeCommand="-install_dotnet"
|
|
||||||
Return="asyncNoWait"
|
|
||||||
Execute="immediate"
|
|
||||||
Impersonate="no" />
|
|
||||||
|
|
||||||
<CustomAction Id="SetRegisterPowerToysSchTaskParam"
|
<CustomAction Id="SetRegisterPowerToysSchTaskParam"
|
||||||
Property="RegisterPowerToysSchTask"
|
Property="RegisterPowerToysSchTask"
|
||||||
Value="[#PowerToys.exe]" />
|
Value="[#PowerToys.exe]" />
|
||||||
|
@ -234,21 +234,6 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action == L"-install_dotnet")
|
|
||||||
{
|
|
||||||
if (updating::dotnet_is_installed())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const bool success = updating::install_dotnet();
|
|
||||||
|
|
||||||
MessageBoxW(nullptr,
|
|
||||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE).c_str(),
|
|
||||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE).c_str(),
|
|
||||||
MB_OK | MB_ICONERROR);
|
|
||||||
|
|
||||||
return !success;
|
|
||||||
}
|
|
||||||
else if (action == L"-uninstall_msi")
|
else if (action == L"-uninstall_msi")
|
||||||
{
|
{
|
||||||
return uninstall_msi_action();
|
return uninstall_msi_action();
|
||||||
|
@ -20,9 +20,9 @@ namespace updating
|
|||||||
return runtimes->find(DESKTOP_DOTNET_RUNTIME_STRING) != std::string::npos;
|
return runtimes->find(DESKTOP_DOTNET_RUNTIME_STRING) != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool install_dotnet(const bool silent)
|
std::optional<fs::path> download_dotnet()
|
||||||
{
|
{
|
||||||
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/3eb7efa1-96c6-4e97-bb9f-563ecf595f8a/7efd9c1cdd74df8fb0a34c288138a84f/windowsdesktop-runtime-3.1.6-win-x64.exe";
|
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/513acf37-8da2-497d-bdaa-84d6e33c1fee/eb7b010350df712c752f4ec4b615f89d/windowsdesktop-runtime-3.1.10-win-x64.exe";
|
||||||
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe";
|
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe";
|
||||||
|
|
||||||
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;
|
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;
|
||||||
@ -44,10 +44,11 @@ namespace updating
|
|||||||
// couldn't download
|
// couldn't download
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!download_success)
|
return download_success ? std::make_optional(dotnet_download_path) : std::nullopt;
|
||||||
{
|
}
|
||||||
return false;
|
|
||||||
}
|
bool install_dotnet(fs::path dotnet_download_path, const bool silent = false)
|
||||||
|
{
|
||||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||||
sei.fMask = { SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
|
sei.fMask = { SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
|
||||||
sei.lpFile = dotnet_download_path.c_str();
|
sei.lpFile = dotnet_download_path.c_str();
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
namespace updating
|
namespace updating
|
||||||
{
|
{
|
||||||
bool dotnet_is_installed();
|
bool dotnet_is_installed();
|
||||||
bool install_dotnet(const bool silent = false);
|
std::optional<fs::path> download_dotnet();
|
||||||
|
bool install_dotnet(fs::path dotnet_download_path, const bool silent);
|
||||||
}
|
}
|
@ -17,4 +17,6 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <expected.hpp>
|
#include <expected.hpp>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#endif //PCH_H
|
#endif //PCH_H
|
||||||
|
Loading…
Reference in New Issue
Block a user