[Setup] Use WiX bootstrapper instead of a custom one (#15050)

@dhowett gave approval on teams.  Squash merging
This commit is contained in:
Andrey Nekrasov 2022-01-05 21:28:09 +03:00 committed by GitHub
parent 244a3b936e
commit 3d59c797f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1681 additions and 2643 deletions

View File

@ -122,12 +122,16 @@ AWAYMODE
AYUV
backend
backtracer
BAEC
BAF
bak
bbwe
bck
Bcl
BEEE
betadele
betsegaw
BFB
BGR
bgra
BGSOUNDS
@ -157,6 +161,7 @@ boolalpha
Bools
Boostrapper
bootstrapper
BOOTSTRAPPERINSTALLFOLDER
Bopomofo
BOTTOMALIGN
BPBF
@ -175,6 +180,7 @@ buf
BUFSIZE
bugreport
Buid
BUILDARCH
buildtransitive
BValue
bytearray
@ -195,6 +201,7 @@ CDeclaration
CDEF
cdpx
CENTERALIGN
CFDB
cguid
changecursor
Changemove
@ -352,6 +359,8 @@ Datavalue
DATAW
davidegiacometti
Dayof
DBCD
DBDA
dbg
Dbghelp
DBLCLKS
@ -367,6 +376,7 @@ declspec
decltype
Dedup
deduplicate
DEFAULTBOOTSTRAPPERINSTALLFOLDER
DEFAULTCOLOR
DEFAULTFLAGS
DEFAULTONLY
@ -477,6 +487,9 @@ dwrite
dxgi
dxgiformat
dxguid
EAF
EBA
EBE
ecount
ededf
EDITKEYBOARD
@ -484,6 +497,8 @@ editkeyboardwindow
editorconfig
EDITSHORTCUTS
editshortcutswindow
EFBD
efgh
EFile
egistry
ekus
@ -551,7 +566,10 @@ FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
Farbraum
FARPROC
FBF
FCCD
fdw
FEEBD
feimage
ffaa
fff
@ -850,6 +868,8 @@ INPUTTYPE
INSTALLDESKTOPSHORTCUT
INSTALLDIR
INSTALLFOLDER
INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER
INSTALLFOLDERTOPREVIOUSINSTALLFOLDER
INSTALLLOCATION
INSTALLLOGATTRIBUTES
INSTALLLOGMODE
@ -1365,6 +1385,7 @@ outro
outsettings
OVERLAPPEDWINDOW
overlaywindow
Overridable
OWNDC
PACL
PAINTSTRUCT
@ -1463,6 +1484,7 @@ previewer
PREVIEWHANDLERFRAMEINFO
previewpane
previouscamera
PREVIOUSINSTALLFOLDER
PREVIOUSVERSIONSINSTALLED
prevpane
prgms
@ -2100,6 +2122,7 @@ wcsncmp
wcsnicmp
wdp
wdupenv
We'd
weakme
webcam
webpage

View File

@ -4,7 +4,7 @@
"SignBatches": [
{
"MatchedPath": [
"!(ModernWpf)*.resources.dll",
"*.resources.dll",
"PowerToysSetupCustomActions.dll",

View File

@ -0,0 +1,5 @@
cd /D "%~dp0"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.18362.0
SET IsPipeline=1
call msbuild ../installer/PowerToysSetup.sln /target:PowerToysInstaller /p:Configuration=Release /p:Platform=x64 /p:CIBuild=true || exit /b 1

View File

@ -93,25 +93,6 @@ steps:
msbuildArgs: ${{ parameters.additionalBuildArguments }}
maximumCpuCount: true
- task: NuGetCommand@2
displayName: Restore NuGet packages for PowerToysBootstrapper.sln
inputs:
command: restore
feedsToUse: config
configPath: NuGet.config
restoreSolution: installer\PowerToysBootstrapper\PowerToysBootstrapper.sln
restoreDirectory: '$(Build.SourcesDirectory)\installer\PowerToysBootstrapper\packages'
- task: VSBuild@1
displayName: 'Build PowerToysBootstrapper.sln'
inputs:
solution: '**\installer\PowerToysBootstrapper\PowerToysBootstrapper.sln'
vsVersion: 16.0
platform: '$(BuildPlatform)'
configuration: '$(BuildConfiguration)'
msbuildArgs: ${{ parameters.additionalBuildArguments }}
maximumCpuCount: true
# directly not doing WinAppDriver testing
- task: VSTest@2
displayName: 'MS Tests'

View File

@ -211,7 +211,7 @@ jobs:
inputs:
solution: '**/installer/PowerToysSetup.sln'
vsVersion: 16.0
msbuildArgs: /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
msbuildArgs: /p:CIBuild=true /target:PowerToysInstaller /bl:$(Build.SourcesDirectory)\msbuild.binlog
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
clean: true
@ -231,7 +231,7 @@ jobs:
- task: VSBuild@1
displayName: Build Bootstrapper
inputs:
solution: '**/installer/PowerToysBootstrapper/PowerToysBootstrapper.sln'
solution: '**/installer/PowerToysSetup.sln'
vsVersion: 16.0
msbuildArgs: /p:CIBuild=true /bl:$(Build.SourcesDirectory)\msbuild.binlog
platform: $(BuildPlatform)
@ -239,11 +239,53 @@ jobs:
clean: true
maximumCpuCount: true
- task: CmdLine@2
displayName: 'Insignia: Extract Engine from Bundle'
inputs:
script: '.\installer\packages\WiX.3.11.2\tools\insignia.exe -ib installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform).exe -o installer\engine.exe'
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
displayName: 'ESRP CodeSigning (Engine)'
inputs:
ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection'
FolderPath: 'installer'
Pattern: engine.exe
signConfigType: inlineSignParams
inlineOperation: |
[
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolSign",
"Parameters": {
"OpusName": "Microsoft",
"OpusInfo": "http://www.microsoft.com",
"FileDigest": "/fd \"SHA256\"",
"PageHash": "/NPH",
"TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
},
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationCode": "SigntoolVerify",
"Parameters": {},
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
- task: CmdLine@2
displayName: 'Insignia: Merge Engine into Bundle'
inputs:
script: '.\installer\packages\WiX.3.11.2\tools\insignia.exe -ab installer\engine.exe installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform).exe -o installer\PowerToysSetup\$(BuildPlatform)\$(BuildConfiguration)\PowerToysSetup-${{ parameters.versionNumber }}-$(BuildPlatform).exe'
- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1
displayName: Sign Boostrapper
inputs:
ConnectedServiceName: 'Terminal/Console/WinAppDriver Team Code Signing Connection'
FolderPath: 'installer/PowerToysBootstrapper/$(BuildPlatform)\$(BuildConfiguration)'
FolderPath: 'installer/PowerToysSetup/$(BuildPlatform)\$(BuildConfiguration)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'

View File

@ -6,4 +6,4 @@ We use the awesome [spdlog](https://github.com/gabime/spdlog) library for loggin
<Import Project="..\..\..\deps\spdlog.props" />
```
It'll add the required include dirs and link the library binary itself.
You can see many example usage of the library in its repository or in the [bootstrapper project](../../installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp).

View File

@ -66,7 +66,6 @@ The installer can only be compiled in `Release` mode, step 1 and 2 must be done
2. Compile `BugReportTool.sln` tool. Path from root: `tools\BugReportTool\BugReportTool.sln` (details listed below)
3. Compile `WebcamReportTool.sln` tool. Path from root: `tools\WebcamReportTool\WebcamReportTool.sln` (details listed below)
4. Compile `PowerToysSetup.sln` Path from root: `installer\PowerToysSetup.sln` (details listed below)
5. Compile `PowerToysBootstrapper.sln` Path from root: `installer\PowerToysBootstrapper\PowerToysBootstrapper.sln` (details listed below)
### Prerequisites for building the MSI installer
@ -85,7 +84,7 @@ The installer can only be compiled in `Release` mode, step 1 and 2 must be done
2. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release`
3. From the `Build` menu, choose `Build Solution`.
### Locally compiling the .MSI installer
### Locally compiling the installer
1. Open `installer\PowerToysSetup.sln`
2. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release`
@ -93,14 +92,6 @@ The installer can only be compiled in `Release` mode, step 1 and 2 must be done
The resulting `PowerToysSetup.msi` installer will be available in the `installer\PowerToysSetup\x64\Release\` folder.
### Locally compiling the .EXE Bootstrapper installer
1. Open `installer\PowerToysBootstrapper\PowerToysBootstrapper.sln`
2. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release`
3. From the `Build` menu choose `Build Solution`.
The `PowerToysSetup-0.0.1-x64.exe` binary is created in the `installer\PowerToysBootstrapper\x64\Release\` folder.
#### Supported arguments for the .EXE Bootstrapper installer
Head over to the wiki to see the [full list of supported installer arguments][installerArgWiki].

View File

@ -1,43 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30320.27
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bootstrapper", "bootstrapper\bootstrapper.vcxproj", "{D194E3AA-F824-4CA9-9A58-034DD6B7D022}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spdlog", "..\..\src\logging\logging.vcxproj", "{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Version", "..\..\src\common\version\version.vcxproj", "{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SetttingsAPI", "..\..\src\common\SettingsAPI\SetttingsAPI.vcxproj", "{6955446D-23F7-4023-9BB3-8657F904AF99}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D194E3AA-F824-4CA9-9A58-034DD6B7D022}.Debug|x64.ActiveCfg = Debug|x64
{D194E3AA-F824-4CA9-9A58-034DD6B7D022}.Debug|x64.Build.0 = Debug|x64
{D194E3AA-F824-4CA9-9A58-034DD6B7D022}.Release|x64.ActiveCfg = Release|x64
{D194E3AA-F824-4CA9-9A58-034DD6B7D022}.Release|x64.Build.0 = Release|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|x64.ActiveCfg = Debug|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|x64.Build.0 = Debug|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|x64.ActiveCfg = Release|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|x64.Build.0 = Release|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|x64.ActiveCfg = Debug|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|x64.Build.0 = Debug|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.ActiveCfg = Release|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.Build.0 = Release|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Debug|x64.ActiveCfg = Debug|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Debug|x64.Build.0 = Debug|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Release|x64.ActiveCfg = Release|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B288A538-1B79-4782-A737-14CD9CF07F58}
EndGlobalSection
EndGlobal

View File

@ -1,90 +0,0 @@
#include "pch.h"
#include "DotnetInstallation.h"
#include <common/utils/exec.h>
#include <common/utils/HttpClient.h>
#include <common/utils/winapi_error.h>
namespace fs = std::filesystem;
namespace updating
{
constexpr size_t REQUIRED_MINIMAL_PATCH = 20;
bool dotnet_is_installed()
{
auto runtimes = exec_and_read_output(LR"(dotnet --list-runtimes)");
if (!runtimes)
{
return false;
}
std::regex dotnet3_1_x{ R"(Microsoft\.WindowsDesktop\.App\s3\.1\.(\d+))" };
size_t latestPatchInstalled = 0;
using rexit = std::sregex_iterator;
for (auto it = rexit{ begin(*runtimes), end(*runtimes), dotnet3_1_x }; it != rexit{}; ++it)
{
if (!it->ready() || it->size() < 2)
{
continue;
}
auto patchNumberGroup = (*it)[1];
if (!patchNumberGroup.matched)
{
continue;
}
const auto patchString = patchNumberGroup.str();
size_t patch = 0;
if (auto [_, ec] = std::from_chars(&*begin(patchString), &*end(patchString), patch); ec == std::errc())
{
latestPatchInstalled = std::max(patch, latestPatchInstalled);
}
}
return latestPatchInstalled >= REQUIRED_MINIMAL_PATCH;
}
std::optional<fs::path> download_dotnet()
{
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/93c69a29-d379-4a5d-bb9e-3116cc14de41/907bbc52446d8bb7baa0c6faebde1d44/windowsdesktop-runtime-3.1.20-win-x64.exe";
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe";
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;
winrt::Windows::Foundation::Uri download_link{ DOTNET_DESKTOP_DOWNLOAD_LINK };
const size_t max_attempts = 3;
bool download_success = false;
for (size_t i = 0; i < max_attempts; ++i)
{
try
{
http::HttpClient client;
client.download(download_link, dotnet_download_path).wait();
download_success = true;
break;
}
catch (...)
{
// couldn't download
}
}
return download_success ? std::make_optional(dotnet_download_path) : std::nullopt;
}
bool install_dotnet(fs::path dotnet_download_path, const bool silent = false)
{
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
sei.lpFile = dotnet_download_path.c_str();
sei.nShow = SW_SHOWNORMAL;
std::wstring dotnet_flags = L"/install ";
dotnet_flags += silent ? L"/quiet" : L"/passive";
sei.lpParameters = dotnet_flags.c_str();
if (ShellExecuteExW(&sei) != TRUE)
{
return false;
}
WaitForSingleObject(sei.hProcess, INFINITE);
CloseHandle(sei.hProcess);
return true;
}
}

View File

@ -1,12 +0,0 @@
#pragma once
#include <filesystem>
#include <optional>
namespace fs = std::filesystem;
namespace updating
{
bool dotnet_is_installed();
std::optional<fs::path> download_dotnet();
bool install_dotnet(fs::path dotnet_download_path, const bool silent);
}

View File

@ -1,97 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="BOOTSTRAPPER_PROGRESS_TITLE" xml:space="preserve">
<value>PowerToys Installer</value>
</data>
<data name="DOTNET_CORE_DOWNLOAD_FAILURE" xml:space="preserve">
<value>Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.</value>
</data>
<data name="DOTNET_CORE_DOWNLOAD_FAILURE_TITLE" xml:space="preserve">
<value>PowerToys installation error</value>
</data>
<data name="GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT" xml:space="preserve">
<value>An update to PowerToys is available. Visit our GitHub page to update.</value>
</data>
<data name="INSTALLER_EXTRACT_ERROR" xml:space="preserve">
<value>Couldn't extract MSI installer.</value>
</data>
<data name="EXTRACTING_INSTALLER" xml:space="preserve">
<value>Extracting PowerToys MSI...</value>
</data>
<data name="UNINSTALL_PREVIOUS_VERSION_ERROR" xml:space="preserve">
<value>Couldn't uninstall previous PowerToys version.</value>
</data>
<data name="DOWNLOADING_DOTNET" xml:space="preserve">
<value>Downloading .NET Core...</value>
</data>
<data name="DOTNET_INSTALL_ERROR" xml:space="preserve">
<value>Couldn't install .NET Core.</value>
</data>
<data name="NEW_VERSION_INSTALLATION_ERROR" xml:space="preserve">
<value>Couldn't install new PowerToys version.</value>
</data>
<data name="NEWER_VERSION_ERROR" xml:space="preserve">
<value>A newer version is already installed.</value>
</data>
<data name="OLD_WINDOWS_ERROR" xml:space="preserve">
<value>PowerToys requires Windows 10 version 1903 (May 2019 Update) or newer to run.</value>
</data>
</root>

View File

@ -1,41 +0,0 @@
#include <windows.h>
#include "resource.h"
#include "../../../src/common/version/version.h"
MAINICON ICON "../../../src/runner/svgs/icon.ico"
IDR_BIN_ICON BIN "../../../src/runner/svgs/icon.ico"
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END
#include "Generated Files\installer_resource.rc"

View File

@ -1,527 +0,0 @@
#include "pch.h"
#include "Generated Files/resource.h"
#include "RcResource.h"
#include <common/version/helper.h>
#include <common/version/version.h>
#include <common/utils/appMutex.h>
#include <common/utils/elevation.h>
#include <common/utils/MsiUtils.h>
#include <common/utils/os-detect.h>
#include <common/utils/processApi.h>
#include <common/utils/resources.h>
#include <common/utils/window.h>
#include <common/utils/winapi_error.h>
#include <common/SettingsAPI/settings_helpers.h>
#include "DotnetInstallation.h"
#include "progressbar_window.h"
static bool g_Silent = false;
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
namespace // Strings in this namespace should not be localized
{
const wchar_t APPLICATION_ID[] = L"PowerToysInstaller";
const char EXE_LOG_FILENAME[] = "powertoys-bootstrapper-exe-" 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";
}
#undef STR
#undef STR_HELPER
namespace fs = std::filesystem;
std::optional<fs::path> ExtractEmbeddedInstaller(const fs::path extractPath)
{
auto executableRes = RcResource::create(IDR_BIN_MSIINSTALLER, L"BIN");
if (!executableRes)
{
return std::nullopt;
}
std::wstring msiName(L"PowerToysSetup-" STRINGIZE(VERSION_MAJOR) "." STRINGIZE(VERSION_MINOR) "." STRINGIZE(VERSION_REVISION) L"-");
msiName += get_architecture_string(get_current_architecture()) + std::wstring(L".msi");
auto installerPath = extractPath / msiName;
return executableRes->saveAsFile(installerPath) ? std::make_optional(std::move(installerPath)) : std::nullopt;
}
void SetupLogger(fs::path directory, const spdlog::level::level_enum severity)
{
std::shared_ptr<spdlog::logger> logger;
auto nullLogger = spdlog::null_logger_mt("null");
try
{
if (severity != spdlog::level::off)
{
logger = spdlog::basic_logger_mt("file", (directory / EXE_LOG_FILENAME).wstring());
std::error_code _;
const DWORD msiSev = severity == spdlog::level::debug ? INSTALLLOGMODE_VERBOSE : INSTALLLOGMODE_ERROR;
const auto msiLogPath = directory / MSI_LOG_FILENAME;
MsiEnableLogW(msiSev, msiLogPath.c_str(), INSTALLLOGATTRIBUTES_APPEND);
}
else
{
logger = nullLogger;
}
logger->set_pattern("[%L][%d-%m-%C-%T] %v");
logger->set_level(severity);
spdlog::set_default_logger(std::move(logger));
spdlog::set_level(severity);
spdlog::flush_every(std::chrono::seconds(5));
}
catch (...)
{
spdlog::set_default_logger(nullLogger);
}
}
void CleanupSettingsFromOlderVersions()
{
try
{
const auto logSettingsFile = fs::path{ PTSettingsHelper::get_root_save_folder_location() } / PTSettingsHelper::log_settings_filename;
if (fs::is_regular_file(logSettingsFile))
{
fs::remove(logSettingsFile);
spdlog::info("Removed old log settings file");
}
else
{
spdlog::info("Old log settings file wasn't found");
}
}
catch (...)
{
spdlog::error("Failed to cleanup old log settings");
}
}
void ShowMessageBoxError(const wchar_t* message)
{
if (!g_Silent)
{
MessageBoxW(nullptr,
message,
GET_RESOURCE_STRING(IDS_BOOTSTRAPPER_PROGRESS_TITLE).c_str(),
MB_OK | MB_ICONERROR);
}
}
void ShowMessageBoxError(const UINT messageId)
{
ShowMessageBoxError(GET_RESOURCE_STRING(messageId).c_str());
}
bool uninstall_msi_version(const std::wstring& package_path)
{
const auto uninstall_result = MsiInstallProductW(package_path.c_str(), L"REMOVE=ALL");
return ERROR_SUCCESS == uninstall_result;
}
struct InstalledVersionInfo
{
VersionHelper version;
std::wstring install_folder;
};
std::optional<InstalledVersionInfo> get_installed_powertoys_version()
{
auto installed_path = GetMsiPackageInstalledPath();
if (!installed_path)
{
return std::nullopt;
}
std::wstring executable_path = *installed_path + L"\\PowerToys.exe";
// Get the version information for the file requested
const DWORD fvSize = GetFileVersionInfoSizeW(executable_path.c_str(), nullptr);
if (!fvSize)
{
return std::nullopt;
}
auto pbVersionInfo = std::make_unique<BYTE[]>(fvSize);
if (!GetFileVersionInfoW(executable_path.c_str(), 0, fvSize, pbVersionInfo.get()))
{
return std::nullopt;
}
VS_FIXEDFILEINFO* fileInfo = nullptr;
UINT fileInfoLen = 0;
if (!VerQueryValueW(pbVersionInfo.get(), L"\\", reinterpret_cast<LPVOID*>(&fileInfo), &fileInfoLen))
{
return std::nullopt;
}
return InstalledVersionInfo{
.version = VersionHelper{ (fileInfo->dwFileVersionMS >> 16) & 0xffff,
(fileInfo->dwFileVersionMS >> 0) & 0xffff,
(fileInfo->dwFileVersionLS >> 16) & 0xffff },
.install_folder = std::move(*installed_path)
};
}
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)
{
winrt::init_apartment();
fs::path logDir = PTSettingsHelper::get_root_save_folder_location();
cxxopts::Options options{ "PowerToysBootstrapper" };
// clang-format off
options.add_options()
("h,help", "Show help")
("no_full_ui", "Use reduced UI for MSI")
("s,silent", "Suppress all UI, notifications and does not start PowerToys")
("no_start_pt", "Do not launch PowerToys after the installation is complete")
("start_pt", "Always launch PowerToys after the installation is complete")
("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<std::string>()->default_value("off"))
("log_dir", "Log directory", cxxopts::value<std::string>()->default_value(logDir.string()))
("install_dir", "Installation directory", cxxopts::value<std::string>()->default_value(""))
("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;
try
{
cmdArgs = options.parse(__argc, const_cast<const char**>(__argv));
}
catch (...)
{
showHelp = true;
}
showHelp = showHelp || cmdArgs["help"].as<bool>();
if (showHelp)
{
std::ostringstream helpMsg;
helpMsg << options.help();
MessageBoxA(nullptr, helpMsg.str().c_str(), "Help", MB_OK | MB_ICONINFORMATION);
return 0;
}
g_Silent = cmdArgs["silent"].as<bool>();
const bool noFullUI = cmdArgs["no_full_ui"].as<bool>();
const bool skipDotnetInstall = cmdArgs["skip_dotnet_install"].as<bool>();
const bool noStartPT = cmdArgs["no_start_pt"].as<bool>();
const bool startPT = cmdArgs["start_pt"].as<bool>();
const auto logLevel = cmdArgs["log_level"].as<std::string>();
const auto logDirArg = cmdArgs["log_dir"].as<std::string>();
const auto installDirArg = cmdArgs["install_dir"].as<std::string>();
const bool extractMsiOnly = cmdArgs["extract_msi"].as<bool>();
std::wstring installFolderProp;
if (!installDirArg.empty())
{
std::string installDir;
if (installDirArg.find(' ') != std::string::npos)
{
installDir = "\"" + installDirArg + "\"";
}
else
{
installDir = installDirArg;
}
installFolderProp = std::wstring(installDir.length(), L' ');
std::copy(installDir.begin(), installDir.end(), installFolderProp.begin());
installFolderProp = L"INSTALLFOLDER=\"" + installFolderProp + L"\"";
}
try
{
fs::path logDirArgPath = logDirArg;
if (fs::exists(logDirArgPath) && fs::is_directory(logDirArgPath))
{
logDir = logDirArgPath;
}
}
catch (...)
{
}
spdlog::level::level_enum severity = spdlog::level::debug;
if (logLevel == "error")
{
severity = spdlog::level::err;
}
SetupLogger(logDir, severity);
spdlog::debug("PowerToys Bootstrapper is launched\nnoFullUI: {}\nsilent: {}\nno_start_pt: {}\nskip_dotnet_install: {}\nlog_level: {}\ninstall_dir: {}\nextract_msi: {}\n", noFullUI, g_Silent, noStartPT, skipDotnetInstall, logLevel, installDirArg, extractMsiOnly);
// If a user requested an MSI -> extract it and exit
if (extractMsiOnly)
{
if (const auto installerPath = ExtractEmbeddedInstaller(fs::current_path()))
{
spdlog::debug("MSI installer extracted to {}", installerPath->string());
}
else
{
spdlog::error("MSI installer couldn't be extracted");
}
return 0;
}
const VersionHelper myVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
// Do not support installing on Windows < 1903
if (!Is19H1OrHigher())
{
ShowMessageBoxError(IDS_OLD_WINDOWS_ERROR);
spdlog::error("PowerToys {} requires at least Windows 1903 to run.", myVersion.toString());
return 1;
}
// Check if there's a newer version installed
const auto installedVersionInfo = get_installed_powertoys_version();
if (installedVersionInfo)
{
if (installedVersionInfo->version >= myVersion)
{
spdlog::error(L"Detected a newer version {} vs {}", installedVersionInfo->version.toWstring(), myVersion.toWstring());
ShowMessageBoxError(IDS_NEWER_VERSION_ERROR);
return 0;
}
// If we are good to go and install folder wasn't specified via cmd line, make sure to retain the previous
// installation path
else if (installFolderProp.empty())
{
installFolderProp = L"INSTALLFOLDER=\"" + installedVersionInfo->install_folder + L"\"";
}
}
// 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)
{
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
}
if (g_Silent)
{
MsiSetInternalUI(INSTALLUILEVEL_NONE, nullptr);
}
// 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)
{
spdlog::error("Couldn't acquire PowerToys global mutex. Setup couldn't terminate PowerToys.exe process");
return 1;
}
spdlog::debug("Extracting embedded MSI installer");
const auto installerPath = ExtractEmbeddedInstaller(fs::temp_directory_path());
if (!installerPath)
{
ShowMessageBoxError(IDS_INSTALLER_EXTRACT_ERROR);
spdlog::error("Couldn't install the MSI installer ({})", GetLastError());
return 1;
}
auto removeExtractedInstaller = wil::scope_exit([&] {
std::error_code _;
fs::remove(*installerPath, _);
});
spdlog::debug("Acquiring existing MSI package path if exists");
const auto package_path = GetMsiPackagePath();
if (!package_path.empty())
{
spdlog::debug(L"Existing MSI package path found: {}", package_path);
}
else
{
spdlog::debug("Existing MSI package path not found");
}
if (!package_path.empty() && !uninstall_msi_version(package_path))
{
spdlog::error("Couldn't uninstall the existing MSI package ({})", GetLastError());
ShowMessageBoxError(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR);
return 1;
}
const bool installDotnet = !skipDotnetInstall;
if (!g_Silent)
{
OpenProgressBarDialog(hInstance, 0, GET_RESOURCE_STRING(IDS_BOOTSTRAPPER_PROGRESS_TITLE).c_str(), GET_RESOURCE_STRING(IDS_DOWNLOADING_DOTNET).c_str());
}
try
{
if (installDotnet)
{
spdlog::debug("Detecting if dotnet is installed");
const bool dotnetInstalled = updating::dotnet_is_installed();
spdlog::debug("Dotnet is already installed: {}", dotnetInstalled);
if (!dotnetInstalled)
{
bool installedSuccessfully = false;
if (const auto dotnet_installer_path = updating::download_dotnet())
{
// Dotnet installer has its own progress bar
CloseProgressBarDialog();
installedSuccessfully = updating::install_dotnet(*dotnet_installer_path, g_Silent);
if (!installedSuccessfully)
{
spdlog::error("Couldn't install dotnet");
}
}
else
{
spdlog::error("Couldn't download dotnet");
}
if (!installedSuccessfully)
{
ShowMessageBoxError(IDS_DOTNET_INSTALL_ERROR);
}
}
}
}
catch (...)
{
spdlog::error("Unknown exception during dotnet installation");
ShowMessageBoxError(IDS_DOTNET_INSTALL_ERROR);
}
// At this point, there's no reason to show progress bar window, since MSI installers have their own
CloseProgressBarDialog();
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)
{
spdlog::error("Couldn't install new MSI package ({})", GetLastError());
return 1;
}
spdlog::debug("Installation completed");
if ((!noStartPT && !g_Silent) || startPT)
{
spdlog::debug("Starting the newly installed PowerToys.exe");
auto newPTPath = GetMsiPackageInstalledPath();
if (!newPTPath)
{
spdlog::error("Couldn't determine new MSI package install location ({})", GetLastError());
return 1;
}
*newPTPath += L"\\PowerToys.exe";
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE };
sei.lpFile = newPTPath->c_str();
sei.nShow = SW_SHOWNORMAL;
ShellExecuteExW(&sei);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hi, HINSTANCE, LPSTR, int)
{
try
{
return Bootstrapper(hi);
}
catch (const std::exception& ex)
{
std::string messageA{ "Unhandled std exception encountered\n" };
messageA.append(ex.what());
spdlog::error(messageA.c_str());
std::wstring messageW{};
std::copy(messageA.begin(), messageA.end(), messageW.begin());
ShowMessageBoxError(messageW.c_str());
}
catch (winrt::hresult_error const& ex)
{
std::wstring message{ L"Unhandled winrt exception encountered\n" };
message.append(ex.message().c_str());
spdlog::error(message.c_str());
ShowMessageBoxError(message.c_str());
}
catch (...)
{
auto lastErrorMessage = get_last_error_message(GetLastError());
std::wstring message{ L"Unknown exception encountered\n" };
message.append(lastErrorMessage ? std::move(*lastErrorMessage) : L"");
spdlog::error(message.c_str());
ShowMessageBoxError(message.c_str());
}
return 0;
}

View File

@ -1,157 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\src\Version.props" />
<Target Name="Generate Resource file" BeforeTargets="PrepareForBuild">
<ItemGroup>
<RCLines Include="IDR_BIN_MSIINSTALLER BIN &quot;..\\PowerToysSetup\\$(PlatformShortName)\\$(Configuration)\\PowerToysSetup-$(Version)-$(PlatformShortName).msi&quot;" />
</ItemGroup>
<WriteLinesToFile File="Generated Files\installer_resource.rc" Lines="@(RCLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)..\..\tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h bootstrapper.base.rc bootstrapper.rc 105" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{D194E3AA-F824-4CA9-9A58-034DD6B7D022}</ProjectGuid>
<RootNamespace>bootstrapper</RootNamespace>
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
<TargetPlatformVersion>10.0.18362.0</TargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ProjectName>bootstrapper</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\..\..\deps\spdlog.props" />
<Import Project="..\..\..\deps\cxxopts.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup>
<TargetName>PowerToysSetup-$(Version)-$(PlatformShortName)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>../;$(IncludePath)</IncludePath>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>../;$(IncludePath)</IncludePath>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<MultiProcessorCompilation Condition="'$(CIBuild)'!='true'">true</MultiProcessorCompilation>
<AdditionalIncludeDirectories>../../../src/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalOptions>/await /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;Comctl32.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<MultiProcessorCompilation Condition="'$(CIBuild)'!='true'">true</MultiProcessorCompilation>
<AdditionalIncludeDirectories>../../../src/;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalOptions>/await /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;Comctl32.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="bootstrapper.cpp" />
<ClCompile Include="DotnetInstallation.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="progressbar_window.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="DotnetInstallation.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="progressbar_window.h" />
</ItemGroup>
<ItemGroup>
<None Include="bootstrapper.base.rc" />
<None Include="packages.config" />
<ResourceCompile Include="Generated Files/bootstrapper.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="..\runner\svgs\icon.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\common\SettingsAPI\SetttingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\src\common\version\version.vcxproj">
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\src\logging\logging.vcxproj">
<Project>{7e1e3f13-2bd6-3f75-a6a7-873a2b55c60f}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.ImplementationLibrary.1.0.210204.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.210204.1" targetFramework="native" />
</packages>

View File

@ -1 +0,0 @@
#include "pch.h"

View File

@ -1,32 +0,0 @@
#pragma once
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shellapi.h>
#include <commctrl.h>
#include <charconv>
#include <string_view>
#include <optional>
#include <fstream>
#include <wil/resource.h>
#include <Msi.h>
#include <unordered_set>
#include <thread>
#include <tuple>
#include <sstream>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#pragma warning(push, 0)
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#pragma warning(pop)
#include <cxxopts.hpp>

View File

@ -1,187 +0,0 @@
#include "pch.h"
#include <common/utils/window.h>
#include "progressbar_window.h"
#include "Generated Files/resource.h"
const int labelHeight = 18;
const int progressBarHeight = 20;
const int margin = 10;
const int windowWidth = 480;
const int titleBarHeight = 32;
const int windowHeight = margin * 4 + progressBarHeight + labelHeight + titleBarHeight;
int progressBarSteps = 0;
HWND hDialog = nullptr;
HWND hLabel = nullptr;
HWND hProgressBar = nullptr;
HBRUSH hBrush = nullptr;
std::wstring labelText;
std::mutex uiThreadIsRunning;
namespace nonlocalized
{
const wchar_t windowClass[] = L"PTBProgressBarWnd";
const wchar_t labelClass[] = 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:
{
uiThreadIsRunning.lock();
hLabel = CreateWindowW(nonlocalized::labelClass,
labelText.c_str(),
WS_CHILD | WS_VISIBLE | WS_TABSTOP,
margin,
margin,
windowWidth - (margin * 4),
labelHeight,
hWnd,
(HMENU)(501),
(HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE), nullptr);
hProgressBar = CreateWindowExW(0,
PROGRESS_CLASS,
nullptr,
WS_VISIBLE | WS_CHILD | PBS_SMOOTH,
margin,
(margin * 2) + labelHeight,
windowWidth - (margin * 4),
progressBarHeight,
hWnd,
(HMENU)(IDR_PROGRESS_BAR),
(HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
nullptr);
bool filledOnStart = false;
if (progressBarSteps == 0)
{
progressBarSteps = 1;
filledOnStart = true;
}
SendMessageW(hProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, progressBarSteps));
SendMessageW(hProgressBar, PBM_SETSTEP, 1, 0);
if (filledOnStart)
{
SendMessageW(hProgressBar, PBM_STEPIT, 0, 0);
}
break;
}
case WM_CTLCOLORSTATIC:
{
if (lParam == (LPARAM)hLabel)
{
if (!hBrush)
{
HDC hdcStatic = (HDC)wParam;
SetTextColor(hdcStatic, RGB(0, 0, 0));
SetBkColor(hdcStatic, RGB(255, 255, 255));
hBrush = CreateSolidBrush(RGB(255, 255, 255));
}
return (LRESULT)hBrush;
}
break;
}
case WM_CLOSE:
{
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProcW(hWnd, Msg, wParam, lParam);
}
}
return 0;
}
void OpenProgressBarDialog(HINSTANCE hInstance, const int nProgressbarSteps, const wchar_t* title, const wchar_t* label)
{
labelText = label;
progressBarSteps = nProgressbarSteps;
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::windowClass;
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 - windowWidth / 2;
rect.top = rect.bottom / 4 - windowHeight / 2;
hDialog = CreateWindowExW(WS_EX_CLIENTEDGE,
nonlocalized::windowClass,
window_title.c_str(),
WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX,
rect.left,
rect.top,
windowWidth,
windowHeight,
nullptr,
nullptr,
hInstance,
nullptr);
if (!hDialog)
{
spdlog::warn("Couldn't create progress bar main_window");
return;
}
ShowWindow(hDialog, SW_SHOW);
UpdateWindow(hDialog);
run_message_loop();
uiThreadIsRunning.unlock();
}
}.detach();
}
void UpdateProgressBarDialog(const wchar_t* label)
{
SetWindowTextW(hLabel, label);
SendMessageW(hProgressBar, PBM_STEPIT, 0, 0);
}
void CloseProgressBarDialog()
{
SendMessageW(hDialog, WM_CLOSE, {}, {});
{
std::unique_lock waitForUIToExit{ uiThreadIsRunning };
}
// 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());
}

View File

@ -1,9 +0,0 @@
#pragma once
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
void OpenProgressBarDialog(HINSTANCE hInstance, const int nProgressbarSteps, const wchar_t* title, const wchar_t* label);
void UpdateProgressBarDialog(const wchar_t* label);
void CloseProgressBarDialog();

View File

@ -1,17 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by bootstrapper.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys Bootstrapper"
#define INTERNAL_NAME "bootstrapper"
#define ORIGINAL_FILENAME "bootstrapper.exe"
// Non-localizable
//////////////////////////////
#define IDR_BIN_MSIINSTALLER 103
#define IDR_BIN_ICON 104
#define IDR_PROGRESS_BAR 105

View File

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
MinimumVisualStudioVersion = 10.0.40219.1
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysSetup", "PowerToysSetup\PowerToysSetup.wixproj", "{022A9D30-7C4F-416D-A9DF-5FF2661CC0AD}"
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysInstaller", "PowerToysSetup\PowerToysInstaller.wixproj", "{022A9D30-7C4F-416D-A9DF-5FF2661CC0AD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToysSetupCustomActions", "PowerToysSetupCustomActions\PowerToysSetupCustomActions.vcxproj", "{32F3882B-F2D6-4586-B5ED-11E39E522BD3}"
EndProject
@ -11,6 +11,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "spdlog", "..\src\logging\lo
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logger", "..\src\common\logger\logger.vcxproj", "{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}"
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysBootstrapper", "PowerToysSetup\PowerToysBootstrapper.wixproj", "{31D72625-43C1-41B1-B784-BCE4A8DC5543}"
ProjectSection(ProjectDependencies) = postProject
{022A9D30-7C4F-416D-A9DF-5FF2661CC0AD} = {022A9D30-7C4F-416D-A9DF-5FF2661CC0AD}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -33,6 +38,10 @@ Global
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x64.Build.0 = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x64.ActiveCfg = Release|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x64.Build.0 = Release|x64
{31D72625-43C1-41B1-B784-BCE4A8DC5543}.Debug|x64.ActiveCfg = Debug|x64
{31D72625-43C1-41B1-B784-BCE4A8DC5543}.Debug|x64.Build.0 = Debug|x64
{31D72625-43C1-41B1-B784-BCE4A8DC5543}.Release|x64.ActiveCfg = Release|x64
{31D72625-43C1-41B1-B784-BCE4A8DC5543}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<?define UpgradeCode="6341382d-c0a9-4238-9188-be9607e3fab2"?>
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
<?define BinX64Dir="$(var.RepoDir)x64\$(var.Configuration)\" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="PowerToys (Preview) $(sys.BUILDARCH)"
Version="$(var.Version)"
Manufacturer="Microsoft Corporation"
IconSourceFile="$(var.BinX64Dir)svgs\icon.ico"
UpgradeCode="$(var.UpgradeCode)">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense">
<bal:WixStandardBootstrapperApplication
LicenseFile="$(var.RepoDir)\installer\License.rtf"
LogoFile="$(var.RepoDir)\installer\MSIX\Images\logo44.png"
SuppressOptionsUI="no"
SuppressRepair="yes" />
</BootstrapperApplicationRef>
<util:FileSearch Variable="HasDotnet3122" Path="[ProgramFiles64Folder]dotnet\shared\Microsoft.WindowsDesktop.App\3.1.22\System.Xaml.dll" Result="exists" />
<Variable Name="InstallFolder" Type="string" Value="[ProgramFiles64Folder]PowerToys" bal:Overridable="yes"/>
<!-- Only install/upgrade if the version is greater or equal than the currently installed version of PowerToys, to handle the case in which PowerToys was installed from old MSI (before WiX bootstrapper was used) -->
<!-- If the previous installation is a bundle installation, just let WiX run its logic. -->
<Variable Name="DetectedPowerToysVersion" Type="version" Value="0.0.0.0"/>
<Variable Name="TargetPowerToysVersion" Type="version" Value="$(var.Version)"/>
<util:ProductSearch Id="SearchInstalledPowerToysVersion" Variable="DetectedPowerToysVersion" UpgradeCode="42B84BF7-5FBF-473B-9C8B-049DC16F7708" Result="version" />
<bal:Condition Message="A later version of PowerToys is already installed." >TargetPowerToysVersion &gt;= DetectedPowerToysVersion OR WixBundleInstalled</bal:Condition>
<Variable Name="DetectedWindowsBuildNumber" Type="version" Value="0"/>
<util:RegistrySearch Id="SearchWindowsBuildNumber" Root="HKLM" Key="SOFTWARE\Microsoft\Windows NT\CurrentVersion" Value="CurrentBuildNumber" Result="value" Format="raw" Variable="DetectedWindowsBuildNumber" />
<bal:Condition Message="This application is only supported on Windows 10 version 1903 (build 18362) or higher.">DetectedWindowsBuildNumber &gt;= 18362 OR WixBundleInstalled</bal:Condition>
<Chain>
<ExePackage
Name="windowsdesktop-runtime-3.1.22-win-x64.exe"
Compressed="no"
Id="DotnetRuntime"
DetectCondition="HasDotnet3122"
DownloadUrl="https://download.visualstudio.microsoft.com/download/pr/1c14e24b-7f31-42dc-ba3c-83295a2d6f7e/41b93591162dfe556cc160ae44fbe75e/windowsdesktop-runtime-3.1.22-win-x64.exe"
InstallCommand="/install /quiet"
RepairCommand="/repair /passive"
Permanent="yes"
PerMachine="yes"
UninstallCommand="/uninstall /quiet">
<RemotePayload
Description="Microsoft Windows Desktop Runtime - 3.1.22 (x64)"
ProductName="Microsoft Windows Desktop Runtime - 3.1.22 (x64)"
Size="54342024"
Version="3.1.22.30721"
Hash="08EF2F6CFDB33946061884B1CE13FA867EFBD576" />
</ExePackage>
<MsiPackage
SourceFile="x64\Release\PowerToysSetup-$(var.Version)-x64.msi"
Compressed="yes"
DisplayInternalUI="no">
<MsiProperty Name="BOOTSTRAPPERINSTALLFOLDER" Value="[InstallFolder]" />
</MsiPackage>
</Chain>
</Bundle>
</Wix>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureNuGetPackageBuildImports" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\WiX.3.11.2\build\wix.props" Condition="Exists('..\packages\WiX.3.11.2\build\wix.props')" />
<Import Project="..\..\src\Version.props" />
<PropertyGroup>
<DefineConstants>Version=$(Version)</DefineConstants>
<Name>PowerToysBootstrapper</Name>
<ProjectGuid>{31d72625-43c1-41b1-b784-bce4a8dc5543}</ProjectGuid>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
<ProductVersion>3.10</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>PowerToysSetup-$(Version)-$(Platform)</OutputName>
<OutputType>Bundle</OutputType>
<SuppressAclReset>True</SuppressAclReset>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<OutputPath>$(Platform)\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>$(Platform)\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Include="PowerToys.wxs" />
</ItemGroup>
<ItemGroup>
<WixExtension Include="WixUtilExtension">
<HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath>
<Name>WixUtilExtension</Name>
</WixExtension>
<WixExtension Include="WixUIExtension">
<HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
<Name>WixUIExtension</Name>
</WixExtension>
<WixExtension Include="WixNetFxExtension">
<HintPath>$(WixExtDir)\WixNetFxExtension.dll</HintPath>
<Name>WixNetFxExtension</Name>
</WixExtension>
<WixExtension Include="WixNetFxExtension">
<HintPath>$(WixExtDir)\WixBalExtension.dll</HintPath>
<Name>WixBalExtension</Name>
</WixExtension>
</ItemGroup>
<ItemGroup>
<Folder Include="CustomDialogs" />
</ItemGroup>
<ItemGroup>
<Content Include="packages.config" />
</ItemGroup>
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
<Error Text="The WiX Toolset v3 build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
</Target>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\WiX.3.11.2\build\wix.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WiX.3.11.2\build\wix.props'))" />
</Target>
</Project>

View File

@ -4,6 +4,7 @@
<Import Project="..\..\src\Version.props" />
<PropertyGroup>
<DefineConstants>Version=$(Version)</DefineConstants>
<Name>PowerToysInstaller</Name>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
@ -20,10 +21,14 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
<OutputPath>$(Platform)\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
<SuppressIces>ICE91</SuppressIces>
<SuppressValidation>True</SuppressValidation>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
<OutputPath>$(Platform)\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Platform)\$(Configuration)\</IntermediateOutputPath>
<SuppressValidation>True</SuppressValidation>
<SuppressIces>ICE91</SuppressIces>
</PropertyGroup>
<ItemGroup>
<Compile Include="CustomDialogs\PTInstallDirDlg.wxs" />

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "resource.h"
#include "RcResource.h"
#include <ProjectTelemetry.h>
#include <spdlog/sinks/base_sink.h>
@ -10,8 +11,6 @@
#include "../../src/common/updating/installer.h"
#include "../../src/common/version/version.h"
#include "../../installer/PowerToysBootstrapper/bootstrapper/RcResource.h"
using namespace std;
HINSTANCE DLL_HANDLE = nullptr;
@ -76,7 +75,7 @@ UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
for (const auto& changeSet : getAllModulesChangeSets(installationFolder, false))
for (const auto& changeSet : getAllModulesChangeSets(installationFolder))
{
if (!changeSet.apply())
{
@ -105,7 +104,7 @@ UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");
for (const auto& changeSet : getAllModulesChangeSets(installationFolder, false))
for (const auto& changeSet : getAllModulesChangeSets(installationFolder))
{
changeSet.unApply();
}
@ -748,12 +747,12 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "DetectPrevInstallPathCA");
MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", L"");
try
{
if (auto install_path = GetMsiPackageInstalledPath())
{
MsiSetPropertyW(hInstall, L"INSTALLFOLDER", install_path->data());
MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", install_path->data());
}
}
catch (...)

View File

@ -16,6 +16,7 @@
<Keyword>Win32Proj</Keyword>
<RootNamespace>PowerToysSetupCustomActions</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ProjectName>PowerToysSetupCustomActions</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
@ -53,12 +54,12 @@
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>inc;..\..\src\;telemetry;$(WIX)sdk\$(WixPlatformToolset)\inc;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>inc;..\..\src\;..\..\src\common\Telemetry;telemetry;$(WIX)sdk\$(WixPlatformToolset)\inc;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\inc;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/await /Zc:twoPhase- /Wv:18 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;..\..\$(PlatformShortName)\$(Configuration)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>WindowsApp.lib;Newdev.lib;Crypt32.lib;msi.lib;wcautil.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;ApplicationUpdate.lib;Notifications.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>WindowsApp.lib;Newdev.lib;Crypt32.lib;msi.lib;wcautil.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -114,7 +115,7 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\PowerToysBootstrapper\bootstrapper\RcResource.h" />
<ClInclude Include="RcResource.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="Telemetry\ProjectTelemetry.h" />

View File

@ -13,7 +13,7 @@
<Filter>Telemetry</Filter>
</ClInclude>
<ClInclude Include="resource.h" />
<ClInclude Include="..\PowerToysBootstrapper\bootstrapper\RcResource.h" />
<ClInclude Include="RcResource.h" />
</ItemGroup>
<ItemGroup>
<None Include="CustomAction.def" />

View File

@ -148,22 +148,12 @@ bool InstallNewVersionStage2(std::wstring installer_path, std::wstring_view inst
}
else
{
// If it's not .msi, then it's our .exe installer
// If it's not .msi, then it's a wix bootstrapper
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
sei.lpFile = installer_path.c_str();
sei.nShow = SW_SHOWNORMAL;
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";
}
std::wstring parameters = L"/passive";
sei.lpParameters = parameters.c_str();
success = ShellExecuteExW(&sei) == TRUE;

View File

@ -106,13 +106,14 @@ inline registry::ChangeSet getGcodeThumbnailHandlerChangeSet(const std::wstring
L".gcode");
}
inline std::vector<registry::ChangeSet> getAllModulesChangeSets(const std::wstring installationDir, const bool perUser)
inline std::vector<registry::ChangeSet> getAllModulesChangeSets(const std::wstring installationDir)
{
return { getSvgPreviewHandlerChangeSet(installationDir, perUser),
getMdPreviewHandlerChangeSet(installationDir, perUser),
getPdfPreviewHandlerChangeSet(installationDir, perUser),
getGcodePreviewHandlerChangeSet(installationDir, perUser),
getSvgThumbnailHandlerChangeSet(installationDir, perUser),
getPdfThumbnailHandlerChangeSet(installationDir, perUser),
getGcodeThumbnailHandlerChangeSet(installationDir, perUser) };
constexpr bool PER_USER = true;
return { getSvgPreviewHandlerChangeSet(installationDir, PER_USER),
getMdPreviewHandlerChangeSet(installationDir, PER_USER),
getPdfPreviewHandlerChangeSet(installationDir, PER_USER),
getGcodePreviewHandlerChangeSet(installationDir, PER_USER),
getSvgThumbnailHandlerChangeSet(installationDir, PER_USER),
getPdfThumbnailHandlerChangeSet(installationDir, PER_USER),
getGcodeThumbnailHandlerChangeSet(installationDir, PER_USER) };
}

View File

@ -25,7 +25,7 @@ PowerPreviewModule::PowerPreviewModule() :
Logger::init(LogSettings::fileExplorerLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
Logger::info("Initializing PowerPreviewModule");
const bool installPerUser = false;
const bool installPerUser = true;
m_fileExplorerModules.push_back({ .settingName = L"svg-previewer-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_PREVPANE_SVG_SETTINGS_DESCRIPTION),
.registryChanges = getSvgPreviewHandlerChangeSet(installationDir, installPerUser) });
@ -49,7 +49,7 @@ PowerPreviewModule::PowerPreviewModule() :
m_fileExplorerModules.push_back({ .settingName = L"pdf-thumbnail-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_PDF_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.registryChanges = getPdfThumbnailHandlerChangeSet(installationDir, installPerUser) });
m_fileExplorerModules.push_back({ .settingName = L"gcode-thumbnail-toggle-setting",
.settingDescription = GET_RESOURCE_STRING(IDS_GCODE_THUMBNAIL_PROVIDER_SETTINGS_DESCRIPTION),
.registryChanges = getGcodeThumbnailHandlerChangeSet(installationDir, installPerUser) });
@ -144,21 +144,13 @@ void PowerPreviewModule::enable()
// Disable active preview handlers.
void PowerPreviewModule::disable()
{
// Check if the process is elevated in order to have permissions to modify HKLM registry
if (is_process_elevated(false))
for (auto& fileExplorerModule : m_fileExplorerModules)
{
for (auto& fileExplorerModule : m_fileExplorerModules)
if (!fileExplorerModule.registryChanges.unApply())
{
if (!fileExplorerModule.registryChanges.unApply())
{
Logger::error(L"Couldn't disable file explorer module {} during module disable() call", fileExplorerModule.settingName);
}
Logger::error(L"Couldn't disable file explorer module {} during module disable() call", fileExplorerModule.settingName);
}
}
else
{
show_update_warning_message();
}
if (m_enabled)
{
@ -197,9 +189,7 @@ void PowerPreviewModule::show_update_warning_message()
void PowerPreviewModule::apply_settings(const PowerToysSettings::PowerToyValues& settings)
{
const bool isElevated = is_process_elevated(false);
bool notifyShell = false;
bool updatesNeeded = false;
for (auto& fileExplorerModule : m_fileExplorerModules)
{
@ -210,11 +200,6 @@ void PowerPreviewModule::apply_settings(const PowerToysSettings::PowerToyValues&
{
continue;
}
else
{
// Mark that updates were to the registry were needed
updatesNeeded = true;
}
// (Un)Apply registry changes depending on the new setting value
const bool updated = *toggle ? fileExplorerModule.registryChanges.apply() : fileExplorerModule.registryChanges.unApply();
@ -230,10 +215,6 @@ void PowerPreviewModule::apply_settings(const PowerToysSettings::PowerToyValues&
Trace::PowerPreviewSettingsUpdateFailed(fileExplorerModule.settingName.c_str(), !*toggle, *toggle, true);
}
}
if (!isElevated && updatesNeeded)
{
show_update_warning_message();
}
if (notifyShell)
{
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);

View File

@ -1006,9 +1006,6 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
<data name="FileExplorerPreview_RunAsAdminRequired.Title" xml:space="preserve">
<value>You need to run as administrator to modify these settings.</value>
</data>
<data name="FileExplorerPreview_AffectsAllUsers.Title" xml:space="preserve">
<value>The settings on this page affect all users on the system</value>
</data>
<data name="FileExplorerPreview_RebootRequired.Title" xml:space="preserve">
<value>A reboot may be required for changes to these settings to take effect</value>
</data>

View File

@ -20,25 +20,10 @@
<StackPanel Orientation="Vertical">
<muxc:InfoBar Severity="Warning"
x:Uid="FileExplorerPreview_RunAsAdminRequired"
IsOpen="True"
IsTabStop="True"
IsClosable="False"
Visibility="{Binding Mode=OneWay, Path=IsElevated, Converter={StaticResource BoolToVisibilityConverter}}" />
<muxc:InfoBar Severity="Informational"
x:Uid="FileExplorerPreview_AffectsAllUsers"
IsOpen="True"
IsTabStop="True"
IsClosable="False"
/>
<controls:SettingsGroup x:Uid="FileExplorerPreview_PreviewPane">
<controls:Setting x:Uid="FileExplorerPreview_ToggleSwitch_Preview_SVG" Icon="&#xE91B;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.SVGRenderIsEnabled}"
IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"
x:Uid="ToggleSwitch"/>
</controls:Setting.ActionContent>
</controls:Setting>
@ -46,7 +31,6 @@
<controls:Setting x:Uid="FileExplorerPreview_ToggleSwitch_Preview_MD" Icon="&#xE943;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.MDRenderIsEnabled}"
IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"
x:Uid="ToggleSwitch"/>
</controls:Setting.ActionContent>
</controls:Setting>
@ -54,7 +38,6 @@
<controls:Setting x:Uid="FileExplorerPreview_ToggleSwitch_Preview_PDF" Icon="&#xEA90;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.PDFRenderIsEnabled}"
IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"
x:Uid="ToggleSwitch"/>
</controls:Setting.ActionContent>
</controls:Setting>
@ -62,7 +45,6 @@
<controls:Setting x:Uid="FileExplorerPreview_ToggleSwitch_Preview_GCODE" Icon="&#xE81E;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.GCODERenderIsEnabled}"
IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"
x:Uid="ToggleSwitch"/>
</controls:Setting.ActionContent>
</controls:Setting>
@ -78,7 +60,6 @@
<controls:Setting x:Uid="FileExplorerPreview_ToggleSwitch_SVG_Thumbnail" Icon="&#xE91B;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.SVGThumbnailIsEnabled}"
IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"
x:Uid="ToggleSwitch"/>
</controls:Setting.ActionContent>
</controls:Setting>
@ -86,7 +67,6 @@
<controls:Setting x:Uid="FileExplorerPreview_ToggleSwitch_PDF_Thumbnail" Icon="&#xEA90;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.PDFThumbnailIsEnabled}"
IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"
x:Uid="ToggleSwitch"/>
</controls:Setting.ActionContent>
</controls:Setting>
@ -94,7 +74,6 @@
<controls:Setting x:Uid="FileExplorerPreview_ToggleSwitch_GCODE_Thumbnail" Icon="&#xE81E;">
<controls:Setting.ActionContent>
<ToggleSwitch IsOn="{x:Bind Mode=TwoWay, Path=ViewModel.GCODEThumbnailIsEnabled}"
IsEnabled="{Binding Mode=OneWay, Path=IsElevated}"
x:Uid="ToggleSwitch"/>
</controls:Setting.ActionContent>
</controls:Setting>
@ -107,4 +86,4 @@
<controls:PageLink x:Uid="LearnMore_PowerPreview" Link="https://aka.ms/PowerToysOverview_FileExplorerAddOns"/>
</controls:SettingsPageControl.PrimaryLinks>
</controls:SettingsPageControl>
</Page>
</Page>