[Setup] Always elevate bootstrapper to avoid multiple UAC prompts on upgrade

This commit is contained in:
yuyoyuppe
2021-10-22 03:55:46 +03:00
committed by Andrey Nekrasov
parent 092ee49139
commit 59108365f1
7 changed files with 88 additions and 85 deletions

View File

@@ -206,6 +206,7 @@ CANRENAME
Captureascreenshot Captureascreenshot
CAPTURECHANGED CAPTURECHANGED
CASESENSITIVE CASESENSITIVE
cassert
CAtl CAtl
CCDDEE CCDDEE
ccf ccf
@@ -1010,7 +1011,6 @@ IRepository
IResult IResult
ISavable ISavable
isbi isbi
iss
ISearch ISearch
IService IService
isetting isetting
@@ -1019,6 +1019,7 @@ IShell
ISingle ISingle
ISmart ISmart
isocpp isocpp
iss
IStorage IStorage
IStream IStream
istreambuf istreambuf
@@ -1029,9 +1030,9 @@ ITab
ITask ITask
ITemplate ITemplate
ITEMSTATEICONCLICK ITEMSTATEICONCLICK
ITerminal
ITest ITest
ith ith
ITerminal
IThrottled IThrottled
IThumbnail IThumbnail
ITrigger ITrigger
@@ -1342,6 +1343,7 @@ msedge
MSGFLT MSGFLT
mshtmdid mshtmdid
msi msi
msiexec
MSIFASTINSTALL MSIFASTINSTALL
MSIHANDLE MSIHANDLE
MSIINSTALLER MSIINSTALLER

View File

@@ -165,6 +165,51 @@ std::optional<InstalledVersionInfo> get_installed_powertoys_version()
}; };
} }
void ReLaunchElevatedAndExit()
{
std::wstring params;
int nCmdArgs = 0;
LPWSTR* argList = CommandLineToArgvW(GetCommandLineW(), &nCmdArgs);
for (int i = 1; i < nCmdArgs; ++i)
{
if (std::wstring_view{ argList[i] }.find(L' ') != std::wstring_view::npos)
{
params += L'"';
params += argList[i];
params += L'"';
}
else
{
params += argList[i];
}
if (i != nCmdArgs - 1)
{
params += L' ';
}
}
const auto processHandle = run_elevated(argList[0], params.c_str());
if (!processHandle)
{
spdlog::error("Couldn't restart elevated: ({})", GetLastError());
return;
}
if (WaitForSingleObject(processHandle, 3600000) == WAIT_OBJECT_0)
{
DWORD exitCode = 0;
GetExitCodeProcess(processHandle, &exitCode);
std::exit(exitCode);
}
else
{
spdlog::error("Elevated setup process timed out after 60m: ({})", GetLastError());
TerminateProcess(processHandle, 0);
std::exit(1);
}
}
int Bootstrapper(HINSTANCE hInstance) int Bootstrapper(HINSTANCE hInstance)
{ {
winrt::init_apartment(); winrt::init_apartment();
@@ -299,7 +344,14 @@ int Bootstrapper(HINSTANCE hInstance)
} }
} }
// Setup MSI UI visibility and restart as elevated if required // Always elevate bootstrapper process since it invokes msiexec multiple times,
// so we can avoid multiple UAC confirmations
if (!is_process_elevated())
{
ReLaunchElevatedAndExit();
}
// Setup MSI UI visibility
if (!noFullUI) if (!noFullUI)
{ {
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr); MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
@@ -307,58 +359,7 @@ int Bootstrapper(HINSTANCE hInstance)
if (g_Silent) if (g_Silent)
{ {
if (is_process_elevated()) MsiSetInternalUI(INSTALLUILEVEL_NONE, nullptr);
{
MsiSetInternalUI(INSTALLUILEVEL_NONE, nullptr);
}
else
{
spdlog::debug("MSI doesn't support silent mode without elevation => restarting elevated");
// MSI fails to run in silent mode due to a suppressed UAC w/o elevation,
// so we restart ourselves elevated with the same args
std::wstring params;
int nCmdArgs = 0;
LPWSTR* argList = CommandLineToArgvW(GetCommandLineW(), &nCmdArgs);
for (int i = 1; i < nCmdArgs; ++i)
{
if (std::wstring_view{ argList[i] }.find(L' ') != std::wstring_view::npos)
{
params += L'"';
params += argList[i];
params += L'"';
}
else
{
params += argList[i];
}
if (i != nCmdArgs - 1)
{
params += L' ';
}
}
const auto processHandle = run_elevated(argList[0], params.c_str());
if (!processHandle)
{
spdlog::error("Couldn't restart elevated to enable silent mode! ({})", GetLastError());
return 1;
}
if (WaitForSingleObject(processHandle, 3600000) == WAIT_OBJECT_0)
{
DWORD exitCode = 0;
GetExitCodeProcess(processHandle, &exitCode);
return exitCode;
}
else
{
spdlog::error("Elevated setup process timed out after 60m => using basic MSI UI ({})", GetLastError());
// 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 by acquiring app mutex // Try killing PowerToys and prevent future processes launch by acquiring app mutex

View File

@@ -52,11 +52,11 @@ UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder); hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installfolder."); ExitOnFailure(hr, "Failed to get installfolder.");
for (const auto& changeset : getAllModulesChangesets(installationFolder, false)) for (const auto& changeSet : getAllModulesChangeSets(installationFolder, false))
{ {
if (!changeset.apply()) if (!changeSet.apply())
{ {
WcaLog(LOGMSG_STANDARD, "Couldn't apply registry changeset"); WcaLog(LOGMSG_STANDARD, "Couldn't apply registry changeSet");
} }
} }
@@ -77,9 +77,9 @@ UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder); hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installfolder."); ExitOnFailure(hr, "Failed to get installfolder.");
for (const auto& changeset : getAllModulesChangesets(installationFolder, false)) for (const auto& changeSet : getAllModulesChangeSets(installationFolder, false))
{ {
changeset.unapply(); changeSet.unApply();
} }
ExitOnFailure(hr, "Failed to extract msix"); ExitOnFailure(hr, "Failed to extract msix");

View File

@@ -6,7 +6,7 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
inline registry::Changeset getSvgPreviewHandlerChangset(const std::wstring installationDir, const bool perUser) inline registry::ChangeSet getSvgPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{ {
using namespace registry::shellex; using namespace registry::shellex;
return generatePreviewHandler(PreviewHandlerType::preview, return generatePreviewHandler(PreviewHandlerType::preview,
@@ -22,7 +22,7 @@ inline registry::Changeset getSvgPreviewHandlerChangset(const std::wstring insta
L".svg"); L".svg");
} }
inline registry::Changeset getMdPreviewHandlerChangset(const std::wstring installationDir, const bool perUser) inline registry::ChangeSet getMdPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{ {
using namespace registry::shellex; using namespace registry::shellex;
return generatePreviewHandler(PreviewHandlerType::preview, return generatePreviewHandler(PreviewHandlerType::preview,
@@ -36,7 +36,7 @@ inline registry::Changeset getMdPreviewHandlerChangset(const std::wstring instal
L".md"); L".md");
} }
inline registry::Changeset getPdfPreviewHandlerChangset(const std::wstring installationDir, const bool perUser) inline registry::ChangeSet getPdfPreviewHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{ {
using namespace registry::shellex; using namespace registry::shellex;
return generatePreviewHandler(PreviewHandlerType::preview, return generatePreviewHandler(PreviewHandlerType::preview,
@@ -50,7 +50,7 @@ inline registry::Changeset getPdfPreviewHandlerChangset(const std::wstring insta
L".pdf"); L".pdf");
} }
inline registry::Changeset getSvgThumbnailHandlerChangset(const std::wstring installationDir, const bool perUser) inline registry::ChangeSet getSvgThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{ {
using namespace registry::shellex; using namespace registry::shellex;
return generatePreviewHandler(PreviewHandlerType::thumbnail, return generatePreviewHandler(PreviewHandlerType::thumbnail,
@@ -64,7 +64,7 @@ inline registry::Changeset getSvgThumbnailHandlerChangset(const std::wstring ins
L".svg"); L".svg");
} }
inline registry::Changeset getPdfThumbnailHandlerChangset(const std::wstring installationDir, const bool perUser) inline registry::ChangeSet getPdfThumbnailHandlerChangeSet(const std::wstring installationDir, const bool perUser)
{ {
using namespace registry::shellex; using namespace registry::shellex;
return generatePreviewHandler(PreviewHandlerType::thumbnail, return generatePreviewHandler(PreviewHandlerType::thumbnail,
@@ -78,11 +78,11 @@ inline registry::Changeset getPdfThumbnailHandlerChangset(const std::wstring ins
L".pdf"); L".pdf");
} }
inline std::vector<registry::Changeset> getAllModulesChangesets(const std::wstring installationDir, const bool perUser) inline std::vector<registry::ChangeSet> getAllModulesChangeSets(const std::wstring installationDir, const bool perUser)
{ {
return { getSvgPreviewHandlerChangset(installationDir, perUser), return { getSvgPreviewHandlerChangeSet(installationDir, perUser),
getMdPreviewHandlerChangset(installationDir, perUser), getMdPreviewHandlerChangeSet(installationDir, perUser),
getPdfPreviewHandlerChangset(installationDir, perUser), getPdfPreviewHandlerChangeSet(installationDir, perUser),
getSvgThumbnailHandlerChangset(installationDir, perUser), getSvgThumbnailHandlerChangeSet(installationDir, perUser),
getPdfThumbnailHandlerChangset(installationDir, perUser) }; getPdfThumbnailHandlerChangeSet(installationDir, perUser) };
} }

View File

@@ -114,7 +114,7 @@ namespace registry
valueSize) == ERROR_SUCCESS; valueSize) == ERROR_SUCCESS;
} }
bool unapply() const bool unApply() const
{ {
HKEY key{}; HKEY key{};
if (RegOpenKeyExW(scope, path.c_str(), 0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) if (RegOpenKeyExW(scope, path.c_str(), 0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS)
@@ -207,7 +207,7 @@ namespace registry
} }
}; };
struct Changeset struct ChangeSet
{ {
std::vector<ValueChange> changes; std::vector<ValueChange> changes;
@@ -233,12 +233,12 @@ namespace registry
return ok; return ok;
} }
bool unapply() const bool unApply() const
{ {
bool ok = true; bool ok = true;
for (const auto& c : changes) for (const auto& c : changes)
{ {
ok = c.unapply() && ok; ok = c.unApply() && ok;
} }
return ok; return ok;
} }
@@ -256,7 +256,7 @@ namespace registry
thumbnail thumbnail
}; };
inline registry::Changeset generatePreviewHandler(const PreviewHandlerType handlerType, inline registry::ChangeSet generatePreviewHandler(const PreviewHandlerType handlerType,
const bool perUser, const bool perUser,
std::wstring handlerClsid, std::wstring handlerClsid,
std::wstring powertoysVersion, std::wstring powertoysVersion,
@@ -323,7 +323,7 @@ namespace registry
changes.push_back({ scope, previewHandlerListPath, handlerClsid, displayName }); changes.push_back({ scope, previewHandlerListPath, handlerClsid, displayName });
} }
return registry::Changeset{ .changes = std::move(changes) }; return registry::ChangeSet{ .changes = std::move(changes) };
} }
} }
} }

View File

@@ -20,23 +20,23 @@ PowerPreviewModule::PowerPreviewModule() :
const bool installPerUser = false; const bool installPerUser = false;
m_fileExplorerModules.push_back({ .settingName = L"svg-previewer-toggle-setting", m_fileExplorerModules.push_back({ .settingName = L"svg-previewer-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION), .settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION),
.registryChanges = getSvgPreviewHandlerChangset(installationDir, installPerUser) }); .registryChanges = getSvgPreviewHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"md-previewer-toggle-setting", m_fileExplorerModules.push_back({ .settingName = L"md-previewer-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION), .settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_MD_SETTINGS_DESCRIPTION),
.registryChanges = getMdPreviewHandlerChangset(installationDir, installPerUser) }); .registryChanges = getMdPreviewHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"pdf-previewer-toggle-setting", m_fileExplorerModules.push_back({ .settingName = L"pdf-previewer-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_PDF_SETTINGS_DESCRIPTION), .settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_PDF_SETTINGS_DESCRIPTION),
.registryChanges = getPdfPreviewHandlerChangset(installationDir, installPerUser) }); .registryChanges = getPdfPreviewHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"svg-thumbnail-toggle-setting", m_fileExplorerModules.push_back({ .settingName = L"svg-thumbnail-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION), .settingDescription = GET_RESOURCE_STRING(IDS_SVG_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.registryChanges = getSvgThumbnailHandlerChangset(installationDir, installPerUser) }); .registryChanges = getSvgThumbnailHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"pdf-thumbnail-toggle-setting", m_fileExplorerModules.push_back({ .settingName = L"pdf-thumbnail-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_PDF_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION), .settingDescription = GET_RESOURCE_STRING(IDS_PDF_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.registryChanges = getPdfThumbnailHandlerChangset(installationDir, installPerUser) }); .registryChanges = getPdfThumbnailHandlerChangeSet(installationDir, installPerUser) });
try try
{ {
@@ -133,7 +133,7 @@ void PowerPreviewModule::disable()
{ {
for (auto& fileExplorerModule : m_fileExplorerModules) for (auto& fileExplorerModule : m_fileExplorerModules)
{ {
fileExplorerModule.registryChanges.unapply(); fileExplorerModule.registryChanges.unApply();
} }
} }
else else
@@ -197,7 +197,7 @@ void PowerPreviewModule::apply_settings(const PowerToysSettings::PowerToyValues&
} }
// (Un)Apply registry changes depending on the new setting value // (Un)Apply registry changes depending on the new setting value
const bool updated = *toggle ? fileExplorerModule.registryChanges.apply() : fileExplorerModule.registryChanges.unapply(); const bool updated = *toggle ? fileExplorerModule.registryChanges.apply() : fileExplorerModule.registryChanges.unApply();
if (updated) if (updated)
{ {

View File

@@ -12,7 +12,7 @@ struct FileExplorerModule
{ {
std::wstring settingName; std::wstring settingName;
std::wstring settingDescription; std::wstring settingDescription;
registry::Changeset registryChanges; registry::ChangeSet registryChanges;
}; };
// Implement the PowerToy Module Interface and all the required methods. // Implement the PowerToy Module Interface and all the required methods.