bootstrapper: add support for silent arg (#5590)

This commit is contained in:
Andrey Nekrasov
2020-08-07 12:00:14 +03:00
committed by GitHub
parent 8f98866d71
commit bd80bb6bb3
5 changed files with 121 additions and 47 deletions

View File

@@ -60,11 +60,77 @@ std::optional<fs::path> extractIcon()
return iconRes->saveAsFile(icoPath) ? std::make_optional(std::move(icoPath)) : std::nullopt; return iconRes->saveAsFile(icoPath) ? std::make_optional(std::move(icoPath)) : std::nullopt;
} }
enum class CmdArgs
{
silent,
noFullUI,
noStartPT
};
std::unordered_set<CmdArgs> parseCmdArgs(const int nCmdArgs, LPWSTR* argList)
{
const std::unordered_map<std::wstring_view, CmdArgs> knownArgs = { { L"--no_full_ui", CmdArgs::noFullUI }, { L"--silent", CmdArgs::silent }, { L"--no_start_pt", CmdArgs::noStartPT } };
std::unordered_set<CmdArgs> result;
for (size_t i = 1; i < nCmdArgs; ++i)
{
if (auto it = knownArgs.find(argList[i]); it != end(knownArgs))
{
result.emplace(it->second);
}
}
return result;
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{ {
using namespace localized_strings; using namespace localized_strings;
winrt::init_apartment(); winrt::init_apartment();
int nCmdArgs = 0;
LPWSTR* argList = CommandLineToArgvW(GetCommandLineW(), &nCmdArgs);
const auto cmdArgs = parseCmdArgs(nCmdArgs, argList);
if (!cmdArgs.contains(CmdArgs::noFullUI))
{
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
}
if (cmdArgs.contains(CmdArgs::silent))
{
if (is_process_elevated())
{
MsiSetInternalUI(INSTALLUILEVEL_NONE, nullptr);
}
else
{
// MSI fails to run in silent mode due to a suppressed UAC w/o elevation, so we restart elevated
std::wstring params;
for (int i = 1; i < nCmdArgs; ++i)
{
params += argList[i];
if (i != nCmdArgs - 1)
{
params += L' ';
}
}
const auto processHandle = run_elevated(argList[0], params.c_str());
if (!processHandle)
{
return 1;
}
if (WaitForSingleObject(processHandle, 3600000) == WAIT_OBJECT_0)
{
DWORD exitCode = 0;
GetExitCodeProcess(processHandle, &exitCode);
return exitCode;
}
else
{
// Couldn't install using the completely silent mode in an hour, use basic UI.
TerminateProcess(processHandle, 0);
MsiSetInternalUI(INSTALLUILEVEL_BASIC, nullptr);
}
}
}
// Try killing PowerToys and prevent future processes launch // Try killing PowerToys and prevent future processes launch
for (auto& handle : getProcessHandlesByName(L"PowerToys.exe", PROCESS_TERMINATE)) for (auto& handle : getProcessHandlesByName(L"PowerToys.exe", PROCESS_TERMINATE))
{ {
@@ -72,10 +138,6 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
} }
auto powerToysMutex = createAppMutex(POWERTOYS_MSI_MUTEX_NAME); auto powerToysMutex = createAppMutex(POWERTOYS_MSI_MUTEX_NAME);
int n_cmd_args = 0;
LPWSTR* cmd_arg_list = CommandLineToArgvW(GetCommandLineW(), &n_cmd_args);
const bool silent = n_cmd_args > 1 && std::wstring_view{ L"-silent" } == cmd_arg_list[1];
auto instanceMutex = createAppMutex(POWERTOYS_BOOTSTRAPPER_MUTEX_NAME); auto instanceMutex = createAppMutex(POWERTOYS_BOOTSTRAPPER_MUTEX_NAME);
if (!instanceMutex) if (!instanceMutex)
{ {
@@ -102,7 +164,6 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
auto msi_path = updating::get_msi_package_path(); auto msi_path = updating::get_msi_package_path();
if (!msi_path.empty()) if (!msi_path.empty())
{ {
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
MsiInstallProductW(msi_path.c_str(), nullptr); MsiInstallProductW(msi_path.c_str(), nullptr);
return 0; return 0;
} }
@@ -113,27 +174,37 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
progressParams.progress = 0.0f; progressParams.progress = 0.0f;
progressParams.progress_title = EXTRACTING_INSTALLER; progressParams.progress_title = EXTRACTING_INSTALLER;
notifications::toast_params params{ TOAST_TAG, false, std::move(progressParams) }; notifications::toast_params params{ TOAST_TAG, false, std::move(progressParams) };
notifications::show_toast_with_activations({}, TOAST_TITLE, {}, {}, std::move(params)); if (!cmdArgs.contains(CmdArgs::silent))
{
notifications::show_toast_with_activations({}, TOAST_TITLE, {}, {}, std::move(params));
}
auto processToasts = wil::scope_exit([&] { auto processToasts = wil::scope_exit([&] {
run_message_loop(true, 2); run_message_loop(true, 2);
}); });
// Worker thread to periodically increase progress and keep the progress toast from losing focus if (!cmdArgs.contains(CmdArgs::silent))
std::thread{ [&] { {
for (;; Sleep(3000)) // Worker thread to periodically increase progress and keep the progress toast from losing focus
{ std::thread{ [&] {
std::scoped_lock lock{ progressLock }; for (;; Sleep(3000))
if (progressParams.progress == 1.f)
{ {
break; std::scoped_lock lock{ progressLock };
if (progressParams.progress == 1.f)
{
break;
}
progressParams.progress = min(0.99f, progressParams.progress + 0.001f);
notifications::update_progress_bar_toast(TOAST_TAG, progressParams);
} }
progressParams.progress = min(0.99f, progressParams.progress + 0.001f); } }.detach();
notifications::update_progress_bar_toast(TOAST_TAG, progressParams); }
}
} }.detach();
auto updateProgressBar = [&](const float value, const wchar_t* title) { auto updateProgressBar = [&](const float value, const wchar_t* title) {
if (cmdArgs.contains(CmdArgs::silent))
{
return;
}
std::scoped_lock lock{ progressLock }; std::scoped_lock lock{ progressLock };
progressParams.progress = value; progressParams.progress = value;
progressParams.progress_title = title; progressParams.progress_title = title;
@@ -143,7 +214,10 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
const auto installerPath = extractEmbeddedInstaller(); const auto installerPath = extractEmbeddedInstaller();
if (!installerPath) if (!installerPath)
{ {
notifications::show_toast(INSTALLER_EXTRACT_ERROR, TOAST_TITLE); if (!cmdArgs.contains(CmdArgs::silent))
{
notifications::show_toast(INSTALLER_EXTRACT_ERROR, TOAST_TITLE);
}
return 1; return 1;
} }
auto removeExtractedInstaller = wil::scope_exit([&] { auto removeExtractedInstaller = wil::scope_exit([&] {
@@ -153,38 +227,33 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
updateProgressBar(.25f, UNINSTALLING_PREVIOUS_VERSION); updateProgressBar(.25f, UNINSTALLING_PREVIOUS_VERSION);
const auto package_path = updating::get_msi_package_path(); const auto package_path = updating::get_msi_package_path();
if (!package_path.empty() && !updating::uninstall_msi_version(package_path)) if (!package_path.empty() && !updating::uninstall_msi_version(package_path) && !cmdArgs.contains(CmdArgs::silent))
{ {
notifications::show_toast(UNINSTALL_PREVIOUS_VERSION_ERROR, TOAST_TITLE); notifications::show_toast(UNINSTALL_PREVIOUS_VERSION_ERROR, TOAST_TITLE);
} }
updateProgressBar(.5f, INSTALLING_DOTNET); updateProgressBar(.5f, INSTALLING_DOTNET);
if (!updating::dotnet_is_installed() && !updating::install_dotnet()) if (!updating::dotnet_is_installed() && !updating::install_dotnet() && !cmdArgs.contains(CmdArgs::silent))
{ {
notifications::show_toast(DOTNET_INSTALL_ERROR, TOAST_TITLE); notifications::show_toast(DOTNET_INSTALL_ERROR, TOAST_TITLE);
} }
updateProgressBar(.75f, INSTALLING_NEW_VERSION); updateProgressBar(.75f, INSTALLING_NEW_VERSION);
if (!silent)
{
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
}
const bool installationDone = MsiInstallProductW(installerPath->c_str(), nullptr) == ERROR_SUCCESS; const bool installationDone = MsiInstallProductW(installerPath->c_str(), nullptr) == ERROR_SUCCESS;
updateProgressBar(1.f, installationDone ? NEW_VERSION_INSTALLATION_DONE : NEW_VERSION_INSTALLATION_ERROR); updateProgressBar(1.f, installationDone ? NEW_VERSION_INSTALLATION_DONE : NEW_VERSION_INSTALLATION_ERROR);
if (!installationDone) if (!installationDone)
{ {
return 1; return 1;
} }
auto newPTPath = updating::get_msi_package_installed_path();
if (!newPTPath) if (!cmdArgs.contains(CmdArgs::noStartPT) && !cmdArgs.contains(CmdArgs::silent))
{
return 1;
}
// Do not launch PowerToys, if we're launched from the action_runner
if (!silent)
{ {
auto newPTPath = updating::get_msi_package_installed_path();
if (!newPTPath)
{
return 1;
}
*newPTPath += L"\\PowerToys.exe"; *newPTPath += L"\\PowerToys.exe";
SHELLEXECUTEINFOW sei{ sizeof(sei) }; SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE }; sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE };

View File

@@ -8,4 +8,6 @@
#include <optional> #include <optional>
#include <fstream> #include <fstream>
#include <wil/resource.h> #include <wil/resource.h>
#include <Msi.h> #include <Msi.h>
#include <unordered_set>

View File

@@ -98,7 +98,7 @@ bool install_new_version_stage_1(const std::wstring_view installer_filename, con
} }
} }
bool install_new_version_stage_2(std::wstring 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, bool launch_powertoys)
{ {
std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower); std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower);
@@ -112,10 +112,20 @@ bool install_new_version_stage_2(std::wstring installer_path, std::wstring_view
{ {
// If it's not .msi, then it's our .exe installer // If it's not .msi, then it's our .exe installer
SHELLEXECUTEINFOW sei{ sizeof(sei) }; SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE}; sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
sei.lpFile = installer_path.c_str(); sei.lpFile = installer_path.c_str();
sei.nShow = SW_SHOWNORMAL; sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = L"-silent"; std::wstring parameters = L"--no_full_ui";
if (launch_powertoys)
{
// .exe installer launches the main app by default
launch_powertoys = false;
}
else
{
parameters += L"--no_start_pt";
}
sei.lpParameters = parameters.c_str();
success = ShellExecuteExW(&sei) == TRUE; success = ShellExecuteExW(&sei) == TRUE;
// Wait for the install completion // Wait for the install completion

View File

@@ -451,7 +451,7 @@ std::wstring get_process_path(DWORD pid) noexcept
return name; return name;
} }
bool run_elevated(const std::wstring& file, const std::wstring& params) HANDLE run_elevated(const std::wstring& file, const std::wstring& params)
{ {
SHELLEXECUTEINFOW exec_info = { 0 }; SHELLEXECUTEINFOW exec_info = { 0 };
exec_info.cbSize = sizeof(SHELLEXECUTEINFOW); exec_info.cbSize = sizeof(SHELLEXECUTEINFOW);
@@ -464,14 +464,7 @@ bool run_elevated(const std::wstring& file, const std::wstring& params)
exec_info.hInstApp = 0; exec_info.hInstApp = 0;
exec_info.nShow = SW_SHOWDEFAULT; exec_info.nShow = SW_SHOWDEFAULT;
if (ShellExecuteExW(&exec_info)) return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
{
return exec_info.hProcess != nullptr;
}
else
{
return false;
}
} }
bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid) bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid)

View File

@@ -67,7 +67,7 @@ bool is_process_elevated(const bool use_cached_value = true);
bool drop_elevated_privileges(); bool drop_elevated_privileges();
// Run command as elevated user, returns true if succeeded // Run command as elevated user, returns true if succeeded
bool run_elevated(const std::wstring& file, const std::wstring& params); HANDLE run_elevated(const std::wstring& file, const std::wstring& params);
// Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL // Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL
bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid); bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWORD* returnPid);