diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt
index 2ef0425846..19d01faa1d 100644
--- a/.github/actions/spell-check/allow/code.txt
+++ b/.github/actions/spell-check/allow/code.txt
@@ -56,6 +56,8 @@ YVU
YVYU
zipfolder
CODEOWNERS
+VNext
+vnext
# FONTS
@@ -98,13 +100,25 @@ svgl
altdown
BUTTONUP
+bafunctions
+Baf
+Bitness
+BUILDARCHSHORT
CTRLALTDEL
Ctrls
+CSilent
+CBal
+CREATEBAFUNCTIONS
+CPrereq
+dirutil
+DUtil
+Editbox
EXSEL
HOLDENTER
HOLDESC
HOLDSPACE
HOLDBACKSPACE
+IDIGNORE
KBDLLHOOKSTRUCT
keyevent
LAlt
@@ -116,12 +130,16 @@ LCONTROL
LCtrl
LEFTDOWN
LEFTUP
+locutil
+logutil
+msimg
MBUTTON
MBUTTONDBLCLK
MBUTTONDOWN
MBUTTONUP
MIDDLEDOWN
MIDDLEUP
+memutil
NCRBUTTONDBLCLK
NCRBUTTONDOWN
NCRBUTTONUP
@@ -134,8 +152,18 @@ RCONTROL
RCtrl
RIGHTDOWN
RIGHTUP
+Richedit
+rgwz
+resrutil
+srd
+scz
+shelutil
+thmutil
+uriutil
VKTAB
+wcautil
winkey
+wininet
WMKEYDOWN
WMKEYUP
WMSYSKEYDOWN
@@ -145,6 +173,7 @@ XBUTTONDBLCLK
XBUTTONDOWN
XBUTTONUP
XDOWN
+xmlutil
# Prefix
pcs
diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
index ddb7b18844..de9c5a062b 100644
--- a/.github/actions/spell-check/expect.txt
+++ b/.github/actions/spell-check/expect.txt
@@ -400,6 +400,7 @@ DString
DSVG
DTo
DUMMYUNIONNAME
+dutil
DVASPECT
DVASPECTINFO
DVD
@@ -1789,6 +1790,7 @@ uncompilable
UNCPRIORITY
UNDNAME
UNICODETEXT
+unins
uninstalls
Uniquifies
unitconverter
diff --git a/.gitignore b/.gitignore
index 66532cc074..a1a09282a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -350,6 +350,10 @@ src/common/Telemetry/*.etl
# Generated installer file for Monaco source files.
/installer/PowerToysSetup/MonacoSRC.wxs
+/installer/PowerToysSetupVNext/MonacoSRC.wxs
# MSBuildCache
/MSBuildCacheLogs/
+
+# PowerToysInstaller Build Temp Files
+installer/*/*.wxs.bk
diff --git a/.pipelines/ESRPSigning_installer.json b/.pipelines/ESRPSigning_installer.json
index b20e2cdc82..cd96fb6f64 100644
--- a/.pipelines/ESRPSigning_installer.json
+++ b/.pipelines/ESRPSigning_installer.json
@@ -5,6 +5,8 @@
{
"MatchedPath": [
"PowerToysSetupCustomActions.dll",
+ "PowerToysSetupCustomActionsVNext.dll",
+ "SilentFilesInUseBAFunction.dll",
"PowerToys*Setup-*.exe",
"PowerToys*Setup-*.msi"
],
diff --git a/.pipelines/v2/release.yml b/.pipelines/v2/release.yml
index d6c2177720..e13792d8d1 100644
--- a/.pipelines/v2/release.yml
+++ b/.pipelines/v2/release.yml
@@ -20,6 +20,11 @@ parameters:
type: string
default: '0.0.1'
+ - name: installerSuffix
+ type: string
+ displayName: "WiX5 installer suffix (e.g., 'wix5', 'vnext', etc.)"
+ default: "wix5"
+
- name: buildConfigurations
displayName: "Build Configurations"
type: object
@@ -104,7 +109,8 @@ extends:
useManagedIdentity: $(SigningUseManagedIdentity)
clientId: $(SigningOriginalClientId)
# Have msbuild use the release nuget config profile
- additionalBuildOptions: /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:EnableCmdPalAOT=${{ parameters.enableAOT }}
+ additionalBuildOptions: /p:RestoreConfigFile="$(Build.SourcesDirectory)\.pipelines\release-nuget.config" /p:EnableCmdPalAOT=${{ parameters.enableAOT }} /p:InstallerSuffix=${{ parameters.installerSuffix }}
+ installerSuffix: ${{ parameters.installerSuffix }}
beforeBuildSteps:
# Sets versions for all PowerToy created DLLs
- pwsh: |-
diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml
index 948ef951c3..e345cdc3fa 100644
--- a/.pipelines/v2/templates/job-build-project.yml
+++ b/.pipelines/v2/templates/job-build-project.yml
@@ -62,6 +62,9 @@ parameters:
- name: versionNumber
type: string
default: '0.0.1'
+ - name: installerSuffix
+ type: string
+ default: "wix5"
- name: useLatestWinAppSDK
type: boolean
default: false
@@ -476,6 +479,23 @@ jobs:
additionalBuildOptions: ${{ parameters.additionalBuildOptions }}
buildUserInstaller: true # NOTE: This is the distinction between the above and below rules
+ - template: steps-build-installer-vnext.yml
+ parameters:
+ codeSign: ${{ parameters.codeSign }}
+ signingIdentity: ${{ parameters.signingIdentity }}
+ versionNumber: ${{ parameters.versionNumber }}
+ additionalBuildOptions: ${{ parameters.additionalBuildOptions }}
+ installerSuffix: ${{ parameters.installerSuffix }}
+
+ - template: steps-build-installer-vnext.yml
+ parameters:
+ codeSign: ${{ parameters.codeSign }}
+ signingIdentity: ${{ parameters.signingIdentity }}
+ versionNumber: ${{ parameters.versionNumber }}
+ additionalBuildOptions: ${{ parameters.additionalBuildOptions }}
+ installerSuffix: ${{ parameters.installerSuffix }}
+ buildUserInstaller: true # NOTE: This is the distinction between the above and below rules
+
# This saves ~1GiB per architecture. We won't need these later.
# Removes:
# - All .pdb files from any static libs .libs (which were only used during linking)
@@ -494,7 +514,9 @@ jobs:
- task: CopyFiles@2
displayName: Stage Installers
inputs:
- contents: "**/PowerToys*Setup-*.exe"
+ contents: |-
+ **/PowerToys*Setup-*.exe
+ !**/PowerToysSetupVNext/obj/**
flattenFolders: True
targetFolder: $(JobOutputDirectory)
@@ -510,26 +532,48 @@ jobs:
- pwsh: |-
$p = "$(JobOutputDirectory)\"
- $userHash = ((Get-Item $p\PowerToysUserSetup*.exe | Get-FileHash).Hash);
- $machineHash = ((Get-Item $p\PowerToysSetup*.exe | Get-FileHash).Hash);
- $userPlat = "hash_user_$(BuildPlatform).txt";
- $machinePlat = "hash_machine_$(BuildPlatform).txt";
- $combinedUserPath = $p + $userPlat;
- $combinedMachinePath = $p + $machinePlat;
-
- echo $p
-
- echo $userPlat
- echo $userHash
- echo $combinedUserPath
-
- echo $machinePlat
- echo $machineHash
- echo $combinedMachinePath
-
- $userHash | out-file -filepath $combinedUserPath
- $machineHash | out-file -filepath $combinedMachinePath
- displayName: Calculate file hashes
+ $installerSuffix = "${{ parameters.installerSuffix }}"
+
+ # Calculate hashes for regular installers (without custom suffix)
+ $userSetupFiles = Get-ChildItem -Path $p -Filter "PowerToysUserSetup*.exe" | Where-Object { $_.Name -notmatch "-$installerSuffix-" }
+ $machineSetupFiles = Get-ChildItem -Path $p -Filter "PowerToysSetup*.exe" | Where-Object { $_.Name -notmatch "-$installerSuffix-" -and $_.Name -notmatch "PowerToysUserSetup" }
+
+ if ($userSetupFiles.Count -gt 0) {
+ $userHash = ($userSetupFiles[0] | Get-FileHash).Hash;
+ $userPlat = "hash_user_$(BuildPlatform).txt";
+ $combinedUserPath = $p + $userPlat;
+ echo "Regular User: $userHash"
+ $userHash | out-file -filepath $combinedUserPath
+ }
+
+ if ($machineSetupFiles.Count -gt 0) {
+ $machineHash = ($machineSetupFiles[0] | Get-FileHash).Hash;
+ $machinePlat = "hash_machine_$(BuildPlatform).txt";
+ $combinedMachinePath = $p + $machinePlat;
+ echo "Regular Machine: $machineHash"
+ $machineHash | out-file -filepath $combinedMachinePath
+ }
+
+ # Calculate hashes for VNext installers (with custom suffix)
+ $userVNextFiles = Get-ChildItem -Path $p -Filter "PowerToysUserSetup*-$installerSuffix-*.exe"
+ $machineVNextFiles = Get-ChildItem -Path $p -Filter "PowerToysSetup*-$installerSuffix-*.exe" | Where-Object { $_.Name -notmatch "PowerToysUserSetup" }
+
+ if ($userVNextFiles.Count -gt 0) {
+ $userVNextHash = ($userVNextFiles[0] | Get-FileHash).Hash;
+ $userVNextPlat = "hash_user_vnext_$(BuildPlatform).txt";
+ $combinedUserVNextPath = $p + $userVNextPlat;
+ echo "VNext User: $userVNextHash"
+ $userVNextHash | out-file -filepath $combinedUserVNextPath
+ }
+
+ if ($machineVNextFiles.Count -gt 0) {
+ $machineVNextHash = ($machineVNextFiles[0] | Get-FileHash).Hash;
+ $machineVNextPlat = "hash_machine_vnext_$(BuildPlatform).txt";
+ $combinedMachineVNextPath = $p + $machineVNextPlat;
+ echo "VNext Machine: $machineVNextHash"
+ $machineVNextHash | out-file -filepath $combinedMachineVNextPath
+ }
+ displayName: Calculate file hashes for all installers
# Publishing the GPO files
- pwsh: |-
diff --git a/.pipelines/v2/templates/steps-build-installer-vnext.yml b/.pipelines/v2/templates/steps-build-installer-vnext.yml
new file mode 100644
index 0000000000..0f7908fceb
--- /dev/null
+++ b/.pipelines/v2/templates/steps-build-installer-vnext.yml
@@ -0,0 +1,208 @@
+parameters:
+ - name: versionNumber
+ type: string
+ default: "0.0.1"
+ - name: buildUserInstaller
+ type: boolean
+ default: false
+ - name: codeSign
+ type: boolean
+ default: false
+ - name: signingIdentity
+ type: object
+ default: {}
+ - name: additionalBuildOptions
+ type: string
+ default: ''
+ - name: installerSuffix
+ type: string
+ default: "wix5"
+
+steps:
+ # Install WiX 5.0.2 tools needed for VNext installer (matching project SDK)
+ - task: DotNetCoreCLI@2
+ displayName: Install WiX 5.0.2 tools
+ inputs:
+ command: 'custom'
+ custom: 'tool'
+ arguments: 'install --global wix --version 5.0.2'
+
+ - pwsh: |-
+ & git clean -xfd -e *exe -- .\installer\
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Clean installer to reduce cross-contamination
+
+ - pwsh: |-
+ # Determine whether this is a per-user build
+ $IsPerUser = $${{ parameters.buildUserInstaller }}
+
+ # Build slug used to locate the artifacts
+ $InstallerBuildSlug = if ($IsPerUser) { 'UserSetup' } else { 'MachineSetup' }
+
+ # VNext bundle folder; base name intentionally omits the VNext suffix
+ $InstallerFolder = 'PowerToysSetupVNext'
+ if ($IsPerUser) {
+ $InstallerBasename = "PowerToysUserSetup-${{ parameters.versionNumber }}-${{ parameters.installerSuffix }}-$(BuildPlatform)"
+ }
+ else {
+ $InstallerBasename = "PowerToysSetup-${{ parameters.versionNumber }}-${{ parameters.installerSuffix }}-$(BuildPlatform)"
+ }
+
+ # Export variables for downstream steps
+ Write-Host "##vso[task.setvariable variable=InstallerBuildSlug]$InstallerBuildSlug"
+ Write-Host "##vso[task.setvariable variable=InstallerRelativePath]$(BuildPlatform)\$(BuildConfiguration)\$InstallerBuildSlug"
+ Write-Host "##vso[task.setvariable variable=InstallerBasename]$InstallerBasename"
+ Write-Host "##vso[task.setvariable variable=InstallerFolder]$InstallerFolder"
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Prepare Installer variables
+
+ # This dll needs to be built and signed before building the MSI.
+ - task: VSBuild@1
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Build PowerToysSetupCustomActionsVNext
+ inputs:
+ solution: "**/installer/PowerToysSetup.sln"
+ vsVersion: 17.0
+ msbuildArgs: >-
+ /t:PowerToysSetupCustomActionsVNext
+ /p:RunBuildEvents=true;PerUser=${{parameters.buildUserInstaller}};RestorePackagesConfig=true;CIBuild=true
+ /p:InstallerSuffix=${{ parameters.installerSuffix }}
+ -restore -graph
+ /bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-actions.binlog
+ ${{ parameters.additionalBuildOptions }}
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+ clean: true
+ msbuildArchitecture: x64
+ maximumCpuCount: true
+
+ - ${{ if eq(parameters.codeSign, true) }}:
+ - template: steps-esrp-signing.yml
+ parameters:
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Sign PowerToysSetupCustomActionsVNext
+ signingIdentity: ${{ parameters.signingIdentity }}
+ inputs:
+ FolderPath: 'installer/PowerToysSetupCustomActionsVNext/$(InstallerRelativePath)'
+ signType: batchSigning
+ batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
+ ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
+
+ ## INSTALLER START
+ #### MSI BUILDING AND SIGNING
+ - task: VSBuild@1
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Build VNext MSI
+ inputs:
+ solution: "**/installer/PowerToysSetup.sln"
+ vsVersion: 17.0
+ msbuildArgs: >-
+ -restore
+ /t:PowerToysInstallerVNext
+ /p:RunBuildEvents=false;PerUser=${{parameters.buildUserInstaller}};BuildProjectReferences=false;CIBuild=true
+ /p:InstallerSuffix=${{ parameters.installerSuffix }}
+ /bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-msi.binlog
+ ${{ parameters.additionalBuildOptions }}
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+ clean: false # don't undo our hard work above by deleting the CustomActions dll
+ msbuildArchitecture: x64
+ maximumCpuCount: true
+
+ - script: |-
+ wix msi decompile installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).msi -x $(build.sourcesdirectory)\extractedMsi
+ dir $(build.sourcesdirectory)\extractedMsi
+ displayName: "${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} WiX5: Extract and verify MSI"
+
+ # Check if deps.json files don't reference different dll versions.
+ - pwsh: |-
+ & '.pipelines/verifyDepsJsonLibraryVersions.ps1' -targetDir '$(build.sourcesdirectory)\extractedMsi\File'
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Audit deps.json in MSI extracted files
+
+ - ${{ if eq(parameters.codeSign, true) }}:
+ - pwsh: |-
+ & .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMsi\File'
+ & .pipelines/versionAndSignCheck.ps1 -targetDir '$(build.sourcesdirectory)\extractedMsi\Binary'
+ git clean -xfd ./extractedMsi
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Verify all binaries are signed and versioned
+
+ - template: steps-esrp-signing.yml
+ parameters:
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Sign VNext MSI
+ signingIdentity: ${{ parameters.signingIdentity }}
+ inputs:
+ FolderPath: 'installer/$(InstallerFolder)/$(InstallerRelativePath)'
+ signType: batchSigning
+ batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
+ ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
+
+ #### END MSI
+ #### BOOTSTRAP BUILDING AND SIGNING
+ - task: VSBuild@1
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Build VNext Bootstrapper
+ inputs:
+ solution: "**/installer/PowerToysSetup.sln"
+ vsVersion: 17.0
+ msbuildArgs: >-
+ -restore
+ /t:PowerToysBootstrapperVNext
+ /p:PerUser=${{parameters.buildUserInstaller}};CIBuild=true
+ /p:InstallerSuffix=${{ parameters.installerSuffix }}
+ /bl:$(LogOutputDirectory)\installer-$(InstallerBuildSlug)-bootstrapper.binlog
+ -restore -graph
+ ${{ parameters.additionalBuildOptions }}
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+ clean: false # don't undo our hard work above by deleting the MSI
+ msbuildArchitecture: x64
+ maximumCpuCount: true
+
+ # The entirety of bundle unpacking/re-packing is unnecessary if we are not code signing it.
+ - ${{ if eq(parameters.codeSign, true) }}:
+ - script: |-
+ wix burn detach installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe -engine installer\engine.exe
+ displayName: "${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} WiX5: Extract Engine from Bundle"
+
+ - template: steps-esrp-signing.yml
+ parameters:
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Sign WiX Engine
+ signingIdentity: ${{ parameters.signingIdentity }}
+ inputs:
+ 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"
+ }
+ ]
+
+ - script: |-
+ wix burn reattach installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe -engine installer\engine.exe -o installer\$(InstallerFolder)\$(InstallerRelativePath)\$(InstallerBasename).exe
+ displayName: "${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} WiX5: Reattach Engine to Bundle"
+
+ - template: steps-esrp-signing.yml
+ parameters:
+ displayName: ${{replace(replace(parameters.buildUserInstaller,'True','π€'),'False','π»')}} Sign Final Bootstrapper
+ signingIdentity: ${{ parameters.signingIdentity }}
+ inputs:
+ FolderPath: 'installer/$(InstallerFolder)/$(InstallerRelativePath)'
+ signType: batchSigning
+ batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_installer.json'
+ ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
+
+ #### END BOOTSTRAP
+ ## END INSTALLER
diff --git a/Directory.Packages.props b/Directory.Packages.props
index a5270e7d75..649c927a64 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -108,6 +108,14 @@
+
+
+
+
+
+
+
+
diff --git a/doc/devdocs/core/installer.md b/doc/devdocs/core/installer.md
index b4619e26cd..8e9f008f2e 100644
--- a/doc/devdocs/core/installer.md
+++ b/doc/devdocs/core/installer.md
@@ -1,6 +1,6 @@
# PowerToys Installer
-## Installer Architecture (WiX 3)
+## Installer Architecture (WiX 3/ WiX 5)
- Uses a bootstrapper to check dependencies and close PowerToys
- MSI defined in product.wxs
@@ -22,7 +22,7 @@
### MSI Installer Build Process
-- First builds `PowerToysSetupCustomActions` DLL and signs it
+- First builds `PowerToysSetupCustomActions` DLL and signs it, for WiX5 project, installer will build `PowerToysSetupCustomActionsVNext` DLL.
- Then builds the installer without cleaning, to reuse the signed DLL
- Uses PowerShell scripts to modify .wxs files before build
- Restores original .wxs files after build completes
@@ -135,6 +135,23 @@ If you prefer, you can alternatively build prerequisite projects for the install
The resulting `PowerToysSetup.msi` installer will be available in the `installer\PowerToysSetup\x64\Release\` folder.
+For WiX3 project, run `Developer Command Prompt for VS 2022` in admin mode and execute the following command to build the installer. The generated installer package will be located at `\installer\PowerToysSetup\{platform}\Release\MachineSetup`.
+
+```
+git clean -xfd -e *exe -- .\installer\
+MSBuild -t:restore .\installer\PowerToysSetup.sln -p:RestorePackagesConfig=true /p:Platform="x64" /p:Configuration=Release
+MSBuild -m .\installer\PowerToysSetup.sln /t:PowerToysInstaller /p:Configuration=Release /p:Platform="x64"
+MSBuild -m .\installer\PowerToysSetup.sln /t:PowerToysBootstrapper /p:Configuration=Release /p:Platform="x64"
+```
+For WiX5 project, run `Developer Command Prompt for VS 2022` in admin mode and execute the following command to build the installer. The generated installer package will be located at `\installer\PowerToysSetupVNext\{platform}\Release\MachineSetup`.
+
+```
+git clean -xfd -e *exe -- .\installer\
+MSBuild -t:restore .\installer\PowerToysSetup.sln -p:RestorePackagesConfig=true /p:Platform="x64" /p:Configuration=Release
+MSBuild -t:Restore -m .\installer\PowerToysSetup.sln /t:PowerToysInstallerVNext /p:Configuration=Release /p:Platform="x64"
+MSBuild -t:Restore -m .\installer\PowerToysSetup.sln /t:PowerToysBootstrapperVNext /p:Configuration=Release /p:Platform="x64"
+```
+
### Supported arguments for the .EXE Bootstrapper installer
Head over to the wiki to see the [full list of supported installer arguments][installerArgWiki].
diff --git a/installer/PowerToysSetup.sln b/installer/PowerToysSetup.sln
index 540ef43d23..47d0c56070 100644
--- a/installer/PowerToysSetup.sln
+++ b/installer/PowerToysSetup.sln
@@ -1,5 +1,4 @@
-ο»Ώ
-Microsoft Visual Studio Solution File, Format Version 12.00
+ο»ΏMicrosoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32414.318
MinimumVisualStudioVersion = 10.0.40219.1
@@ -17,6 +16,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Version", "..\src\common\ve
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "..\src\common\Telemetry\EtwTrace\EtwTrace.vcxproj", "{8F021B46-362B-485C-BFBA-CCF83E820CBD}"
EndProject
+Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysInstallerVNext", "PowerToysSetupVNext\PowerToysInstallerVNext.wixproj", "{B6E94700-DF38-41F6-A3FD-18B69674AB1E}"
+EndProject
+Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysBootstrapperVNext", "PowerToysSetupVNext\PowerToysBootstrapperVNext.wixproj", "{DA4E9744-80BE-424C-B0F5-AFD8757DB575}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToysSetupCustomActionsVNext", "PowerToysSetupCustomActionsVNext\PowerToysSetupCustomActionsVNext.vcxproj", "{B3A354B0-1E54-4B55-A962-FB5AF9330C19}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SilentFilesInUseBAFunction", "PowerToysSetupVNext\SilentFilesInUseBA\SilentFilesInUseBAFunction.vcxproj", "{F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -78,6 +85,36 @@ Global
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|ARM64.Build.0 = Release|ARM64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.ActiveCfg = Release|x64
{8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.Build.0 = Release|x64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|ARM64.Build.0 = Debug|ARM64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|x64.ActiveCfg = Debug|x64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Debug|x64.Build.0 = Debug|x64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|ARM64.ActiveCfg = Release|ARM64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|ARM64.Build.0 = Release|ARM64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|x64.ActiveCfg = Release|x64
+ {B6E94700-DF38-41F6-A3FD-18B69674AB1E}.Release|x64.Build.0 = Release|x64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|ARM64.Build.0 = Debug|ARM64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|x64.ActiveCfg = Debug|x64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Debug|x64.Build.0 = Debug|x64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|ARM64.ActiveCfg = Release|ARM64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|ARM64.Build.0 = Release|ARM64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|x64.ActiveCfg = Release|x64
+ {DA4E9744-80BE-424C-B0F5-AFD8757DB575}.Release|x64.Build.0 = Release|x64
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Debug|x64.ActiveCfg = Debug|x64
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Debug|x64.Build.0 = Debug|x64
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|ARM64.ActiveCfg = Release|ARM64
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|ARM64.Build.0 = Release|ARM64
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|x64.ActiveCfg = Release|x64
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}.Release|x64.Build.0 = Release|x64
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Debug|x64.ActiveCfg = Debug|x64
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Debug|x64.Build.0 = Debug|x64
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|ARM64.ActiveCfg = Release|ARM64
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|ARM64.Build.0 = Release|ARM64
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|x64.ActiveCfg = Release|x64
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -85,4 +122,4 @@ Global
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B7A3DA30-D443-40FF-AC51-988AD41E3962}
EndGlobalSection
-EndGlobal
+EndGlobal
\ No newline at end of file
diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj
index f76d15b73a..6a28fbc896 100644
--- a/installer/PowerToysSetup/PowerToysInstaller.wixproj
+++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj
@@ -57,7 +57,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
call move /Y ..\..\..\WinAppSDK.wxs.bk ..\..\..\WinAppSDK.wxs
call move /Y ..\..\..\WinUI3Applications.wxs.bk ..\..\..\WinUI3Applications.wxs
call move /Y ..\..\..\Workspaces.wxs.bk ..\..\..\Workspaces.wxs
-
+
PowerToysInstaller
diff --git a/installer/PowerToysSetup/publish.cmd b/installer/PowerToysSetup/publish.cmd
index c6113bf285..f61668cffe 100644
--- a/installer/PowerToysSetup/publish.cmd
+++ b/installer/PowerToysSetup/publish.cmd
@@ -14,4 +14,4 @@ msbuild !PTRoot!\src\modules\previewpane\MarkdownPreviewHandler\MarkdownPreviewH
msbuild !PTRoot!\src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
-msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvider.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
+msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvider.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
\ No newline at end of file
diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj
index 09ed1ee31a..0974bddbf9 100644
--- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj
+++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj
@@ -81,7 +81,7 @@
if not "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetup\generateAllFileComponents.ps1 -platform $(Platform)
if "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetup\generateAllFileComponents.ps1 -platform $(Platform) -installscopeperuser $(NormalizedPerUserValue)
- Backing up original files and populating .NET and WPF Runtime dependencies
+ Backing up original files and populating .NET and WPF Runtime dependencies for WiX3 based installer
diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp
new file mode 100644
index 0000000000..8c3ad76448
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp
@@ -0,0 +1,1411 @@
+#include "pch.h"
+#include "resource.h"
+#include "RcResource.h"
+#include
+#include
+
+#include "../../src/common/logger/logger.h"
+#include "../../src/common/utils/gpo.h"
+#include "../../src/common/utils/MsiUtils.h"
+#include "../../src/common/utils/modulesRegistry.h"
+#include "../../src/common/updating/installer.h"
+#include "../../src/common/version/version.h"
+#include "../../src/common/Telemetry/EtwTrace/EtwTrace.h"
+#include "../../src/common/utils/package.h"
+#include "../../src/common/utils/clean_video_conference.h"
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+HINSTANCE DLL_HANDLE = nullptr;
+
+TRACELOGGING_DEFINE_PROVIDER(
+ g_hProvider,
+ "Microsoft.PowerToys",
+ // {38e8889b-9731-53f5-e901-e8a7c1753074}
+ (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
+ TraceLoggingOptionProjectTelemetry());
+
+const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
+const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
+
+static const wchar_t *POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
+static const wchar_t *POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
+
+constexpr inline const wchar_t *DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys";
+constexpr inline const wchar_t *DataDiagnosticsRegValueName = L"AllowDataDiagnostics";
+
+#define TraceLoggingWriteWrapper(provider, eventName, ...) \
+ if (isDataDiagnosticEnabled()) \
+ { \
+ trace.UpdateState(true); \
+ TraceLoggingWrite(provider, eventName, __VA_ARGS__); \
+ trace.Flush(); \
+ trace.UpdateState(false); \
+ }
+
+static Shared::Trace::ETWTrace trace{L"PowerToys_Installer"};
+
+inline bool isDataDiagnosticEnabled()
+{
+ HKEY key{};
+ if (RegOpenKeyExW(HKEY_CURRENT_USER,
+ DataDiagnosticsRegKey,
+ 0,
+ KEY_READ,
+ &key) != ERROR_SUCCESS)
+ {
+ return false;
+ }
+
+ DWORD isDataDiagnosticsEnabled = 0;
+ DWORD size = sizeof(isDataDiagnosticsEnabled);
+
+ if (RegGetValueW(
+ HKEY_CURRENT_USER,
+ DataDiagnosticsRegKey,
+ DataDiagnosticsRegValueName,
+ RRF_RT_REG_DWORD,
+ nullptr,
+ &isDataDiagnosticsEnabled,
+ &size) != ERROR_SUCCESS)
+ {
+ RegCloseKey(key);
+ return false;
+ }
+ RegCloseKey(key);
+
+ return isDataDiagnosticsEnabled == 1;
+}
+
+HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring &installationDir)
+{
+ DWORD len = 0;
+ wchar_t _[1];
+ MsiGetPropertyW(hInstall, L"CustomActionData", _, &len);
+ len += 1;
+ installationDir.resize(len);
+ HRESULT hr = MsiGetPropertyW(hInstall, L"CustomActionData", installationDir.data(), &len);
+ if (installationDir.length())
+ {
+ installationDir.resize(installationDir.length() - 1);
+ }
+ ExitOnFailure(hr, "Failed to get INSTALLFOLDER property.");
+LExit:
+ return hr;
+}
+
+BOOL IsLocalSystem()
+{
+ HANDLE hToken;
+ UCHAR bTokenUser[sizeof(TOKEN_USER) + 8 + 4 * SID_MAX_SUB_AUTHORITIES];
+ PTOKEN_USER pTokenUser = (PTOKEN_USER)bTokenUser;
+ ULONG cbTokenUser;
+ SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
+ PSID pSystemSid;
+ BOOL bSystem;
+
+ // open process token
+ if (!OpenProcessToken(GetCurrentProcess(),
+ TOKEN_QUERY,
+ &hToken))
+ return FALSE;
+
+ // retrieve user SID
+ if (!GetTokenInformation(hToken, TokenUser, pTokenUser,
+ sizeof(bTokenUser), &cbTokenUser))
+ {
+ CloseHandle(hToken);
+ return FALSE;
+ }
+
+ CloseHandle(hToken);
+
+ // allocate LocalSystem well-known SID
+ if (!AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID,
+ 0, 0, 0, 0, 0, 0, 0, &pSystemSid))
+ return FALSE;
+
+ // compare the user SID from the token with the LocalSystem SID
+ bSystem = EqualSid(pTokenUser->User.Sid, pSystemSid);
+
+ FreeSid(pSystemSid);
+
+ return bSystem;
+}
+
+BOOL ImpersonateLoggedInUserAndDoSomething(std::function action)
+{
+ HRESULT hr = S_OK;
+ HANDLE hUserToken = NULL;
+ DWORD dwSessionId;
+ ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId);
+ auto rv = WTSQueryUserToken(dwSessionId, &hUserToken);
+
+ if (rv == 0)
+ {
+ hr = E_ABORT;
+ ExitOnFailure(hr, "Failed to query user token");
+ }
+
+ HANDLE hUserTokenDup;
+ if (DuplicateTokenEx(hUserToken, TOKEN_ALL_ACCESS, NULL, SECURITY_IMPERSONATION_LEVEL::SecurityImpersonation, TOKEN_TYPE::TokenPrimary, &hUserTokenDup) == 0)
+ {
+ CloseHandle(hUserToken);
+ CloseHandle(hUserTokenDup);
+ hr = E_ABORT;
+ ExitOnFailure(hr, "Failed to duplicate user token");
+ }
+
+ if (ImpersonateLoggedOnUser(hUserTokenDup))
+ {
+ if (!action(hUserTokenDup))
+ {
+ hr = E_ABORT;
+ ExitOnFailure(hr, "Failed to execute action");
+ }
+
+ RevertToSelf();
+ CloseHandle(hUserToken);
+ CloseHandle(hUserTokenDup);
+ }
+ else
+ {
+ hr = E_ABORT;
+ ExitOnFailure(hr, "Failed to duplicate user token");
+ }
+
+LExit:
+ return SUCCEEDED(hr);
+}
+
+static std::filesystem::path GetUserPowerShellModulesPath()
+{
+ PWSTR myDocumentsBlockPtr;
+
+ if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &myDocumentsBlockPtr)))
+ {
+ const std::wstring myDocuments{myDocumentsBlockPtr};
+ CoTaskMemFree(myDocumentsBlockPtr);
+ return std::filesystem::path(myDocuments) / "PowerShell" / "Modules";
+ }
+ else
+ {
+ CoTaskMemFree(myDocumentsBlockPtr);
+ return {};
+ }
+}
+
+UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder, path, args;
+ std::wstring commandLine;
+
+ hr = WcaInitialize(hInstall, "LaunchPowerToys");
+ ExitOnFailure(hr, "Failed to initialize");
+ hr = getInstallFolder(hInstall, installationFolder);
+ ExitOnFailure(hr, "Failed to get installFolder.");
+
+ path = installationFolder;
+ path += L"\\PowerToys.exe";
+
+ args = L"--dont-elevate";
+
+ commandLine = L"\"" + path + L"\" ";
+ commandLine += args;
+
+ BOOL isSystemUser = IsLocalSystem();
+
+ if (isSystemUser)
+ {
+
+ auto action = [&commandLine](HANDLE userToken)
+ {
+ STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL};
+ PROCESS_INFORMATION processInformation;
+
+ PVOID lpEnvironment = NULL;
+ CreateEnvironmentBlock(&lpEnvironment, userToken, FALSE);
+
+ CreateProcessAsUser(
+ userToken,
+ NULL,
+ commandLine.data(),
+ NULL,
+ NULL,
+ FALSE,
+ CREATE_DEFAULT_ERROR_MODE | CREATE_UNICODE_ENVIRONMENT,
+ lpEnvironment,
+ NULL,
+ &startupInfo,
+ &processInformation);
+
+ if (!CloseHandle(processInformation.hProcess))
+ {
+ return false;
+ }
+ if (!CloseHandle(processInformation.hThread))
+ {
+ return false;
+ }
+
+ return true;
+ };
+
+ if (!ImpersonateLoggedInUserAndDoSomething(action))
+ {
+ hr = E_ABORT;
+ ExitOnFailure(hr, "ImpersonateLoggedInUserAndDoSomething failed");
+ }
+ }
+ else
+ {
+ STARTUPINFO startupInfo{.cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL};
+
+ PROCESS_INFORMATION processInformation;
+
+ // Start the resizer
+ CreateProcess(
+ NULL,
+ commandLine.data(),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &startupInfo,
+ &processInformation);
+
+ if (!CloseHandle(processInformation.hProcess))
+ {
+ hr = E_ABORT;
+ ExitOnFailure(hr, "Failed to close process handle");
+ }
+ if (!CloseHandle(processInformation.hThread))
+ {
+ hr = E_ABORT;
+ ExitOnFailure(hr, "Failed to close thread handle");
+ }
+ }
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall CheckGPOCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+
+ hr = WcaInitialize(hInstall, "CheckGPOCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ LPWSTR currentScope = nullptr;
+ hr = WcaGetProperty(L"InstallScope", ¤tScope);
+
+ if (std::wstring{currentScope} == L"perUser")
+ {
+ if (powertoys_gpo::getDisablePerUserInstallationValue() == powertoys_gpo::gpo_rule_configured_enabled)
+ {
+ PMSIHANDLE hRecord = MsiCreateRecord(0);
+ MsiRecordSetString(hRecord, 0, TEXT("The system administrator has disabled per-user installation."));
+ MsiProcessMessage(hInstall, static_cast(INSTALLMESSAGE_ERROR + MB_OK), hRecord);
+ hr = E_ABORT;
+ }
+ }
+
+LExit:
+ UINT er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+// We've deprecated Video Conference Mute. This Custom Action cleans up any stray registry entry for the driver dll.
+UINT __stdcall CleanVideoConferenceRegistryCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "CleanVideoConferenceRegistry");
+ ExitOnFailure(hr, "Failed to initialize");
+ clean_video_conference();
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+ bool failedToApply = false;
+
+ hr = WcaInitialize(hInstall, "ApplyModulesRegistryChangeSets");
+ ExitOnFailure(hr, "Failed to initialize");
+ hr = getInstallFolder(hInstall, installationFolder);
+ ExitOnFailure(hr, "Failed to get installFolder.");
+
+ for (const auto &changeSet : getAllOnByDefaultModulesChangeSets(installationFolder))
+ {
+ if (!changeSet.apply())
+ {
+ Logger::error(L"Couldn't apply registry changeSet");
+ failedToApply = true;
+ }
+ }
+
+ if (!failedToApply)
+ {
+ Logger::info(L"All registry changeSets applied successfully");
+ }
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UnApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+
+ hr = WcaInitialize(hInstall, "UndoModulesRegistryChangeSets"); // original func name is too long
+ ExitOnFailure(hr, "Failed to initialize");
+ hr = getInstallFolder(hInstall, installationFolder);
+ ExitOnFailure(hr, "Failed to get installFolder.");
+ for (const auto &changeSet : getAllModulesChangeSets(installationFolder))
+ {
+ changeSet.unApply();
+ }
+
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
+
+ ExitOnFailure(hr, "Failed to extract msix");
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+const wchar_t *DSC_CONFIGURE_PSD1_NAME = L"Microsoft.PowerToys.Configure.psd1";
+const wchar_t *DSC_CONFIGURE_PSM1_NAME = L"Microsoft.PowerToys.Configure.psm1";
+
+UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+
+ hr = WcaInitialize(hInstall, "InstallDSCModuleCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ hr = getInstallFolder(hInstall, installationFolder);
+ ExitOnFailure(hr, "Failed to get installFolder.");
+
+ {
+ const auto baseModulesPath = GetUserPowerShellModulesPath();
+ if (baseModulesPath.empty())
+ {
+ hr = E_FAIL;
+ ExitOnFailure(hr, "Unable to determine Powershell modules path");
+ }
+
+ const auto modulesPath = baseModulesPath / L"Microsoft.PowerToys.Configure" / (get_product_version(false) + L".0");
+
+ std::error_code errorCode;
+ fs::create_directories(modulesPath, errorCode);
+ if (errorCode)
+ {
+ hr = E_FAIL;
+ ExitOnFailure(hr, "Unable to create Powershell modules folder");
+ }
+
+ for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
+ {
+ fs::copy_file(fs::path(installationFolder) / "DSCModules" / filename, modulesPath / filename, fs::copy_options::overwrite_existing, errorCode);
+
+ if (errorCode)
+ {
+ hr = E_FAIL;
+ ExitOnFailure(hr, "Unable to copy Powershell modules file");
+ }
+ }
+ }
+
+LExit:
+ if (SUCCEEDED(hr))
+ {
+ er = ERROR_SUCCESS;
+ Logger::info(L"DSC module was installed!");
+ }
+ else
+ {
+ er = ERROR_INSTALL_FAILURE;
+ Logger::error(L"Couldn't install DSC module!");
+ }
+
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "UninstallDSCModuleCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ {
+ const auto baseModulesPath = GetUserPowerShellModulesPath();
+ if (baseModulesPath.empty())
+ {
+ hr = E_FAIL;
+ ExitOnFailure(hr, "Unable to determine Powershell modules path");
+ }
+
+ const auto powerToysModulePath = baseModulesPath / L"Microsoft.PowerToys.Configure";
+ const auto versionedModulePath = powerToysModulePath / (get_product_version(false) + L".0");
+
+ std::error_code errorCode;
+
+ for (const auto *filename : {DSC_CONFIGURE_PSD1_NAME, DSC_CONFIGURE_PSM1_NAME})
+ {
+ fs::remove(versionedModulePath / filename, errorCode);
+
+ if (errorCode)
+ {
+ hr = E_FAIL;
+ ExitOnFailure(hr, "Unable to delete DSC file");
+ }
+ }
+
+ for (const auto *modulePath : {&versionedModulePath, &powerToysModulePath})
+ {
+ fs::remove(*modulePath, errorCode);
+
+ if (errorCode)
+ {
+ hr = E_FAIL;
+ ExitOnFailure(hr, "Unable to delete DSC folder");
+ }
+ }
+ }
+
+LExit:
+ if (SUCCEEDED(hr))
+ {
+ er = ERROR_SUCCESS;
+ Logger::info(L"DSC module was uninstalled!");
+ }
+ else
+ {
+ er = ERROR_INSTALL_FAILURE;
+ Logger::error(L"Couldn't uninstall DSC module!");
+ }
+
+ return WcaFinalize(er);
+}
+
+UINT __stdcall InstallEmbeddedMSIXCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "InstallEmbeddedMSIXCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ if (auto msix = RcResource::create(IDR_BIN_MSIX_HELLO_PACKAGE, L"BIN", DLL_HANDLE))
+ {
+ Logger::info(L"Extracted MSIX");
+ // TODO: Use to activate embedded MSIX
+ const auto msix_path = std::filesystem::temp_directory_path() / "hello_package.msix";
+ if (!msix->saveAsFile(msix_path))
+ {
+ ExitOnFailure(hr, "Failed to save msix");
+ }
+ Logger::info(L"Saved MSIX");
+ using namespace winrt::Windows::Management::Deployment;
+ using namespace winrt::Windows::Foundation;
+
+ Uri msix_uri{msix_path.wstring()};
+ PackageManager pm;
+ auto result = pm.AddPackageAsync(msix_uri, nullptr, DeploymentOptions::None).get();
+ if (!result)
+ {
+ ExitOnFailure(hr, "Failed to AddPackage");
+ }
+
+ Logger::info(L"MSIX[s] were installed!");
+ }
+ else
+ {
+ ExitOnFailure(hr, "Failed to extract msix");
+ }
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UninstallEmbeddedMSIXCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ using namespace winrt::Windows::Management::Deployment;
+ using namespace winrt::Windows::Foundation;
+ // TODO: This must be replaced with the actual publisher and package name
+ const wchar_t package_name[] = L"46b35c25-b593-48d5-aeb1-d3e9c3b796e9";
+ const wchar_t publisher[] = L"CN=yuyoyuppe";
+ PackageManager pm;
+
+ hr = WcaInitialize(hInstall, "UninstallEmbeddedMSIXCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ for (const auto &p : pm.FindPackagesForUser({}, package_name, publisher))
+ {
+ auto result = pm.RemovePackageAsync(p.Id().FullName()).get();
+ if (result)
+ {
+ Logger::info(L"MSIX was uninstalled!");
+ }
+ else
+ {
+ Logger::error(L"Couldn't uninstall MSIX!");
+ }
+ }
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall RemoveWindowsServiceByName(std::wstring serviceName)
+{
+ SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+
+ if (!hSCManager)
+ {
+ return ERROR_INSTALL_FAILURE;
+ }
+
+ SC_HANDLE hService = OpenService(hSCManager, serviceName.c_str(), SERVICE_STOP | DELETE);
+ if (!hService)
+ {
+ CloseServiceHandle(hSCManager);
+ return ERROR_INSTALL_FAILURE;
+ }
+
+ SERVICE_STATUS ss;
+ if (ControlService(hService, SERVICE_CONTROL_STOP, &ss))
+ {
+ Sleep(1000);
+ while (QueryServiceStatus(hService, &ss))
+ {
+ if (ss.dwCurrentState == SERVICE_STOP_PENDING)
+ {
+ Sleep(1000);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ BOOL deleteResult = DeleteService(hService);
+ CloseServiceHandle(hService);
+ CloseServiceHandle(hSCManager);
+
+ if (!deleteResult)
+ {
+ return ERROR_INSTALL_FAILURE;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall UnsetAdvancedPasteAPIKeyCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ try
+ {
+ winrt::Windows::Security::Credentials::PasswordVault vault;
+ winrt::Windows::Security::Credentials::PasswordCredential cred;
+
+ hr = WcaInitialize(hInstall, "UnsetAdvancedPasteAPIKey");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ cred = vault.Retrieve(L"https://platform.openai.com/api-keys", L"PowerToys_AdvancedPaste_OpenAIKey");
+ vault.Remove(cred);
+ }
+ catch (...)
+ {
+ }
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+ std::string command;
+
+ hr = WcaInitialize(hInstall, "UninstallCommandNotFoundModule");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ hr = getInstallFolder(hInstall, installationFolder);
+ ExitOnFailure(hr, "Failed to get installFolder.");
+
+#ifdef _M_ARM64
+ command = "powershell.exe";
+ command += " ";
+ command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted";
+ command += " -Command ";
+ command += "\"[Environment]::SetEnvironmentVariable('PATH', [Environment]::GetEnvironmentVariable('PATH', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('PATH', 'User'), 'Process');";
+ command += "pwsh.exe -NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File '" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "'\"";
+#else
+ command = "pwsh.exe";
+ command += " ";
+ command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\DisableModule.ps1" + "\"";
+#endif
+
+ system(command.c_str());
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UpgradeCommandNotFoundModuleCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+ std::string command;
+
+ hr = WcaInitialize(hInstall, "UpgradeCommandNotFoundModule");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ hr = getInstallFolder(hInstall, installationFolder);
+ ExitOnFailure(hr, "Failed to get installFolder.");
+
+ command = "pwsh.exe";
+ command += " ";
+ command += "-NoProfile -NonInteractive -NoLogo -WindowStyle Hidden -ExecutionPolicy Unrestricted -File \"" + winrt::to_string(installationFolder) + "\\WinUI3Apps\\Assets\\Settings\\Scripts\\UpgradeModule.ps1" + "\"";
+
+ system(command.c_str());
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UninstallServicesCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "UninstallServicesCA");
+
+ ExitOnFailure(hr, "Failed to initialize");
+
+ hr = RemoveWindowsServiceByName(L"PowerToys.MWB.Service");
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+// Removes all Scheduled Tasks in the PowerToys folder and deletes the folder afterwards.
+// Based on the Task Scheduler Displaying Task Names and State example:
+// https://learn.microsoft.com/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/
+UINT __stdcall RemoveScheduledTasksCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ ITaskService *pService = nullptr;
+ ITaskFolder *pTaskFolder = nullptr;
+ IRegisteredTaskCollection *pTaskCollection = nullptr;
+ ITaskFolder *pRootFolder = nullptr;
+ LONG numTasks = 0;
+
+ hr = WcaInitialize(hInstall, "RemoveScheduledTasksCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ Logger::info(L"RemoveScheduledTasksCA Initialized.");
+
+ // COM and Security Initialization is expected to have been done by the MSI.
+ // It couldn't be done in the DLL, anyway.
+ // ------------------------------------------------------
+ // Create an instance of the Task Service.
+ hr = CoCreateInstance(CLSID_TaskScheduler,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_ITaskService,
+ reinterpret_cast(&pService));
+ ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
+
+ // Connect to the task service.
+ hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
+ ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
+
+ // ------------------------------------------------------
+ // Get the PowerToys task folder.
+ hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
+ if (FAILED(hr))
+ {
+ // Folder doesn't exist. No need to delete anything.
+ Logger::info(L"The PowerToys scheduled task folder wasn't found. Nothing to delete.");
+ hr = S_OK;
+ ExitFunction();
+ }
+
+ // -------------------------------------------------------
+ // Get the registered tasks in the folder.
+ hr = pTaskFolder->GetTasks(TASK_ENUM_HIDDEN, &pTaskCollection);
+ ExitOnFailure(hr, "Cannot get the registered tasks: %x", hr);
+
+ hr = pTaskCollection->get_Count(&numTasks);
+ for (LONG i = 0; i < numTasks; i++)
+ {
+ // Delete all the tasks found.
+ // If some tasks can't be deleted, the folder won't be deleted later and the user will still be notified.
+ IRegisteredTask *pRegisteredTask = nullptr;
+ hr = pTaskCollection->get_Item(_variant_t(i + 1), &pRegisteredTask);
+ if (SUCCEEDED(hr))
+ {
+ BSTR taskName = nullptr;
+ hr = pRegisteredTask->get_Name(&taskName);
+ if (SUCCEEDED(hr))
+ {
+ hr = pTaskFolder->DeleteTask(taskName, 0);
+ if (FAILED(hr))
+ {
+ Logger::error(L"Cannot delete the {} task: {}", taskName, hr);
+ }
+ SysFreeString(taskName);
+ }
+ else
+ {
+ Logger::error(L"Cannot get the registered task name: {}", hr);
+ }
+ pRegisteredTask->Release();
+ }
+ else
+ {
+ Logger::error(L"Cannot get the registered task item at index={}: {}", i + 1, hr);
+ }
+ }
+
+ // ------------------------------------------------------
+ // Get the pointer to the root task folder and delete the PowerToys subfolder.
+ hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
+ ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
+ hr = pRootFolder->DeleteFolder(_bstr_t(L"PowerToys"), 0);
+ pRootFolder->Release();
+ ExitOnFailure(hr, "Cannot delete the PowerToys folder: %x", hr);
+
+ Logger::info(L"Deleted the PowerToys Task Scheduler folder.");
+
+LExit:
+ if (pService)
+ {
+ pService->Release();
+ }
+ if (pTaskFolder)
+ {
+ pTaskFolder->Release();
+ }
+ if (pTaskCollection)
+ {
+ pTaskCollection->Release();
+ }
+
+ if (!SUCCEEDED(hr))
+ {
+ PMSIHANDLE hRecord = MsiCreateRecord(0);
+ MsiRecordSetString(hRecord, 0, TEXT("Failed to remove the PowerToys folder from the scheduled task. These can be removed manually later."));
+ MsiProcessMessage(hInstall, static_cast(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
+ }
+
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogInstallSuccessCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "Install_Success",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogInstallCancelCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "Install_Cancel",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogInstallFailCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "Install_Fail",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogUninstallSuccessCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "UnInstall_Success",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogUninstallCancelCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "UnInstall_Cancel",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogUninstallFailCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "UnInstall_Fail",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogRepairCancelCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "Repair_Cancel",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "TelemetryLogRepairFailCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ TraceLoggingWriteWrapper(
+ g_hProvider,
+ "Repair_Fail",
+ TraceLoggingWideString(get_product_version().c_str(), "Version"),
+ ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
+ TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
+ TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
+
+LExit:
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "DetectPrevInstallPathCA");
+ MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", L"");
+
+ LPWSTR currentScope = nullptr;
+ hr = WcaGetProperty(L"InstallScope", ¤tScope);
+
+ try
+ {
+ if (auto install_path = GetMsiPackageInstalledPath(std::wstring{currentScope} == L"perUser"))
+ {
+ MsiSetPropertyW(hInstall, L"PREVIOUSINSTALLFOLDER", install_path->data());
+ }
+ }
+ catch (...)
+ {
+ }
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall InstallCmdPalPackageCA(MSIHANDLE hInstall)
+{
+ using namespace winrt::Windows::Foundation;
+ using namespace winrt::Windows::Management::Deployment;
+
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ std::wstring installationFolder;
+
+ hr = WcaInitialize(hInstall, "InstallCmdPalPackage");
+ hr = getInstallFolder(hInstall, installationFolder);
+
+ try
+ {
+ auto msix = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\", false);
+ auto dependencies = package::FindMsixFile(installationFolder + L"\\WinUI3Apps\\CmdPal\\Dependencies\\", true);
+
+ if (!msix.empty())
+ {
+ auto msixPath = msix[0];
+
+ if (!package::RegisterPackage(msixPath, dependencies))
+ {
+ Logger::error(L"Failed to install CmdPal package");
+ er = ERROR_INSTALL_FAILURE;
+ }
+ }
+ }
+ catch (std::exception &e)
+ {
+ std::string errorMessage{"Exception thrown while trying to install CmdPal package: "};
+ errorMessage += e.what();
+ Logger::error(errorMessage);
+
+ er = ERROR_INSTALL_FAILURE;
+ }
+
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UnRegisterCmdPalPackageCA(MSIHANDLE hInstall)
+{
+ using namespace winrt::Windows::Foundation;
+ using namespace winrt::Windows::Management::Deployment;
+
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "UnRegisterCmdPalPackageCA");
+
+ try
+ {
+ // Packages to unregister
+ std::wstring packageToRemoveDisplayName {L"Microsoft.CommandPalette"};
+
+ if (!package::UnRegisterPackage(packageToRemoveDisplayName))
+ {
+ Logger::error(L"Failed to unregister package: " + packageToRemoveDisplayName);
+ er = ERROR_INSTALL_FAILURE;
+ }
+ }
+ catch (std::exception &e)
+ {
+ std::string errorMessage{"Exception thrown while trying to unregister the CmdPal package: "};
+ errorMessage += e.what();
+ Logger::error(errorMessage);
+
+ er = ERROR_INSTALL_FAILURE;
+ }
+
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
+
+UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
+{
+ using namespace winrt::Windows::Foundation;
+ using namespace winrt::Windows::Management::Deployment;
+
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+
+ hr = WcaInitialize(hInstall, "UnRegisterContextMenuPackagesCA"); // original func name is too long
+
+ try
+ {
+ // Packages to unregister
+ const std::vector packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}};
+
+ for (auto const &package : packagesToRemoveDisplayName)
+ {
+ if (!package::UnRegisterPackage(package))
+ {
+ Logger::error(L"Failed to unregister package: " + package);
+ er = ERROR_INSTALL_FAILURE;
+ }
+ }
+ }
+ catch (std::exception &e)
+ {
+ std::string errorMessage{"Exception thrown while trying to unregister sparse packages: "};
+ errorMessage += e.what();
+ Logger::error(errorMessage);
+
+ er = ERROR_INSTALL_FAILURE;
+ }
+
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
+
+UINT __stdcall CleanImageResizerRuntimeRegistryCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "CleanImageResizerRuntimeRegistryCA");
+
+ try
+ {
+ const wchar_t* CLSID_STR = L"{51B4D7E5-7568-4234-B4BB-47FB3C016A69}";
+ const wchar_t* exts[] = { L".bmp", L".dib", L".gif", L".jfif", L".jpe", L".jpeg", L".jpg", L".jxr", L".png", L".rle", L".tif", L".tiff", L".wdp" };
+
+ auto deleteKeyRecursive = [](HKEY root, const std::wstring &path) {
+ RegDeleteTreeW(root, path.c_str());
+ };
+
+ // InprocServer32 chain root CLSID
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
+ // DragDrop handler
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\ShellEx\\DragDropHandlers\\ImageResizer");
+ // Extensions
+ for (auto ext : exts)
+ {
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\SystemFileAssociations\\" + std::wstring(ext) + L"\\ShellEx\\ContextMenuHandlers\\ImageResizer");
+ }
+ // Sentinel
+ RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\ImageResizer");
+ }
+ catch (...)
+ {
+ er = ERROR_INSTALL_FAILURE;
+ }
+
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall CleanFileLocksmithRuntimeRegistryCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "CleanFileLocksmithRuntimeRegistryCA");
+ try
+ {
+ const wchar_t* CLSID_STR = L"{84D68575-E186-46AD-B0CB-BAEB45EE29C0}";
+ auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
+ RegDeleteTreeW(root, path.c_str());
+ };
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt");
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Drive\\ShellEx\\ContextMenuHandlers\\FileLocksmithExt");
+ RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\FileLocksmith");
+ }
+ catch (...)
+ {
+ er = ERROR_INSTALL_FAILURE;
+ }
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall CleanPowerRenameRuntimeRegistryCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "CleanPowerRenameRuntimeRegistryCA");
+ try
+ {
+ const wchar_t* CLSID_STR = L"{0440049F-D1DC-4E46-B27B-98393D79486B}";
+ auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
+ RegDeleteTreeW(root, path.c_str());
+ };
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\AllFileSystemObjects\\ShellEx\\ContextMenuHandlers\\PowerRenameExt");
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\background\\ShellEx\\ContextMenuHandlers\\PowerRenameExt");
+ RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\PowerRename");
+ }
+ catch (...)
+ {
+ er = ERROR_INSTALL_FAILURE;
+ }
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall CleanNewPlusRuntimeRegistryCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "CleanNewPlusRuntimeRegistryCA");
+ try
+ {
+ const wchar_t* CLSID_STR = L"{FF90D477-E32A-4BE8-8CC5-A502A97F5401}";
+ auto deleteKeyRecursive = [](HKEY root, const std::wstring& path) {
+ RegDeleteTreeW(root, path.c_str());
+ };
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" + std::wstring(CLSID_STR));
+ deleteKeyRecursive(HKEY_CURRENT_USER, L"Software\\Classes\\Directory\\background\\ShellEx\\ContextMenuHandlers\\NewPlusShellExtensionWin10");
+ RegDeleteTreeW(HKEY_CURRENT_USER, L"Software\\Microsoft\\PowerToys\\NewPlus");
+ }
+ catch (...)
+ {
+ er = ERROR_INSTALL_FAILURE;
+ }
+ er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ hr = WcaInitialize(hInstall, "TerminateProcessesCA");
+
+ std::vector processes;
+ const size_t maxProcesses = 4096;
+ DWORD bytes = maxProcesses * sizeof(processes[0]);
+ processes.resize(maxProcesses);
+
+ if (!EnumProcesses(processes.data(), bytes, &bytes))
+ {
+ return 1;
+ }
+ processes.resize(bytes / sizeof(processes[0]));
+
+ std::array processesToTerminate = {
+ L"PowerToys.PowerLauncher.exe",
+ L"PowerToys.Settings.exe",
+ L"PowerToys.AdvancedPaste.exe",
+ L"PowerToys.Awake.exe",
+ L"PowerToys.FancyZones.exe",
+ L"PowerToys.FancyZonesEditor.exe",
+ L"PowerToys.FileLocksmithUI.exe",
+ L"PowerToys.MouseJumpUI.exe",
+ L"PowerToys.ColorPickerUI.exe",
+ L"PowerToys.AlwaysOnTop.exe",
+ L"PowerToys.RegistryPreview.exe",
+ L"PowerToys.Hosts.exe",
+ L"PowerToys.PowerRename.exe",
+ L"PowerToys.ImageResizer.exe",
+ L"PowerToys.GcodeThumbnailProvider.exe",
+ L"PowerToys.BgcodeThumbnailProvider.exe",
+ L"PowerToys.PdfThumbnailProvider.exe",
+ L"PowerToys.MonacoPreviewHandler.exe",
+ L"PowerToys.MarkdownPreviewHandler.exe",
+ L"PowerToys.StlThumbnailProvider.exe",
+ L"PowerToys.SvgThumbnailProvider.exe",
+ L"PowerToys.GcodePreviewHandler.exe",
+ L"PowerToys.BgcodePreviewHandler.exe",
+ L"PowerToys.QoiPreviewHandler.exe",
+ L"PowerToys.PdfPreviewHandler.exe",
+ L"PowerToys.QoiThumbnailProvider.exe",
+ L"PowerToys.SvgPreviewHandler.exe",
+ L"PowerToys.Peek.UI.exe",
+ L"PowerToys.MouseWithoutBorders.exe",
+ L"PowerToys.MouseWithoutBordersHelper.exe",
+ L"PowerToys.MouseWithoutBordersService.exe",
+ L"PowerToys.CropAndLock.exe",
+ L"PowerToys.EnvironmentVariables.exe",
+ L"PowerToys.WorkspacesSnapshotTool.exe",
+ L"PowerToys.WorkspacesLauncher.exe",
+ L"PowerToys.WorkspacesLauncherUI.exe",
+ L"PowerToys.WorkspacesEditor.exe",
+ L"PowerToys.WorkspacesWindowArranger.exe",
+ L"Microsoft.CmdPal.UI.exe",
+ L"PowerToys.ZoomIt.exe",
+ L"PowerToys.exe",
+ };
+
+ for (const auto procID : processes)
+ {
+ if (!procID)
+ {
+ continue;
+ }
+ wchar_t processName[MAX_PATH] = L"";
+
+ HANDLE hProcess{OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, FALSE, procID)};
+ if (!hProcess)
+ {
+ continue;
+ }
+ HMODULE hMod;
+ DWORD cbNeeded;
+
+ if (!EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
+ {
+ CloseHandle(hProcess);
+ continue;
+ }
+ GetModuleBaseNameW(hProcess, hMod, processName, sizeof(processName) / sizeof(wchar_t));
+
+ for (const auto processToTerminate : processesToTerminate)
+ {
+ if (processName == processToTerminate)
+ {
+ const DWORD timeout = 500;
+ auto windowEnumerator = [](HWND hwnd, LPARAM procIDPtr) -> BOOL
+ {
+ auto targetProcID = *reinterpret_cast(procIDPtr);
+ DWORD windowProcID = 0;
+ GetWindowThreadProcessId(hwnd, &windowProcID);
+ if (windowProcID == targetProcID)
+ {
+ DWORD_PTR _{};
+ SendMessageTimeoutA(hwnd, WM_CLOSE, 0, 0, SMTO_BLOCK, timeout, &_);
+ }
+ return TRUE;
+ };
+ EnumWindows(windowEnumerator, reinterpret_cast(&procID));
+ Sleep(timeout);
+ TerminateProcess(hProcess, 0);
+ break;
+ }
+ }
+ CloseHandle(hProcess);
+ }
+
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+void initSystemLogger()
+{
+ static std::once_flag initLoggerFlag;
+ std::call_once(initLoggerFlag, []()
+ {
+ WCHAR temp_path[MAX_PATH];
+ auto ret = GetTempPath(MAX_PATH, temp_path);
+
+ if (ret)
+ {
+ Logger::init("PowerToysMSI", std::wstring{ temp_path } + L"\\PowerToysMSIInstaller", L"");
+ } });
+}
+
+// DllMain - Initialize and cleanup WiX custom action utils.
+extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
+{
+ switch (ulReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ WcaGlobalInitialize(hInst);
+ initSystemLogger();
+ TraceLoggingRegister(g_hProvider);
+ DLL_HANDLE = hInst;
+ break;
+
+ case DLL_PROCESS_DETACH:
+ TraceLoggingUnregister(g_hProvider);
+ WcaGlobalFinalize();
+ break;
+ }
+
+ return TRUE;
+}
diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.def b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def
new file mode 100644
index 0000000000..39efc9ff70
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def
@@ -0,0 +1,35 @@
+LIBRARY "PowerToysSetupCustomActionsVNext"
+
+EXPORTS
+ LaunchPowerToysCA
+ CheckGPOCA
+ CleanVideoConferenceRegistryCA
+ ApplyModulesRegistryChangeSetsCA
+ DetectPrevInstallPathCA
+ RemoveScheduledTasksCA
+ TelemetryLogInstallSuccessCA
+ TelemetryLogInstallCancelCA
+ TelemetryLogInstallFailCA
+ TelemetryLogUninstallSuccessCA
+ TelemetryLogUninstallCancelCA
+ TelemetryLogUninstallFailCA
+ TelemetryLogRepairCancelCA
+ TelemetryLogRepairFailCA
+ TerminateProcessesCA
+ InstallEmbeddedMSIXCA
+ InstallDSCModuleCA
+ InstallCmdPalPackageCA
+ UnApplyModulesRegistryChangeSetsCA
+ UnRegisterCmdPalPackageCA
+ UnRegisterContextMenuPackagesCA
+ UninstallEmbeddedMSIXCA
+ UninstallDSCModuleCA
+ UninstallServicesCA
+ UninstallCommandNotFoundModuleCA
+ UpgradeCommandNotFoundModuleCA
+ UnsetAdvancedPasteAPIKeyCA
+ CleanImageResizerRuntimeRegistryCA
+ CleanFileLocksmithRuntimeRegistryCA
+ CleanPowerRenameRuntimeRegistryCA
+ CleanNewPlusRuntimeRegistryCA
+
\ No newline at end of file
diff --git a/installer/PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj b/installer/PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj
new file mode 100644
index 0000000000..db6f6e6392
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj
@@ -0,0 +1,183 @@
+ο»Ώ
+
+
+
+
+
+
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}
+ Win32Proj
+ PowerToysSetupCustomActionsVNext
+ PowerToysSetupCustomActionsVNext
+
+
+
+ DynamicLibrary
+ Unicode
+ v143
+
+
+ DynamicLibrary
+ Unicode
+ true
+ v143
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(Platform)\$(Configuration)\MachineSetup\
+ $(Platform)\$(Configuration)\UserSetup\
+ $(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\MachineSetup\obj\
+ $(SolutionDir)$(ProjectName)\$(Platform)\$(Configuration)\UserSetup\obj\
+
+ false
+ true
+
+
+ true
+
+
+ false
+ ..\..\src\common\Telemetry;$(IncludePath)
+
+
+
+
+ call cmd /C "copy ""$(ProjectDir)DepsFilesLists.h"" ""$(ProjectDir)DepsFilesLists.h.bk"""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\AdvancedPaste.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\AdvancedPaste.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Awake.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Awake.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\BaseApplications.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\BaseApplications.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\CmdPal.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\CmdPal.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\ColorPicker.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\ColorPicker.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Core.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Core.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\EnvironmentVariables.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\EnvironmentVariables.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\FileExplorerPreview.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\FileExplorerPreview.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\FileLocksmith.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\FileLocksmith.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Hosts.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Hosts.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\ImageResizer.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\ImageResizer.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\KeyboardManager.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\KeyboardManager.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\MouseWithoutBorders.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\MouseWithoutBorders.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\NewPlus.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\NewPlus.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Peek.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Peek.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\PowerRename.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\PowerRename.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Product.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Product.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\RegistryPreview.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\RegistryPreview.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Resources.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Resources.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Run.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Run.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Settings.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Settings.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\ShortcutGuide.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\ShortcutGuide.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Tools.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Tools.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\WinAppSDK.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\WinAppSDK.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\WinUI3Applications.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\WinUI3Applications.wxs.bk""""
+ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetupVNext\Workspaces.wxs"" ""$(ProjectDir)..\PowerToysSetupVNext\Workspaces.wxs.bk""""
+ if not "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform)
+ if "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetupVNext\generateAllFileComponents.ps1 -platform $(Platform) -installscopeperuser $(NormalizedPerUserValue)
+
+ Backing up original files and populating .NET and WPF Runtime dependencies for WiX3 based installer
+
+
+
+ false
+ false
+
+
+
+ inc;..\..\src\;..\..\src\common\Telemetry;telemetry;%(AdditionalIncludeDirectories)
+ /await /Zc:twoPhase- /Wv:18 %(AdditionalOptions)
+ Level4
+ ProgramDatabase
+
+
+ Userenv.lib;Wtsapi32.lib;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)
+ CustomAction.def
+
+
+
+
+ WIN64;%(PreprocessorDefinitions)
+
+
+ $(NUGET_PACKAGES)\wixtoolset.wcautil\5.0.2\build\native\v14\$(Platform);$(NUGET_PACKAGES)\wixtoolset.dutil\5.0.2\build\native\v14\$(Platform);%(AdditionalLibraryDirectories)
+
+
+
+
+ Disabled
+ _DEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+
+
+ true
+ Windows
+ HighestAvailable
+
+
+
+
+ MaxSpeed
+ true
+ NDEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions)
+ MultiThreaded
+ true
+
+
+ true
+ Windows
+ true
+ true
+ HighestAvailable
+
+
+
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}
+
+
+ {8f021b46-362b-485c-bfba-ccf83e820cbd}
+
+
+
+
+
+
+
+
+ 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}.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/installer/PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj.filters b/installer/PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj.filters
new file mode 100644
index 0000000000..f4cf974dc0
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/PowerToysSetupCustomActionsVNext.vcxproj.filters
@@ -0,0 +1,31 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+ Telemetry
+
+
+ Telemetry
+
+
+
+
+
+
+
+
+
+
+
+ {6e73ce5d-e715-4e7e-b796-c5d180b07ff2}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/installer/PowerToysSetupCustomActionsVNext/RcResource.h b/installer/PowerToysSetupCustomActionsVNext/RcResource.h
new file mode 100644
index 0000000000..aabbb532bc
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/RcResource.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+
+#include
+
+class RcResource
+{
+public:
+ const std::byte* _memory = nullptr;
+ size_t _size = 0;
+
+ static inline std::optional create(int resource_id, const std::wstring_view resource_class, const HINSTANCE handle = nullptr)
+ {
+ const HRSRC resHandle = FindResourceW(handle, MAKEINTRESOURCEW(resource_id), resource_class.data());
+ if (!resHandle)
+ {
+ return std::nullopt;
+ }
+
+ const HGLOBAL memHandle = LoadResource(handle, resHandle);
+ if (!memHandle)
+ {
+ return std::nullopt;
+ }
+
+ const size_t resSize = SizeofResource(handle, resHandle);
+ if (!resSize)
+ {
+ return std::nullopt;
+ }
+
+ auto res = static_cast(LockResource(memHandle));
+ if (!res)
+ {
+ return std::nullopt;
+ }
+
+ return RcResource{ res, resSize };
+ }
+
+ inline bool saveAsFile(const std::filesystem::path destination)
+ {
+ std::fstream installerFile{ destination, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc };
+ if (!installerFile.is_open())
+ {
+ return false;
+ }
+
+ installerFile.write(reinterpret_cast(_memory), _size);
+ return true;
+ }
+
+private:
+ RcResource() = delete;
+ RcResource(const std::byte* memory, size_t size) :
+ _memory{ memory }, _size{ size }
+ {
+ }
+};
diff --git a/installer/PowerToysSetupCustomActionsVNext/Resource.rc b/installer/PowerToysSetupCustomActionsVNext/Resource.rc
new file mode 100644
index 0000000000..c5f90c330d
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/Resource.rc
@@ -0,0 +1,96 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include
+#include "resource.h"
+#include "../../src/common/version/version.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+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
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
+LANGUAGE 25, 1
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+// TODO: Use to activate embedded MSIX
+//IDR_BIN_MSIX_HELLO_PACKAGE BIN "..\\..\..\\src\\modules\\HelloModule\\AppPackages\\HelloModule_1.0.0.0_x64_Test\\HelloModule_1.0.0.0_x64.msix"
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/installer/PowerToysSetupCustomActionsVNext/packages.config b/installer/PowerToysSetupCustomActionsVNext/packages.config
new file mode 100644
index 0000000000..926fbe5dbf
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/packages.config
@@ -0,0 +1,6 @@
+ο»Ώ
+
+
+
+
+
\ No newline at end of file
diff --git a/installer/PowerToysSetupCustomActionsVNext/pch.cpp b/installer/PowerToysSetupCustomActionsVNext/pch.cpp
new file mode 100644
index 0000000000..64b7eef6d6
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/pch.cpp
@@ -0,0 +1,5 @@
+// pch.cpp: source file corresponding to the pre-compiled header
+
+#include "pch.h"
+
+// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
diff --git a/installer/PowerToysSetupCustomActionsVNext/pch.h b/installer/PowerToysSetupCustomActionsVNext/pch.h
new file mode 100644
index 0000000000..ebfdd0c258
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/pch.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+#define DPSAPI_VERSION 1
+// Windows Header Files:
+#include
+#include
+#include
+#include
+#include
+#include
+
+// WiX Header Files:
+#include
+
+#define SECURITY_WIN32
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
diff --git a/installer/PowerToysSetupCustomActionsVNext/resource.h b/installer/PowerToysSetupCustomActionsVNext/resource.h
new file mode 100644
index 0000000000..0a9e468096
--- /dev/null
+++ b/installer/PowerToysSetupCustomActionsVNext/resource.h
@@ -0,0 +1,20 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Resource.rc
+
+#define FILE_DESCRIPTION "PowerToys Setup Custom Actions"
+#define INTERNAL_NAME "PowerToysSetupCustomActionsVNext"
+#define ORIGINAL_FILENAME "PowerToysSetupCustomActionsVNext.dll"
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 102
+#endif
+#endif
+
+#define IDR_BIN_MSIX_HELLO_PACKAGE 101
diff --git a/installer/PowerToysSetupVNext/AdvancedPaste.wxs b/installer/PowerToysSetupVNext/AdvancedPaste.wxs
new file mode 100644
index 0000000000..f7a01b29ab
--- /dev/null
+++ b/installer/PowerToysSetupVNext/AdvancedPaste.wxs
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Awake.wxs b/installer/PowerToysSetupVNext/Awake.wxs
new file mode 100644
index 0000000000..6dbd86ed43
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Awake.wxs
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/BaseApplications.wxs b/installer/PowerToysSetupVNext/BaseApplications.wxs
new file mode 100644
index 0000000000..1947cbf1f2
--- /dev/null
+++ b/installer/PowerToysSetupVNext/BaseApplications.wxs
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/CmdPal.wxs b/installer/PowerToysSetupVNext/CmdPal.wxs
new file mode 100644
index 0000000000..f05a1f2f35
--- /dev/null
+++ b/installer/PowerToysSetupVNext/CmdPal.wxs
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/ColorPicker.wxs b/installer/PowerToysSetupVNext/ColorPicker.wxs
new file mode 100644
index 0000000000..97deb6c499
--- /dev/null
+++ b/installer/PowerToysSetupVNext/ColorPicker.wxs
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Common.wxi b/installer/PowerToysSetupVNext/Common.wxi
new file mode 100644
index 0000000000..4bb4f5a1dc
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Common.wxi
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Core.wxs b/installer/PowerToysSetupVNext/Core.wxs
new file mode 100644
index 0000000000..d3f992d82e
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Core.wxs
@@ -0,0 +1,122 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/CustomDialogs/PTInstallDirDlg.wxs b/installer/PowerToysSetupVNext/CustomDialogs/PTInstallDirDlg.wxs
new file mode 100644
index 0000000000..0f1af3a9e4
--- /dev/null
+++ b/installer/PowerToysSetupVNext/CustomDialogs/PTInstallDirDlg.wxs
@@ -0,0 +1,28 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/CustomDialogs/PTLicenseDlg.wxs b/installer/PowerToysSetupVNext/CustomDialogs/PTLicenseDlg.wxs
new file mode 100644
index 0000000000..969de90bf7
--- /dev/null
+++ b/installer/PowerToysSetupVNext/CustomDialogs/PTLicenseDlg.wxs
@@ -0,0 +1,28 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/installer/PowerToysSetupVNext/CustomDialogs/WixUI_PTInstallDir.wxs b/installer/PowerToysSetupVNext/CustomDialogs/WixUI_PTInstallDir.wxs
new file mode 100644
index 0000000000..d7666077c0
--- /dev/null
+++ b/installer/PowerToysSetupVNext/CustomDialogs/WixUI_PTInstallDir.wxs
@@ -0,0 +1,77 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Directory.Build.props b/installer/PowerToysSetupVNext/Directory.Build.props
new file mode 100644
index 0000000000..505e3cf844
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Directory.Build.props
@@ -0,0 +1,11 @@
+
+
+
+
+ obj\Installer\
+ obj\Bootstrapper\
+
+
+ $(BaseIntermediateOutputPath)
+
+
diff --git a/installer/PowerToysSetupVNext/EnvironmentVariables.wxs b/installer/PowerToysSetupVNext/EnvironmentVariables.wxs
new file mode 100644
index 0000000000..41762e73d0
--- /dev/null
+++ b/installer/PowerToysSetupVNext/EnvironmentVariables.wxs
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/FileExplorerPreview.wxs b/installer/PowerToysSetupVNext/FileExplorerPreview.wxs
new file mode 100644
index 0000000000..d2e3f3fd71
--- /dev/null
+++ b/installer/PowerToysSetupVNext/FileExplorerPreview.wxs
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/FileLocksmith.wxs b/installer/PowerToysSetupVNext/FileLocksmith.wxs
new file mode 100644
index 0000000000..9ed8d5e29a
--- /dev/null
+++ b/installer/PowerToysSetupVNext/FileLocksmith.wxs
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Hosts.wxs b/installer/PowerToysSetupVNext/Hosts.wxs
new file mode 100644
index 0000000000..3fd55ddfd1
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Hosts.wxs
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/ImageResizer.wxs b/installer/PowerToysSetupVNext/ImageResizer.wxs
new file mode 100644
index 0000000000..86566cc597
--- /dev/null
+++ b/installer/PowerToysSetupVNext/ImageResizer.wxs
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Images/banner.png b/installer/PowerToysSetupVNext/Images/banner.png
new file mode 100644
index 0000000000..25f878ee82
Binary files /dev/null and b/installer/PowerToysSetupVNext/Images/banner.png differ
diff --git a/installer/PowerToysSetupVNext/Images/dialog.png b/installer/PowerToysSetupVNext/Images/dialog.png
new file mode 100644
index 0000000000..b422990982
Binary files /dev/null and b/installer/PowerToysSetupVNext/Images/dialog.png differ
diff --git a/installer/PowerToysSetupVNext/Images/logo.png b/installer/PowerToysSetupVNext/Images/logo.png
new file mode 100644
index 0000000000..c9f9405405
Binary files /dev/null and b/installer/PowerToysSetupVNext/Images/logo.png differ
diff --git a/installer/PowerToysSetupVNext/Images/logo150.png b/installer/PowerToysSetupVNext/Images/logo150.png
new file mode 100644
index 0000000000..35e757d8e5
Binary files /dev/null and b/installer/PowerToysSetupVNext/Images/logo150.png differ
diff --git a/installer/PowerToysSetupVNext/Images/logo44.png b/installer/PowerToysSetupVNext/Images/logo44.png
new file mode 100644
index 0000000000..b931931dff
Binary files /dev/null and b/installer/PowerToysSetupVNext/Images/logo44.png differ
diff --git a/installer/PowerToysSetupVNext/KeyboardManager.wxs b/installer/PowerToysSetupVNext/KeyboardManager.wxs
new file mode 100644
index 0000000000..9aa9fc9472
--- /dev/null
+++ b/installer/PowerToysSetupVNext/KeyboardManager.wxs
@@ -0,0 +1,55 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/MouseWithoutBorders.wxs b/installer/PowerToysSetupVNext/MouseWithoutBorders.wxs
new file mode 100644
index 0000000000..9a0453f296
--- /dev/null
+++ b/installer/PowerToysSetupVNext/MouseWithoutBorders.wxs
@@ -0,0 +1,17 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/NewPlus.wxs b/installer/PowerToysSetupVNext/NewPlus.wxs
new file mode 100644
index 0000000000..7061c01126
--- /dev/null
+++ b/installer/PowerToysSetupVNext/NewPlus.wxs
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Peek.wxs b/installer/PowerToysSetupVNext/Peek.wxs
new file mode 100644
index 0000000000..f7f99326ef
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Peek.wxs
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/PowerRename.wxs b/installer/PowerToysSetupVNext/PowerRename.wxs
new file mode 100644
index 0000000000..331dd4521f
--- /dev/null
+++ b/installer/PowerToysSetupVNext/PowerRename.wxs
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/PowerToys.wxs b/installer/PowerToysSetupVNext/PowerToys.wxs
new file mode 100644
index 0000000000..19906089bf
--- /dev/null
+++ b/installer/PowerToysSetupVNext/PowerToys.wxs
@@ -0,0 +1,64 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/PowerToysBootstrapperVNext.wixproj b/installer/PowerToysSetupVNext/PowerToysBootstrapperVNext.wixproj
new file mode 100644
index 0000000000..1a3a4a8cac
--- /dev/null
+++ b/installer/PowerToysSetupVNext/PowerToysBootstrapperVNext.wixproj
@@ -0,0 +1,62 @@
+
+
+ false
+
+
+ Version=$(Version);InstallerSuffix=$(InstallerSuffix)
+ PowerToysVNextBootstrapper
+
+
+ $(DefineConstants);PerUser=true
+
+
+ $(DefineConstants);PerUser=false
+
+
+ $(DefineConstants);CIBuild=true
+
+
+ $(DefineConstants);CIBuild=false
+
+
+ Release
+ x64
+ arm64
+ wix5
+ PowerToysSetup-$(Version)-$(InstallerSuffix)-$(Platform)
+ Bundle
+ True
+ PowerToysSetup-$(Version)-$(InstallerSuffix)-$(Platform)
+ PowerToysUserSetup-$(Version)-$(InstallerSuffix)-$(Platform)
+ $(Platform)\$(Configuration)\MachineSetup
+ $(Platform)\$(Configuration)\UserSetup
+ $(BaseIntermediateOutputPath)$(Platform)\$(Configuration)\MachineSetup
+ $(BaseIntermediateOutputPath)$(Platform)\$(Configuration)\UserSetup
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}
+ SilentFilesInUseBAFunction
+ false
+
+
+
+
+
+ <_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
+
+
+
+
\ No newline at end of file
diff --git a/installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj b/installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj
new file mode 100644
index 0000000000..0cb9118b91
--- /dev/null
+++ b/installer/PowerToysSetupVNext/PowerToysInstallerVNext.wixproj
@@ -0,0 +1,167 @@
+
+
+
+ false
+
+
+ Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\x64\$(Configuration)\Assets\Monaco\monacoSRC;CmdPalVersion=$(CmdPalVersion);InstallerSuffix=$(InstallerSuffix)
+ IF NOT DEFINED IsPipeline (
+call "$([MSBuild]::GetVsInstallRoot())\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.19041.0 -vcvars_ver=$(VCToolsVersion)
+SET PTRoot=$(SolutionDir)\..
+call "..\..\..\publish.cmd" x64
+)
+call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuildThisFileDirectory)\generateMonacoWxs.ps1 -monacoWxsFile "$(MSBuildThisFileDirectory)\MonacoSRC.wxs" -Platform "$(Platform)" -nugetHeatPath "$(NUGET_PACKAGES)\wixtoolset.heat\5.0.2"
+
+
+
+ Version=$(Version);MonacoSRCHarvestPath=$(ProjectDir)..\..\ARM64\$(Configuration)\Assets\Monaco\monacoSRC;CmdPalVersion=$(CmdPalVersion);InstallerSuffix=$(InstallerSuffix)
+ IF NOT DEFINED IsPipeline (
+call "$([MSBuild]::GetVsInstallRoot())\Common7\Tools\VsDevCmd.bat" -arch=arm64 -host_arch=amd64 -winsdk=10.0.19041.0 -vcvars_ver=$(VCToolsVersion)
+SET PTRoot=$(SolutionDir)\..
+call "..\..\..\publish.cmd" arm64
+)
+call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuildThisFileDirectory)\generateMonacoWxs.ps1 -monacoWxsFile "$(MSBuildThisFileDirectory)\MonacoSRC.wxs" -Platform "$(Platform)" -nugetHeatPath "$(NUGET_PACKAGES)\wixtoolset.heat\5.0.2"
+
+
+
+ Always
+
+ call move /Y ..\..\..\AdvancedPaste.wxs.bk ..\..\..\AdvancedPaste.wxs
+ call move /Y ..\..\..\Awake.wxs.bk ..\..\..\Awake.wxs
+ call move /Y ..\..\..\BaseApplications.wxs.bk ..\..\..\BaseApplications.wxs
+ call move /Y ..\..\..\CmdPal.wxs.bk ..\..\..\CmdPal.wxs
+ call move /Y ..\..\..\ColorPicker.wxs.bk ..\..\..\ColorPicker.wxs
+ call move /Y ..\..\..\Core.wxs.bk ..\..\..\Core.wxs
+ call move /Y ..\..\..\EnvironmentVariables.wxs.bk ..\..\..\EnvironmentVariables.wxs
+ call move /Y ..\..\..\FileExplorerPreview.wxs.bk ..\..\..\FileExplorerPreview.wxs
+ call move /Y ..\..\..\FileLocksmith.wxs.bk ..\..\..\FileLocksmith.wxs
+ call move /Y ..\..\..\Hosts.wxs.bk ..\..\..\Hosts.wxs
+ call move /Y ..\..\..\ImageResizer.wxs.bk ..\..\..\ImageResizer.wxs
+ call move /Y ..\..\..\KeyboardManager.wxs.bk ..\..\..\KeyboardManager.wxs
+ call move /Y ..\..\..\MouseWithoutBorders.wxs.bk ..\..\..\MouseWithoutBorders.wxs
+ call move /Y ..\..\..\NewPlus.wxs.bk ..\..\..\NewPlus.wxs
+ call move /Y ..\..\..\Peek.wxs.bk ..\..\..\Peek.wxs
+ call move /Y ..\..\..\PowerRename.wxs.bk ..\..\..\PowerRename.wxs
+ call move /Y ..\..\..\Product.wxs.bk ..\..\..\Product.wxs
+ call move /Y ..\..\..\RegistryPreview.wxs.bk ..\..\..\RegistryPreview.wxs
+ call move /Y ..\..\..\Resources.wxs.bk ..\..\..\Resources.wxs
+ call move /Y ..\..\..\Run.wxs.bk ..\..\..\Run.wxs
+ call move /Y ..\..\..\Settings.wxs.bk ..\..\..\Settings.wxs
+ call move /Y ..\..\..\ShortcutGuide.wxs.bk ..\..\..\ShortcutGuide.wxs
+ call move /Y ..\..\..\Tools.wxs.bk ..\..\..\Tools.wxs
+ call move /Y ..\..\..\WinAppSDK.wxs.bk ..\..\..\WinAppSDK.wxs
+ call move /Y ..\..\..\WinUI3Applications.wxs.bk ..\..\..\WinUI3Applications.wxs
+ call move /Y ..\..\..\Workspaces.wxs.bk ..\..\..\Workspaces.wxs
+
+
+
+ $(DefineConstants);PerUser=true
+
+
+ $(DefineConstants);PerUser=false
+
+
+ $(DefineConstants);CIBuild=true
+
+
+ $(DefineConstants);CIBuild=false
+
+
+ PowerToysVNextInstaller
+
+ Release
+ $(Platform)
+ 3.10
+ {b6e94700-df38-41f6-a3fd-18b69674ab1e}
+ 2.0
+ wix5
+ PowerToysSetup-$(Version)-$(InstallerSuffix)-$(Platform)
+ PowerToysUserSetup-$(Version)-$(InstallerSuffix)-$(Platform)
+ Package
+ True
+
+
+
+
+ ICE91
+ 1026;1076
+
+
+ $(Platform)\$(Configuration)\MachineSetup
+ $(Platform)\$(Configuration)\UserSetup
+ $(BaseIntermediateOutputPath)$(Platform)\$(Configuration)\MachineSetup
+ $(BaseIntermediateOutputPath)$(Platform)\$(Configuration)\UserSetup
+ ICE40
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PowerToysSetupCustomActionsVNext
+ {B3A354B0-1E54-4B55-A962-FB5AF9330C19}
+ True
+ True
+ Binaries;Content;Satellites
+ INSTALLFOLDER
+
+
+
+
+
+
+
+ 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}.
+
+
+
+
+
+ <_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
+
+
+
+
\ No newline at end of file
diff --git a/installer/PowerToysSetupVNext/Product.wxs b/installer/PowerToysSetupVNext/Product.wxs
new file mode 100644
index 0000000000..e343897d5d
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Product.wxs
@@ -0,0 +1,279 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/RegistryPreview.wxs b/installer/PowerToysSetupVNext/RegistryPreview.wxs
new file mode 100644
index 0000000000..e55cce9b1a
--- /dev/null
+++ b/installer/PowerToysSetupVNext/RegistryPreview.wxs
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Resources.wxs b/installer/PowerToysSetupVNext/Resources.wxs
new file mode 100644
index 0000000000..7e62a34be9
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Resources.wxs
@@ -0,0 +1,464 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/RtfTheme.xml b/installer/PowerToysSetupVNext/RtfTheme.xml
new file mode 100644
index 0000000000..da875cc4c3
--- /dev/null
+++ b/installer/PowerToysSetupVNext/RtfTheme.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+ Segoe UI
+ Segoe UI
+ Segoe UI
+ Segoe UI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #(loc.InstallAcceptCheckbox)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #(loc.FailureHyperlinkLogText)
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Run.wxs b/installer/PowerToysSetupVNext/Run.wxs
new file mode 100644
index 0000000000..cf7542b70d
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Run.wxs
@@ -0,0 +1,446 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Settings.wxs b/installer/PowerToysSetupVNext/Settings.wxs
new file mode 100644
index 0000000000..f9e5312ea7
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Settings.wxs
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/ShortcutGuide.wxs b/installer/PowerToysSetupVNext/ShortcutGuide.wxs
new file mode 100644
index 0000000000..37b1c7800b
--- /dev/null
+++ b/installer/PowerToysSetupVNext/ShortcutGuide.wxs
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/SilentFilesInUseBA/SilentFilesInUseBAFunction.vcxproj b/installer/PowerToysSetupVNext/SilentFilesInUseBA/SilentFilesInUseBAFunction.vcxproj
new file mode 100644
index 0000000000..dfa43efebe
--- /dev/null
+++ b/installer/PowerToysSetupVNext/SilentFilesInUseBA/SilentFilesInUseBAFunction.vcxproj
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+
+ {F8B9F842-F5C3-4A2D-8C85-7F8B9E2B4F1D}
+ DynamicLibrary
+ Unicode
+ SilentFilesInUseBAFunction
+ bafunctions.def
+ 10.0
+
+
+
+
+
+
+ DynamicLibrary
+ true
+ v143
+ Unicode
+
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+ comctl32.lib;gdiplus.lib;msimg32.lib;shlwapi.lib;wininet.lib;version.lib
+
+
+
+
+
+
+ native,Version=v0.0
+
+ win-x64;win-arm64
+
+
+
+
+
+ Create
+ precomp.h
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ version.lib;%(AdditionalDependencies)
+ bafunctions.def
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/SilentFilesInUseBA/SilentFilesInUseBAFunctions.cpp b/installer/PowerToysSetupVNext/SilentFilesInUseBA/SilentFilesInUseBAFunctions.cpp
new file mode 100644
index 0000000000..9b9e5d570f
--- /dev/null
+++ b/installer/PowerToysSetupVNext/SilentFilesInUseBA/SilentFilesInUseBAFunctions.cpp
@@ -0,0 +1,162 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+#include "precomp.h"
+#include "BalBaseBAFunctions.h"
+#include "BalBaseBAFunctionsProc.h"
+
+class CSilentFilesInUseBAFunctions : public CBalBaseBAFunctions
+{
+public: // IBootstrapperApplication
+ virtual STDMETHODIMP OnDetectBegin(
+ __in BOOL fCached,
+ __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType,
+ __in DWORD cPackages,
+ __inout BOOL* pfCancel
+ )
+ {
+ HRESULT hr = S_OK;
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running detect begin BA function. fCached=%d, registrationType=%d, cPackages=%u, fCancel=%d", fCached, registrationType, cPackages, *pfCancel);
+
+ LExit:
+ return hr;
+ }
+
+public: // IBAFunctions
+ virtual STDMETHODIMP OnPlanBegin(
+ __in DWORD cPackages,
+ __inout BOOL* pfCancel
+ )
+ {
+ HRESULT hr = S_OK;
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running plan begin BA function. cPackages=%u, fCancel=%d", cPackages, *pfCancel);
+
+ //-------------------------------------------------------------------------------------------------
+ // YOUR CODE GOES HERE
+ // BalExitOnFailure(hr, "Change this message to represent real error handling.");
+ //-------------------------------------------------------------------------------------------------
+
+ LExit:
+ return hr;
+ }
+
+ virtual STDMETHODIMP OnExecuteBegin(
+ __in DWORD cExecutingPackages,
+ __inout BOOL* pfCancel
+ )
+ {
+ HRESULT hr = S_OK;
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running execute begin BA function. cExecutingPackages=%u, fCancel=%d", cExecutingPackages, *pfCancel);
+
+ return hr;
+ }
+
+ virtual STDMETHODIMP OnExecuteFilesInUse(
+ __in_z LPCWSTR wzPackageId,
+ __in DWORD cFiles,
+ __in_ecount_z(cFiles) LPCWSTR* rgwzFiles,
+ __in int nRecommendation,
+ __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
+ __inout int* pResult
+ )
+ {
+ HRESULT hr = S_OK;
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION CALLED *** Running OnExecuteFilesInUse BA function. packageId=%ls, cFiles=%u, recommendation=%d", wzPackageId, cFiles, nRecommendation);
+
+ // Log each file that's in use
+ for (DWORD i = 0; i < cFiles; i++)
+ {
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** FILE IN USE [%u]: %ls", i, rgwzFiles[i]);
+ }
+
+ /*
+ * Summary: Why we return IDIGNORE here
+ *
+ * - Goal: Keep behavior consistent with our previous WiX 3 installer to avoid "files in use / close apps" prompts and preserve silent installs (e.g., winget).
+ * - WiX 5 change: We can no longer suppress that dialog the same way. Combined with winget adding /silent, this BAFunction returns IDIGNORE to continue without prompts.
+ * - Main trigger: Win10-style context menu uses registry + DLL; Explorer/dllhost.exe (COM Surrogate) often holds locks. Killing them is disruptive; this is a pragmatic trade-off.
+ * - Trade-off: Some file replacements may defer until reboot (PendingFileRename), but installation remains non-interruptive.
+ * - Full fix: Rewrite a custom Bootstrapper Application if we need complete control over prompts and behavior.
+ * - Note: Even with this handler, a full-UI install (e.g., double-clicking the installer) can still show a FilesInUse dialog; this primarily targets silent installs.
+ */
+ *pResult = IDIGNORE;
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** BA FUNCTION RETURNING IDIGNORE - SILENTLY CONTINUING ***");
+
+ return hr;
+ }
+
+ virtual STDMETHODIMP OnExecuteComplete(
+ __in HRESULT hrStatus,
+ __inout BOOL* pfCancel
+ )
+ {
+ HRESULT hr = S_OK;
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running execute complete BA function. hrStatus=0x%x, fCancel=%d", hrStatus, *pfCancel);
+
+ return hr;
+ }
+
+public:
+ //
+ // Constructor - initialize member variables.
+ //
+ CSilentFilesInUseBAFunctions(
+ __in HMODULE hModule
+ ) : CBalBaseBAFunctions(hModule)
+ {
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** BA FUNCTION CONSTRUCTOR *** CSilentFilesInUseBAFunctions created");
+ }
+
+ //
+ // Destructor - release member variables.
+ //
+ ~CSilentFilesInUseBAFunctions()
+ {
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** BA FUNCTION DESTRUCTOR *** CSilentFilesInUseBAFunctions destroyed");
+ }
+};
+
+
+HRESULT WINAPI CreateBAFunctions(
+ __in HMODULE hModule,
+ __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
+ __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
+ )
+{
+ HRESULT hr = S_OK;
+ CSilentFilesInUseBAFunctions* pBAFunctions = NULL;
+
+ // First thing - log that we're being called
+ BalInitialize(pArgs->pEngine);
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS CALLED *** BA Function DLL is being loaded!");
+
+ pBAFunctions = new CSilentFilesInUseBAFunctions(hModule);
+ ExitOnNull(pBAFunctions, hr, E_OUTOFMEMORY, "Failed to create new CSilentFilesInUseBAFunctions object.");
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS *** Created CSilentFilesInUseBAFunctions object");
+
+ hr = pBAFunctions->OnCreate(pArgs->pEngine, pArgs->pCommand);
+ ExitOnFailure(hr, "Failed to call OnCreate CPrereqBaf.");
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS *** OnCreate completed successfully");
+
+ pResults->pfnBAFunctionsProc = BalBaseBAFunctionsProc;
+ pResults->pvBAFunctionsProcContext = pBAFunctions;
+ pBAFunctions = NULL;
+
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS SUCCESS *** BA Function system initialized");
+
+LExit:
+ if (FAILED(hr))
+ {
+ BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "*** CREATEBAFUNCTIONS FAILED *** hr=0x%x", hr);
+ }
+ ReleaseObject(pBAFunctions);
+
+ return hr;
+}
diff --git a/installer/PowerToysSetupVNext/SilentFilesInUseBA/bafunctions.cpp b/installer/PowerToysSetupVNext/SilentFilesInUseBA/bafunctions.cpp
new file mode 100644
index 0000000000..72dc1f54c0
--- /dev/null
+++ b/installer/PowerToysSetupVNext/SilentFilesInUseBA/bafunctions.cpp
@@ -0,0 +1,51 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+#include "precomp.h"
+
+static HINSTANCE vhInstance = NULL;
+
+extern "C" BOOL WINAPI DllMain(
+ IN HINSTANCE hInstance,
+ IN DWORD dwReason,
+ IN LPVOID /* pvReserved */
+ )
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ ::DisableThreadLibraryCalls(hInstance);
+ vhInstance = hInstance;
+ break;
+
+ case DLL_PROCESS_DETACH:
+ vhInstance = NULL;
+ break;
+ }
+
+ return TRUE;
+}
+
+extern "C" HRESULT WINAPI BAFunctionsCreate(
+ __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
+ __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
+ )
+{
+ HRESULT hr = S_OK;
+
+ // This is required to enable logging functions.
+ BalInitialize(pArgs->pEngine);
+
+ hr = CreateBAFunctions(vhInstance, pArgs, pResults);
+ BalExitOnFailure(hr, "Failed to create BAFunctions interface.");
+
+LExit:
+ return hr;
+}
+
+extern "C" void WINAPI BAFunctionsDestroy(
+ __in const BA_FUNCTIONS_DESTROY_ARGS* /*pArgs*/,
+ __inout BA_FUNCTIONS_DESTROY_RESULTS* /*pResults*/
+ )
+{
+ BalUninitialize();
+}
diff --git a/installer/PowerToysSetupVNext/SilentFilesInUseBA/bafunctions.def b/installer/PowerToysSetupVNext/SilentFilesInUseBA/bafunctions.def
new file mode 100644
index 0000000000..6e016dad07
--- /dev/null
+++ b/installer/PowerToysSetupVNext/SilentFilesInUseBA/bafunctions.def
@@ -0,0 +1,6 @@
+; Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+
+EXPORTS
+ BAFunctionsCreate
+ BAFunctionsDestroy
diff --git a/installer/PowerToysSetupVNext/SilentFilesInUseBA/precomp.h b/installer/PowerToysSetupVNext/SilentFilesInUseBA/precomp.h
new file mode 100644
index 0000000000..6f679095ae
--- /dev/null
+++ b/installer/PowerToysSetupVNext/SilentFilesInUseBA/precomp.h
@@ -0,0 +1,37 @@
+#pragma once
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+
+#include
+
+#pragma warning(push)
+#pragma warning(disable:4458) // declaration of 'xxx' hides class member
+#include
+#pragma warning(pop)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// Standard WiX header files, include as required
+#include "dutil.h"
+#include "dictutil.h"
+#include "fileutil.h"
+#include "pathutil.h"
+#include "strutil.h"
+#include "regutil.h"
+
+#include "BootstrapperApplicationBase.h"
+
+#include "BAFunctions.h"
+#include "IBAFunctions.h"
+
+HRESULT WINAPI CreateBAFunctions(
+ __in HMODULE hModule,
+ __in const BA_FUNCTIONS_CREATE_ARGS* pArgs,
+ __inout BA_FUNCTIONS_CREATE_RESULTS* pResults
+ );
diff --git a/installer/PowerToysSetupVNext/SilentFilesInUseBA/resource.h b/installer/PowerToysSetupVNext/SilentFilesInUseBA/resource.h
new file mode 100644
index 0000000000..149a8ff48a
--- /dev/null
+++ b/installer/PowerToysSetupVNext/SilentFilesInUseBA/resource.h
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
+
+#define IDC_STATIC -1
+
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1003
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/installer/PowerToysSetupVNext/Tools.wxs b/installer/PowerToysSetupVNext/Tools.wxs
new file mode 100644
index 0000000000..9e43e90916
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Tools.wxs
@@ -0,0 +1,33 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/WebView2/MicrosoftEdgeWebview2Setup.exe b/installer/PowerToysSetupVNext/WebView2/MicrosoftEdgeWebview2Setup.exe
new file mode 100644
index 0000000000..d95ba2e893
Binary files /dev/null and b/installer/PowerToysSetupVNext/WebView2/MicrosoftEdgeWebview2Setup.exe differ
diff --git a/installer/PowerToysSetupVNext/WinAppSDK.wxs b/installer/PowerToysSetupVNext/WinAppSDK.wxs
new file mode 100644
index 0000000000..00b8395735
--- /dev/null
+++ b/installer/PowerToysSetupVNext/WinAppSDK.wxs
@@ -0,0 +1,461 @@
+ο»Ώ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/WinUI3Applications.wxs b/installer/PowerToysSetupVNext/WinUI3Applications.wxs
new file mode 100644
index 0000000000..4c177b960a
--- /dev/null
+++ b/installer/PowerToysSetupVNext/WinUI3Applications.wxs
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/Workspaces.wxs b/installer/PowerToysSetupVNext/Workspaces.wxs
new file mode 100644
index 0000000000..86825ac393
--- /dev/null
+++ b/installer/PowerToysSetupVNext/Workspaces.wxs
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 b/installer/PowerToysSetupVNext/generateAllFileComponents.ps1
new file mode 100644
index 0000000000..fb63868f93
--- /dev/null
+++ b/installer/PowerToysSetupVNext/generateAllFileComponents.ps1
@@ -0,0 +1,323 @@
+[CmdletBinding()]
+Param(
+ [Parameter(Mandatory = $True, Position = 1)]
+ [string]$platform,
+ [Parameter(Mandatory = $False, Position = 2)]
+ [string]$installscopeperuser = "false"
+)
+
+Function Generate-FileList() {
+ [CmdletBinding()]
+ Param(
+ # Can be multiple files separated by ; as long as they're on the same directory
+ [Parameter(Mandatory = $True, Position = 1)]
+ [AllowEmptyString()]
+ [string]$fileDepsJson,
+ [Parameter(Mandatory = $True, Position = 2)]
+ [string]$fileListName,
+ [Parameter(Mandatory = $True, Position = 3)]
+ [string]$wxsFilePath,
+ # If there is no deps.json file, just pass path to files
+ [Parameter(Mandatory = $False, Position = 4)]
+ [string]$depsPath,
+ # launcher plugins are being loaded into launcher process,
+ # so there are some additional dependencies to skip
+ [Parameter(Mandatory = $False, Position = 5)]
+ [bool]$isLauncherPlugin
+ )
+
+ $fileWxs = Get-Content $wxsFilePath;
+
+ $fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe")
+
+ $fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri")
+
+ $dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
+
+ if ($fileDepsJson -eq [string]::Empty) {
+ $fileDepsRoot = $depsPath
+ } else {
+ $multipleDepsJson = $fileDepsJson.Split(";")
+
+ foreach ( $singleDepsJson in $multipleDepsJson )
+ {
+
+ $fileDepsRoot = (Get-ChildItem $singleDepsJson).Directory.FullName
+ $depsJson = Get-Content $singleDepsJson | ConvertFrom-Json
+
+ $runtimeList = ([array]$depsJson.targets.PSObject.Properties)[-1].Value.PSObject.Properties | Where-Object {
+ $_.Name -match "runtimepack.*Runtime"
+ };
+
+ $runtimeList | ForEach-Object {
+ $_.Value.PSObject.Properties.Value | ForEach-Object {
+ $fileExclusionList += $_.PSObject.Properties.Name
+ }
+ }
+ }
+ }
+
+ $fileExclusionList = $fileExclusionList | Where-Object {$_ -notin $dllsToIgnore}
+
+ if ($isLauncherPlugin -eq $True) {
+ $fileInclusionList += @("*.deps.json")
+ $fileExclusionList += @("Ijwhost.dll", "PowerToys.Common.UI.dll", "PowerToys.GPOWrapper.dll", "PowerToys.GPOWrapperProjection.dll", "PowerToys.PowerLauncher.Telemetry.dll", "PowerToys.ManagedCommon.dll", "PowerToys.Settings.UI.Lib.dll", "Wox.Infrastructure.dll", "Wox.Plugin.dll")
+ }
+
+ $fileList = Get-ChildItem $fileDepsRoot -Include $fileInclusionList -Exclude $fileExclusionList -File -Name
+
+ $fileWxs = $fileWxs -replace "(<\?define $($fileListName)=)", "") {
+ [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList',
+ Justification = 'variable is used in another scope')]
+
+ $fileList = $matches[2] -split ';'
+ return
+ }
+ }
+
+ $componentId = "$($fileListName)_Component"
+
+ $componentDefs = "`r`n"
+ $componentDefs +=
+ @"
+
+
+
+ `r`n
+"@
+
+ foreach ($file in $fileList) {
+ $fileTmp = $file -replace "-", "_"
+ $componentDefs +=
+ @"
+ `r`n
+"@
+ }
+
+ $componentDefs +=
+ @"
+ `r`n
+"@
+
+ $wxsFile = $wxsFile -replace "\s+()", $componentDefs
+
+ $componentRef =
+ @"
+
+"@
+
+ $wxsFile = $wxsFile -replace "\s+()", "$componentRef`r`n "
+
+ Set-Content -Path $wxsFilePath -Value $wxsFile
+}
+
+if ($platform -ceq "arm64") {
+ $platform = "ARM64"
+}
+
+if ($installscopeperuser -eq "true") {
+ $registryroot = "HKCU"
+} else {
+ $registryroot = "HKLM"
+}
+
+#BaseApplications
+Generate-FileList -fileDepsJson "" -fileListName BaseApplicationsFiles -wxsFilePath $PSScriptRoot\BaseApplications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release"
+Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSScriptRoot\BaseApplications.wxs -regroot $registryroot
+
+#WinUI3Applications
+Generate-FileList -fileDepsJson "" -fileListName WinUI3ApplicationsFiles -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"
+Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -regroot $registryroot
+
+#AdvancedPaste
+Generate-FileList -fileDepsJson "" -fileListName AdvancedPasteAssetsFiles -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\AdvancedPaste"
+Generate-FileComponents -fileListName "AdvancedPasteAssetsFiles" -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -regroot $registryroot
+
+#AwakeFiles
+Generate-FileList -fileDepsJson "" -fileListName AwakeImagesFiles -wxsFilePath $PSScriptRoot\Awake.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Awake"
+Generate-FileComponents -fileListName "AwakeImagesFiles" -wxsFilePath $PSScriptRoot\Awake.wxs -regroot $registryroot
+
+#ColorPicker
+Generate-FileList -fileDepsJson "" -fileListName ColorPickerAssetsFiles -wxsFilePath $PSScriptRoot\ColorPicker.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ColorPicker"
+Generate-FileComponents -fileListName "ColorPickerAssetsFiles" -wxsFilePath $PSScriptRoot\ColorPicker.wxs -regroot $registryroot
+
+#Environment Variables
+Generate-FileList -fileDepsJson "" -fileListName EnvironmentVariablesAssetsFiles -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\EnvironmentVariables"
+Generate-FileComponents -fileListName "EnvironmentVariablesAssetsFiles" -wxsFilePath $PSScriptRoot\EnvironmentVariables.wxs -regroot $registryroot
+
+#FileExplorerAdd-ons
+Generate-FileList -fileDepsJson "" -fileListName MonacoPreviewHandlerMonacoAssetsFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Monaco"
+Generate-FileList -fileDepsJson "" -fileListName MonacoPreviewHandlerCustomLanguagesFiles -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Monaco\customLanguages"
+Generate-FileComponents -fileListName "MonacoPreviewHandlerMonacoAssetsFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "MonacoPreviewHandlerCustomLanguagesFiles" -wxsFilePath $PSScriptRoot\FileExplorerPreview.wxs -regroot $registryroot
+
+#FileLocksmith
+Generate-FileList -fileDepsJson "" -fileListName FileLocksmithAssetsFiles -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\FileLocksmith"
+Generate-FileComponents -fileListName "FileLocksmithAssetsFiles" -wxsFilePath $PSScriptRoot\FileLocksmith.wxs -regroot $registryroot
+
+#Hosts
+Generate-FileList -fileDepsJson "" -fileListName HostsAssetsFiles -wxsFilePath $PSScriptRoot\Hosts.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Hosts"
+Generate-FileComponents -fileListName "HostsAssetsFiles" -wxsFilePath $PSScriptRoot\Hosts.wxs -regroot $registryroot
+
+#ImageResizer
+Generate-FileList -fileDepsJson "" -fileListName ImageResizerAssetsFiles -wxsFilePath $PSScriptRoot\ImageResizer.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\ImageResizer"
+Generate-FileComponents -fileListName "ImageResizerAssetsFiles" -wxsFilePath $PSScriptRoot\ImageResizer.wxs -regroot $registryroot
+
+#New+
+Generate-FileList -fileDepsJson "" -fileListName NewPlusAssetsFiles -wxsFilePath $PSScriptRoot\NewPlus.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\NewPlus"
+Generate-FileComponents -fileListName "NewPlusAssetsFiles" -wxsFilePath $PSScriptRoot\NewPlus.wxs -regroot $registryroot
+
+#Peek
+Generate-FileList -fileDepsJson "" -fileListName PeekAssetsFiles -wxsFilePath $PSScriptRoot\Peek.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Peek\"
+Generate-FileComponents -fileListName "PeekAssetsFiles" -wxsFilePath $PSScriptRoot\Peek.wxs -regroot $registryroot
+
+#PowerRename
+Generate-FileList -fileDepsJson "" -fileListName PowerRenameAssetsFiles -wxsFilePath $PSScriptRoot\PowerRename.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\PowerRename\"
+Generate-FileComponents -fileListName "PowerRenameAssetsFiles" -wxsFilePath $PSScriptRoot\PowerRename.wxs -regroot $registryroot
+
+#RegistryPreview
+Generate-FileList -fileDepsJson "" -fileListName RegistryPreviewAssetsFiles -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\RegistryPreview\"
+Generate-FileComponents -fileListName "RegistryPreviewAssetsFiles" -wxsFilePath $PSScriptRoot\RegistryPreview.wxs -regroot $registryroot
+
+#Run
+Generate-FileList -fileDepsJson "" -fileListName launcherImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\PowerLauncher"
+Generate-FileComponents -fileListName "launcherImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+## Plugins
+###Calculator
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Calculator\Microsoft.PowerToys.Run.Plugin.Calculator.deps.json" -fileListName calcComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName calcImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Calculator\Images"
+Generate-FileComponents -fileListName "calcComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "calcImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###Folder
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Folder\Microsoft.Plugin.Folder.deps.json" -fileListName FolderComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName FolderImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Folder\Images"
+Generate-FileComponents -fileListName "FolderComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "FolderImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###Program
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Program\Microsoft.Plugin.Program.deps.json" -fileListName ProgramComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName ProgramImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Program\Images"
+Generate-FileComponents -fileListName "ProgramComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "ProgramImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###Shell
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Shell\Microsoft.Plugin.Shell.deps.json" -fileListName ShellComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName ShellImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Shell\Images"
+Generate-FileComponents -fileListName "ShellComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "ShellImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###Indexer
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Indexer\Microsoft.Plugin.Indexer.deps.json" -fileListName IndexerComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName IndexerImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Indexer\Images"
+Generate-FileComponents -fileListName "IndexerComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "IndexerImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###UnitConverter
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\UnitConverter\Community.PowerToys.Run.Plugin.UnitConverter.deps.json" -fileListName UnitConvCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName UnitConvImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\UnitConverter\Images"
+Generate-FileComponents -fileListName "UnitConvCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "UnitConvImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###WebSearch
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WebSearch\Community.PowerToys.Run.Plugin.WebSearch.deps.json" -fileListName WebSrchCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName WebSrchImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WebSearch\Images"
+Generate-FileComponents -fileListName "WebSrchCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "WebSrchImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###History
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\History\Microsoft.PowerToys.Run.Plugin.History.deps.json" -fileListName HistoryPluginComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName HistoryPluginImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\History\Images"
+Generate-FileComponents -fileListName "HistoryPluginComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "HistoryPluginImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###Uri
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Uri\Microsoft.Plugin.Uri.deps.json" -fileListName UriComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName UriImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Uri\Images"
+Generate-FileComponents -fileListName "UriComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "UriImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###VSCodeWorkspaces
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.deps.json" -fileListName VSCWrkCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName VSCWrkImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\VSCodeWorkspaces\Images"
+Generate-FileComponents -fileListName "VSCWrkCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "VSCWrkImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###WindowWalker
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowWalker\Microsoft.Plugin.WindowWalker.deps.json" -fileListName WindowWlkrCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName WindowWlkrImagesCompFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowWalker\Images"
+Generate-FileComponents -fileListName "WindowWlkrCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "WindowWlkrImagesCompFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###OneNote
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\OneNote\Microsoft.PowerToys.Run.Plugin.OneNote.deps.json" -fileListName OneNoteComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName OneNoteImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\OneNote\Images"
+Generate-FileComponents -fileListName "OneNoteComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "OneNoteImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###Registry
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Registry\Microsoft.PowerToys.Run.Plugin.Registry.deps.json" -fileListName RegistryComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName RegistryImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Registry\Images"
+Generate-FileComponents -fileListName "RegistryComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "RegistryImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###Service
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Service\Microsoft.PowerToys.Run.Plugin.Service.deps.json" -fileListName ServiceComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName ServiceImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\Service\Images"
+Generate-FileComponents -fileListName "ServiceComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "ServiceImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###System
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\System\Microsoft.PowerToys.Run.Plugin.System.deps.json" -fileListName SystemComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName SystemImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\System\Images"
+Generate-FileComponents -fileListName "SystemComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "SystemImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###TimeDate
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.deps.json" -fileListName TimeDateComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName TimeDateImagesComponentFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\TimeDate\Images"
+Generate-FileComponents -fileListName "TimeDateComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "TimeDateImagesComponentFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###WindowsSettings
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsSettings\Microsoft.PowerToys.Run.Plugin.WindowsSettings.deps.json" -fileListName WinSetCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName WinSetImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsSettings\Images"
+Generate-FileComponents -fileListName "WinSetCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "WinSetImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###WindowsTerminal
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsTerminal\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.deps.json" -fileListName WinTermCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName WinTermImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\WindowsTerminal\Images"
+Generate-FileComponents -fileListName "WinTermCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "WinTermImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###PowerToys
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\PowerToys\Microsoft.PowerToys.Run.Plugin.PowerToys.deps.json" -fileListName PowerToysCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName PowerToysImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\PowerToys\Images"
+Generate-FileComponents -fileListName "PowerToysCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "PowerToysImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+###ValueGenerator
+Generate-FileList -fileDepsJson "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\ValueGenerator\Community.PowerToys.Run.Plugin.ValueGenerator.deps.json" -fileListName ValueGeneratorCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -isLauncherPlugin 1
+Generate-FileList -fileDepsJson "" -fileListName ValueGeneratorImagesCmpFiles -wxsFilePath $PSScriptRoot\Run.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\RunPlugins\ValueGenerator\Images"
+Generate-FileComponents -fileListName "ValueGeneratorCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "ValueGeneratorImagesCmpFiles" -wxsFilePath $PSScriptRoot\Run.wxs -regroot $registryroot
+## Plugins
+
+#ShortcutGuide
+Generate-FileList -fileDepsJson "" -fileListName ShortcutGuideSvgFiles -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\ShortcutGuide\"
+Generate-FileComponents -fileListName "ShortcutGuideSvgFiles" -wxsFilePath $PSScriptRoot\ShortcutGuide.wxs -regroot $registryroot
+
+#Settings
+Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\"
+Generate-FileList -fileDepsJson "" -fileListName SettingsV2AssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Modules\"
+Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsModulesFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Modules\OOBE\"
+Generate-FileList -fileDepsJson "" -fileListName SettingsV2OOBEAssetsFluentIconsFiles -wxsFilePath $PSScriptRoot\Settings.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\Settings\Icons\"
+Generate-FileComponents -fileListName "SettingsV2AssetsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "SettingsV2AssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "SettingsV2OOBEAssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
+Generate-FileComponents -fileListName "SettingsV2OOBEAssetsFluentIconsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot
+
+#Workspaces
+Generate-FileList -fileDepsJson "" -fileListName WorkspacesImagesComponentFiles -wxsFilePath $PSScriptRoot\Workspaces.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Workspaces\"
+Generate-FileComponents -fileListName "WorkspacesImagesComponentFiles" -wxsFilePath $PSScriptRoot\Workspaces.wxs -regroot $registryroot
diff --git a/installer/PowerToysSetupVNext/generateMonacoWxs.ps1 b/installer/PowerToysSetupVNext/generateMonacoWxs.ps1
new file mode 100644
index 0000000000..da2db6ae80
--- /dev/null
+++ b/installer/PowerToysSetupVNext/generateMonacoWxs.ps1
@@ -0,0 +1,97 @@
+[CmdletBinding()]
+Param(
+ [Parameter(Mandatory = $True, Position = 1)]
+ [string]$monacoWxsFile,
+ [Parameter(Mandatory = $True, Position = 2)]
+ [string]$platform,
+ [Parameter(Mandatory = $True, Position = 3)]
+ [string]$nugetHeatPath
+)
+
+$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
+
+if ($platform -eq "x64") {
+ $HeatPath = Join-Path $nugetHeatPath "tools\net472\x64"
+} else {
+ $HeatPath = Join-Path $nugetHeatPath "tools\net472\x86"
+}
+
+# Validate heat.exe exists at the resolved path; fail fast if not found.
+$heatExe = Join-Path $HeatPath "heat.exe"
+if (-not (Test-Path $heatExe)) {
+ Write-Error "heat.exe not found at '$heatExe'. Ensure the WixToolset.Heat package (5.0.2) is restored under '$nugetHeatPath'."
+ exit 1
+}
+
+$SourceDir = Join-Path $scriptDir "..\..\src\Monaco\monacoSRC" # Now relative to script location
+$OutputFile = Join-Path $scriptDir "MonacoSRC.wxs"
+$ComponentGroup = "MonacoSRCHeatGenerated"
+$DirectoryRef = "MonacoPreviewHandlerMonacoSRCFolder"
+$Variable = "var.MonacoSRCHarvestPath"
+
+& $heatExe dir "$SourceDir" -out "$OutputFile" -cg "$ComponentGroup" -dr "$DirectoryRef" -var "$Variable" -gg -srd -nologo
+
+$fileWxs = Get-Content $monacoWxsFile;
+
+$fileWxs = $fileWxs -replace " KeyPath=`"yes`" ", " "
+
+$newFileContent = ""
+
+$componentId = "error"
+$directories = @()
+
+$fileWxs | ForEach-Object {
+ $line = $_;
+ if ($line -match "") {
+ $line +=
+@"
+`r`n
+ `r`n
+"@
+ }
+ if ($line -match "") {
+ $directories += $matches[1]
+ }
+ if ($line -match "") {
+ $line =
+@"
+
+
+
+
+"@
+ }
+
+ $newFileContent += $line + "`r`n";
+}
+
+$removeFolderEntries =
+@"
+`r`n
+
+
+ `r`n
+"@
+
+$directories | ForEach-Object {
+
+ $removeFolderEntries +=
+@"
+
+
+"@
+}
+
+$removeFolderEntries +=
+@"
+
+"@
+
+
+
+$newFileContent = $newFileContent -replace "\s+()", "$removeFolderEntries`r`n "
+
+Set-Content -Path $monacoWxsFile -Value $newFileContent
\ No newline at end of file
diff --git a/installer/PowerToysSetupVNext/packages.config b/installer/PowerToysSetupVNext/packages.config
new file mode 100644
index 0000000000..569e1bea86
--- /dev/null
+++ b/installer/PowerToysSetupVNext/packages.config
@@ -0,0 +1,3 @@
+ο»Ώ
+
+
diff --git a/installer/PowerToysSetupVNext/publish.cmd b/installer/PowerToysSetupVNext/publish.cmd
new file mode 100644
index 0000000000..f61668cffe
--- /dev/null
+++ b/installer/PowerToysSetupVNext/publish.cmd
@@ -0,0 +1,17 @@
+setlocal enableDelayedExpansion
+
+IF NOT DEFINED PTRoot (SET PTRoot=..\..)
+
+SET PlatformArg=%1
+IF NOT DEFINED PlatformArg (SET PlatformArg=x64)
+SET VCToolsVersion=!VCToolsVersion!
+SET ClearDevCommandPromptEnvVars=false
+
+rem In case of Release we should not use Debug CRT in VCRT forwarders
+msbuild !PTRoot!\src\modules\previewpane\MonacoPreviewHandler\MonacoPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
+
+msbuild !PTRoot!\src\modules\previewpane\MarkdownPreviewHandler\MarkdownPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
+
+msbuild !PTRoot!\src\modules\previewpane\SvgPreviewHandler\SvgPreviewHandler.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
+
+msbuild !PTRoot!\src\modules\previewpane\SvgThumbnailProvider\SvgThumbnailProvider.csproj -t:Publish -p:Configuration="Release" -p:Platform="!PlatformArg!" -p:AppxBundle=Never -p:PowerToysRoot=!PTRoot! -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=InstallationPublishProfile.pubxml -p:TargetFramework=net9.0-windows10.0.26100.0
\ No newline at end of file
diff --git a/installer/PowerToysSetupVNext/terminate_powertoys.cmd b/installer/PowerToysSetupVNext/terminate_powertoys.cmd
new file mode 100644
index 0000000000..8206b90aae
--- /dev/null
+++ b/installer/PowerToysSetupVNext/terminate_powertoys.cmd
@@ -0,0 +1,12 @@
+@echo off
+setlocal ENABLEDELAYEDEXPANSION
+
+@REM We loop here until taskkill cannot find a PowerToys process. We can't use /F flag, because it
+@REM doesn't give application an opportunity to cleanup. Thus we send WM_CLOSE which is being caught
+@REM by multiple windows running a msg loop in PowerToys.exe process, which we close one by one.
+for /l %%x in (1, 1, 100) do (
+ taskkill /IM PowerToys.exe 1>NUL 2>NUL
+ if !ERRORLEVEL! NEQ 0 goto quit
+)
+
+:quit
\ No newline at end of file
diff --git a/tools/Verification scripts/verify-installation-script.ps1 b/tools/Verification scripts/verify-installation-script.ps1
new file mode 100644
index 0000000000..d617aca9a7
--- /dev/null
+++ b/tools/Verification scripts/verify-installation-script.ps1
@@ -0,0 +1,831 @@
+#Requires -Version 5.1
+
+<#
+.SYNOPSIS
+ Verifies a PowerToys installation by checking all components, registry entries, files, and custom logic.
+
+.DESCRIPTION
+ This script comprehensively verifies a PowerToys installation by checking:
+ - Registry entries for both per-machine and per-user installations
+ - File and folder structure integrity
+ - Module registration and functionality
+ - WiX installer logic verification
+ - Custom action results
+ - DSC module installation
+ - Command Palette packages
+
+.PARAMETER InstallScope
+ Specifies the installation scope to verify. Valid values are 'PerMachine' or 'PerUser'.
+ Default is 'PerMachine'.
+
+.PARAMETER InstallPath
+ Optional. Specifies a custom installation path to verify. If not provided, the script will
+ detect the installation path from the registry.
+
+.EXAMPLE
+ .\verify-installation-script.ps1 -InstallScope PerMachine
+
+.EXAMPLE
+ .\verify-installation-script.ps1 -InstallScope PerUser
+
+.NOTES
+ Author: PowerToys Team
+ Requires: PowerShell 5.1 or later
+ Requires: Administrative privileges for per-machine verification
+#>
+
+[CmdletBinding()]
+param(
+ [Parameter(Mandatory = $false)]
+ [ValidateSet('PerMachine', 'PerUser')]
+ [string]$InstallScope = 'PerMachine',
+
+ [Parameter(Mandatory = $false)]
+ [string]$InstallPath
+)
+
+# Initialize results tracking
+$script:Results = @{
+ Summary = @{
+ TotalChecks = 0
+ PassedChecks = 0
+ FailedChecks = 0
+ WarningChecks = 0
+ OverallStatus = "Unknown"
+ }
+ Details = @{}
+ Timestamp = Get-Date
+ Computer = $env:COMPUTERNAME
+ User = $env:USERNAME
+ PowerShellVersion = $PSVersionTable.PSVersion.ToString()
+}
+
+# PowerToys constants
+$PowerToysUpgradeCodePerMachine = "{42B84BF7-5FBF-473B-9C8B-049DC16F7708}"
+$PowerToysUpgradeCodePerUser = "{D8B559DB-4C98-487A-A33F-50A8EEE42726}"
+$PowerToysRegistryKeyPerMachine = "HKLM:\SOFTWARE\Classes\PowerToys"
+$PowerToysRegistryKeyPerUser = "HKCU:\SOFTWARE\Classes\PowerToys"
+
+# Utility functions
+function Write-StatusMessage {
+ param(
+ [string]$Message,
+ [ValidateSet('Info', 'Success', 'Warning', 'Error')]
+ [string]$Level = 'Info'
+ )
+
+ $color = switch ($Level) {
+ 'Info' { 'White' }
+ 'Success' { 'Green' }
+ 'Warning' { 'Yellow' }
+ 'Error' { 'Red' }
+ }
+
+ $prefix = switch ($Level) {
+ 'Info' { '[INFO]' }
+ 'Success' { '[PASS]' }
+ 'Warning' { '[WARN]' }
+ 'Error' { '[FAIL]' }
+ }
+
+ Write-Host "$prefix $Message" -ForegroundColor $color
+}
+
+function Add-CheckResult {
+ param(
+ [string]$Category,
+ [string]$CheckName,
+ [string]$Status,
+ [string]$Message,
+ [object]$Details = $null
+ )
+
+ $script:Results.Summary.TotalChecks++
+
+ switch ($Status) {
+ 'Pass' { $script:Results.Summary.PassedChecks++ }
+ 'Fail' { $script:Results.Summary.FailedChecks++ }
+ 'Warning' { $script:Results.Summary.WarningChecks++ }
+ }
+
+ if (-not $script:Results.Details.ContainsKey($Category)) {
+ $script:Results.Details[$Category] = @{}
+ }
+
+ $checkDetails = @{
+ Status = $Status
+ Message = $Message
+ Details = $Details
+ Timestamp = Get-Date
+ }
+
+ $script:Results.Details[$Category][$CheckName] = $checkDetails
+
+ # Always show all checks with their status
+ $level = switch ($Status) {
+ 'Pass' { 'Success' }
+ 'Fail' { 'Error' }
+ 'Warning' { 'Warning' }
+ }
+ Write-StatusMessage "[$Category] $CheckName - $Message" -Level $level
+}
+
+function Test-RegistryKey {
+ param(
+ [string]$Path
+ )
+ try {
+ return Test-Path $Path
+ }
+ catch {
+ return $false
+ }
+}
+
+function Get-RegistryValue {
+ param(
+ [string]$Path,
+ [string]$Name,
+ [object]$DefaultValue = $null
+ )
+ try {
+ $value = Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue
+ return $value.$Name
+ }
+ catch {
+ return $DefaultValue
+ }
+}
+
+function Test-PowerToysInstallation {
+ param(
+ [ValidateSet('PerMachine', 'PerUser')]
+ [string]$Scope
+ )
+
+ Write-StatusMessage "Verifying PowerToys $Scope installation..." -Level Info
+
+ # Determine registry paths based on scope
+ $registryKey = if ($Scope -eq 'PerMachine') { $PowerToysRegistryKeyPerMachine } else { $PowerToysRegistryKeyPerUser }
+
+ # Check main registry key
+ $mainKeyExists = Test-RegistryKey -Path $registryKey
+ Add-CheckResult -Category "Registry" -CheckName "Main Registry Key ($Scope)" -Status $(if ($mainKeyExists) { 'Pass' } else { 'Fail' }) -Message "Registry key exists: $registryKey"
+
+ if (-not $mainKeyExists) {
+ Add-CheckResult -Category "Installation" -CheckName "Installation Status ($Scope)" -Status 'Fail' -Message "PowerToys $Scope installation not found"
+ return $false
+ }
+
+ # Check install scope value
+ $installScopeValue = Get-RegistryValue -Path $registryKey -Name "InstallScope"
+ $expectedScope = $Scope.ToLower()
+ if ($Scope -eq 'PerMachine') { $expectedScope = 'perMachine' }
+ if ($Scope -eq 'PerUser') { $expectedScope = 'perUser' }
+
+ $scopeCorrect = $installScopeValue -eq $expectedScope
+ Add-CheckResult -Category "Registry" -CheckName "Install Scope Value ($Scope)" -Status $(if ($scopeCorrect) { 'Pass' } else { 'Fail' }) -Message "Install scope: Expected '$expectedScope', Found '$installScopeValue'"
+
+ # Check for uninstall registry entry (this is what makes PowerToys appear in Add/Remove Programs)
+ $uninstallKey = if ($Scope -eq 'PerMachine') {
+ "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
+ }
+ else {
+ "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
+ }
+
+ try {
+ $powerToysEntry = Get-ItemProperty -Path $uninstallKey | Where-Object {
+ $_.DisplayName -like "*PowerToys*"
+ } | Select-Object -First 1
+
+ if ($powerToysEntry) {
+ Add-CheckResult -Category "Registry" -CheckName "Uninstall Registry Entry ($Scope)" -Status 'Pass' -Message "PowerToys uninstall entry found with DisplayName: $($powerToysEntry.DisplayName)"
+
+ # Note: InstallLocation may or may not be set in the uninstall registry
+ # This is normal behavior as PowerToys uses direct file references for system bindings
+ if ($powerToysEntry.InstallLocation) {
+ Add-CheckResult -Category "Registry" -CheckName "Install Location Registry ($Scope)" -Status 'Pass' -Message "InstallLocation found: $($powerToysEntry.InstallLocation)"
+ }
+ # No need to report missing InstallLocation as it's not required
+ }
+ else {
+ Add-CheckResult -Category "Registry" -CheckName "Uninstall Registry Entry ($Scope)" -Status 'Fail' -Message "PowerToys uninstall entry not found in Windows uninstall registry"
+ }
+ }
+ catch {
+ Add-CheckResult -Category "Registry" -CheckName "Uninstall Registry Entry ($Scope)" -Status 'Fail' -Message "Failed to read Windows uninstall registry"
+ }
+
+ # Check for installation folder
+ $installFolder = Get-PowerToysInstallPath -Scope $Scope
+ if ($installFolder -and (Test-Path $installFolder)) {
+ Add-CheckResult -Category "Installation" -CheckName "Install Folder ($Scope)" -Status 'Pass' -Message "Installation folder exists: $installFolder"
+
+ # Verify core files
+ Test-CoreFiles -InstallPath $installFolder -Scope $Scope
+
+ # Verify modules
+ Test-ModuleFiles -InstallPath $installFolder -Scope $Scope
+
+ return $true
+ }
+ else {
+ Add-CheckResult -Category "Installation" -CheckName "Install Folder ($Scope)" -Status 'Fail' -Message "Installation folder not found or inaccessible: $installFolder"
+ return $false
+ }
+}
+
+function Get-PowerToysInstallPath {
+ param(
+ [ValidateSet('PerMachine', 'PerUser')]
+ [string]$Scope
+ )
+
+ if ($InstallPath) {
+ return $InstallPath
+ }
+
+ # Since InstallLocation may not be reliably set in the uninstall registry,
+ # we'll use the default installation paths based on scope
+ if ($Scope -eq 'PerMachine') {
+ $defaultPath = "${env:ProgramFiles}\PowerToys"
+ }
+ else {
+ $defaultPath = "${env:LOCALAPPDATA}\PowerToys"
+ }
+
+ # Verify the path exists before returning it
+ if (Test-Path $defaultPath) {
+ return $defaultPath
+ }
+
+ # If default path doesn't exist, try to get it from uninstall registry as fallback
+ $uninstallKey = if ($Scope -eq 'PerMachine') {
+ "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
+ }
+ else {
+ "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
+ }
+
+ try {
+ $powerToysEntry = Get-ItemProperty -Path $uninstallKey | Where-Object {
+ $_.DisplayName -like "*PowerToys*"
+ } | Select-Object -First 1
+
+ # Check for InstallLocation first, but it may not exist
+ if ($powerToysEntry -and $powerToysEntry.InstallLocation) {
+ return $powerToysEntry.InstallLocation.TrimEnd('\')
+ }
+
+ # Check for UninstallString as alternative source of install path
+ if ($powerToysEntry -and $powerToysEntry.UninstallString) {
+ # Extract directory from uninstall string like "C:\Program Files\PowerToys\unins000.exe"
+ $uninstallExe = $powerToysEntry.UninstallString.Trim('"')
+ $installDir = Split-Path $uninstallExe -Parent
+ if ($installDir -and (Test-Path $installDir)) {
+ return $installDir
+ }
+ }
+ }
+ catch {
+ # If registry read fails, fall back to null
+ }
+
+ # If we can't determine the install path, return null
+ return $null
+}
+
+function Test-CoreFiles {
+ param(
+ [string]$InstallPath,
+ [string]$Scope
+ )
+
+ # Essential core files (must exist for basic functionality)
+ $essentialCoreFiles = @(
+ 'PowerToys.exe',
+ 'PowerToys.ActionRunner.exe',
+ 'License.rtf',
+ 'Notice.md'
+ )
+
+ # Critical signed PowerToys executable files (from ESRP signing config)
+ $criticalSignedFiles = @(
+ # Main PowerToys components
+ 'PowerToys.exe',
+ 'PowerToys.ActionRunner.exe',
+ 'PowerToys.Update.exe',
+ 'PowerToys.BackgroundActivatorDLL.dll',
+ 'PowerToys.FilePreviewCommon.dll',
+ 'PowerToys.Interop.dll',
+
+ # Common libraries
+ 'CalculatorEngineCommon.dll',
+ 'PowerToys.ManagedTelemetry.dll',
+ 'PowerToys.ManagedCommon.dll',
+ 'PowerToys.ManagedCsWin32.dll',
+ 'PowerToys.Common.UI.dll',
+ 'PowerToys.Settings.UI.Lib.dll',
+ 'PowerToys.GPOWrapper.dll',
+ 'PowerToys.GPOWrapperProjection.dll',
+ 'PowerToys.AllExperiments.dll',
+
+ # Module executables and libraries
+ 'PowerToys.AlwaysOnTop.exe',
+ 'PowerToys.AlwaysOnTopModuleInterface.dll',
+ 'PowerToys.CmdNotFoundModuleInterface.dll',
+ 'PowerToys.ColorPicker.dll',
+ 'PowerToys.ColorPickerUI.dll',
+ 'PowerToys.ColorPickerUI.exe',
+ 'PowerToys.CropAndLockModuleInterface.dll',
+ 'PowerToys.CropAndLock.exe',
+ 'PowerToys.PowerOCRModuleInterface.dll',
+ 'PowerToys.PowerOCR.dll',
+ 'PowerToys.PowerOCR.exe',
+ 'PowerToys.AdvancedPasteModuleInterface.dll',
+ 'PowerToys.AwakeModuleInterface.dll',
+ 'PowerToys.Awake.exe',
+ 'PowerToys.Awake.dll',
+
+ # FancyZones
+ 'PowerToys.FancyZonesEditor.exe',
+ 'PowerToys.FancyZonesEditor.dll',
+ 'PowerToys.FancyZonesEditorCommon.dll',
+ 'PowerToys.FancyZonesModuleInterface.dll',
+ 'PowerToys.FancyZones.exe',
+
+ # Preview handlers
+ 'PowerToys.GcodePreviewHandler.dll',
+ 'PowerToys.GcodePreviewHandler.exe',
+ 'PowerToys.GcodePreviewHandlerCpp.dll',
+ 'PowerToys.GcodeThumbnailProvider.dll',
+ 'PowerToys.GcodeThumbnailProvider.exe',
+ 'PowerToys.GcodeThumbnailProviderCpp.dll',
+ 'PowerToys.BgcodePreviewHandler.dll',
+ 'PowerToys.BgcodePreviewHandler.exe',
+ 'PowerToys.BgcodePreviewHandlerCpp.dll',
+ 'PowerToys.BgcodeThumbnailProvider.dll',
+ 'PowerToys.BgcodeThumbnailProvider.exe',
+ 'PowerToys.BgcodeThumbnailProviderCpp.dll',
+ 'PowerToys.MarkdownPreviewHandler.dll',
+ 'PowerToys.MarkdownPreviewHandler.exe',
+ 'PowerToys.MarkdownPreviewHandlerCpp.dll',
+ 'PowerToys.MonacoPreviewHandler.dll',
+ 'PowerToys.MonacoPreviewHandler.exe',
+ 'PowerToys.MonacoPreviewHandlerCpp.dll',
+ 'PowerToys.PdfPreviewHandler.dll',
+ 'PowerToys.PdfPreviewHandler.exe',
+ 'PowerToys.PdfPreviewHandlerCpp.dll',
+ 'PowerToys.PdfThumbnailProvider.dll',
+ 'PowerToys.PdfThumbnailProvider.exe',
+ 'PowerToys.PdfThumbnailProviderCpp.dll',
+ 'PowerToys.powerpreview.dll',
+ 'PowerToys.PreviewHandlerCommon.dll',
+ 'PowerToys.QoiPreviewHandler.dll',
+ 'PowerToys.QoiPreviewHandler.exe',
+ 'PowerToys.QoiPreviewHandlerCpp.dll',
+ 'PowerToys.QoiThumbnailProvider.dll',
+ 'PowerToys.QoiThumbnailProvider.exe',
+ 'PowerToys.QoiThumbnailProviderCpp.dll',
+ 'PowerToys.StlThumbnailProvider.dll',
+ 'PowerToys.StlThumbnailProvider.exe',
+ 'PowerToys.StlThumbnailProviderCpp.dll',
+ 'PowerToys.SvgPreviewHandler.dll',
+ 'PowerToys.SvgPreviewHandler.exe',
+ 'PowerToys.SvgPreviewHandlerCpp.dll',
+ 'PowerToys.SvgThumbnailProvider.dll',
+ 'PowerToys.SvgThumbnailProvider.exe',
+ 'PowerToys.SvgThumbnailProviderCpp.dll',
+
+ # Image Resizer
+ 'PowerToys.ImageResizer.exe',
+ 'PowerToys.ImageResizer.dll',
+ 'PowerToys.ImageResizerExt.dll',
+ 'PowerToys.ImageResizerContextMenu.dll',
+
+ # Keyboard Manager
+ 'PowerToys.KeyboardManager.dll',
+ 'PowerToys.KeyboardManagerEditorLibraryWrapper.dll',
+
+ # PowerToys Run
+ 'PowerToys.Launcher.dll',
+ 'PowerToys.PowerLauncher.dll',
+ 'PowerToys.PowerLauncher.exe',
+ 'PowerToys.PowerLauncher.Telemetry.dll',
+ 'Wox.Infrastructure.dll',
+ 'Wox.Plugin.dll',
+
+ # Mouse utilities
+ 'PowerToys.FindMyMouse.dll',
+ 'PowerToys.MouseHighlighter.dll',
+ 'PowerToys.MouseJump.dll',
+ 'PowerToys.MouseJump.Common.dll',
+ 'PowerToys.MousePointerCrosshairs.dll',
+ 'PowerToys.MouseJumpUI.dll',
+ 'PowerToys.MouseJumpUI.exe',
+ 'PowerToys.MouseWithoutBorders.dll',
+ 'PowerToys.MouseWithoutBorders.exe',
+ 'PowerToys.MouseWithoutBordersModuleInterface.dll',
+ 'PowerToys.MouseWithoutBordersService.dll',
+ 'PowerToys.MouseWithoutBordersService.exe',
+ 'PowerToys.MouseWithoutBordersHelper.dll',
+ 'PowerToys.MouseWithoutBordersHelper.exe',
+
+ # PowerAccent
+ 'PowerAccent.Core.dll',
+ 'PowerToys.PowerAccent.dll',
+ 'PowerToys.PowerAccent.exe',
+ 'PowerToys.PowerAccentModuleInterface.dll',
+ 'PowerToys.PowerAccentKeyboardService.dll',
+
+ # Workspaces
+ 'PowerToys.WorkspacesSnapshotTool.exe',
+ 'PowerToys.WorkspacesLauncher.exe',
+ 'PowerToys.WorkspacesWindowArranger.exe',
+ 'PowerToys.WorkspacesEditor.exe',
+ 'PowerToys.WorkspacesEditor.dll',
+ 'PowerToys.WorkspacesLauncherUI.exe',
+ 'PowerToys.WorkspacesLauncherUI.dll',
+ 'PowerToys.WorkspacesModuleInterface.dll',
+ 'PowerToys.WorkspacesCsharpLibrary.dll',
+
+ # Shortcut Guide
+ 'PowerToys.ShortcutGuide.exe',
+ 'PowerToys.ShortcutGuideModuleInterface.dll',
+
+ # ZoomIt
+ 'PowerToys.ZoomIt.exe',
+ 'PowerToys.ZoomItModuleInterface.dll',
+ 'PowerToys.ZoomItSettingsInterop.dll',
+
+ # Command Palette
+ 'PowerToys.CmdPalModuleInterface.dll',
+ 'CmdPalKeyboardService.dll'
+ )
+
+ # WinUI3Apps signed files (in WinUI3Apps subdirectory)
+ $winUI3SignedFiles = @(
+ 'PowerToys.Settings.dll',
+ 'PowerToys.Settings.exe',
+ 'PowerToys.AdvancedPaste.exe',
+ 'PowerToys.AdvancedPaste.dll',
+ 'PowerToys.HostsModuleInterface.dll',
+ 'PowerToys.HostsUILib.dll',
+ 'PowerToys.Hosts.dll',
+ 'PowerToys.Hosts.exe',
+ 'PowerToys.FileLocksmithLib.Interop.dll',
+ 'PowerToys.FileLocksmithExt.dll',
+ 'PowerToys.FileLocksmithUI.exe',
+ 'PowerToys.FileLocksmithUI.dll',
+ 'PowerToys.FileLocksmithContextMenu.dll',
+ 'Peek.Common.dll',
+ 'Peek.FilePreviewer.dll',
+ 'Powertoys.Peek.UI.dll',
+ 'Powertoys.Peek.UI.exe',
+ 'Powertoys.Peek.dll',
+ 'PowerToys.EnvironmentVariablesModuleInterface.dll',
+ 'PowerToys.EnvironmentVariablesUILib.dll',
+ 'PowerToys.EnvironmentVariables.dll',
+ 'PowerToys.EnvironmentVariables.exe',
+ 'PowerToys.MeasureToolModuleInterface.dll',
+ 'PowerToys.MeasureToolCore.dll',
+ 'PowerToys.MeasureToolUI.dll',
+ 'PowerToys.MeasureToolUI.exe',
+ 'PowerToys.NewPlus.ShellExtension.dll',
+ 'PowerToys.NewPlus.ShellExtension.win10.dll',
+ 'PowerToys.PowerRenameExt.dll',
+ 'PowerToys.PowerRename.exe',
+ 'PowerToys.PowerRenameContextMenu.dll',
+ 'PowerToys.RegistryPreviewExt.dll',
+ 'PowerToys.RegistryPreviewUILib.dll',
+ 'PowerToys.RegistryPreview.dll',
+ 'PowerToys.RegistryPreview.exe'
+ )
+
+ # Tools signed files (in Tools subdirectory)
+ $toolsSignedFiles = @(
+ 'PowerToys.BugReportTool.exe'
+ )
+
+ # KeyboardManager signed files (in specific subdirectories)
+ $keyboardManagerFiles = @{
+ 'KeyboardManagerEditor\PowerToys.KeyboardManagerEditor.exe' = 'KeyboardManagerEditor'
+ 'KeyboardManagerEngine\PowerToys.KeyboardManagerEngine.exe' = 'KeyboardManagerEngine'
+ }
+
+ # Run plugins signed files (in RunPlugins subdirectories)
+ $runPluginFiles = @{
+ 'RunPlugins\Calculator\Microsoft.PowerToys.Run.Plugin.Calculator.dll' = 'Calculator plugin'
+ 'RunPlugins\Folder\Microsoft.Plugin.Folder.dll' = 'Folder plugin'
+ 'RunPlugins\Indexer\Microsoft.Plugin.Indexer.dll' = 'Indexer plugin'
+ 'RunPlugins\OneNote\Microsoft.PowerToys.Run.Plugin.OneNote.dll' = 'OneNote plugin'
+ 'RunPlugins\History\Microsoft.PowerToys.Run.Plugin.History.dll' = 'History plugin'
+ 'RunPlugins\PowerToys\Microsoft.PowerToys.Run.Plugin.PowerToys.dll' = 'PowerToys plugin'
+ 'RunPlugins\Program\Microsoft.Plugin.Program.dll' = 'Program plugin'
+ 'RunPlugins\Registry\Microsoft.PowerToys.Run.Plugin.Registry.dll' = 'Registry plugin'
+ 'RunPlugins\WindowsSettings\Microsoft.PowerToys.Run.Plugin.WindowsSettings.dll' = 'Windows Settings plugin'
+ 'RunPlugins\Shell\Microsoft.Plugin.Shell.dll' = 'Shell plugin'
+ 'RunPlugins\Uri\Microsoft.Plugin.Uri.dll' = 'URI plugin'
+ 'RunPlugins\WindowWalker\Microsoft.Plugin.WindowWalker.dll' = 'Window Walker plugin'
+ 'RunPlugins\UnitConverter\Community.PowerToys.Run.Plugin.UnitConverter.dll' = 'Unit Converter plugin'
+ 'RunPlugins\VSCodeWorkspaces\Community.PowerToys.Run.Plugin.VSCodeWorkspaces.dll' = 'VS Code Workspaces plugin'
+ 'RunPlugins\Service\Microsoft.PowerToys.Run.Plugin.Service.dll' = 'Service plugin'
+ 'RunPlugins\System\Microsoft.PowerToys.Run.Plugin.System.dll' = 'System plugin'
+ 'RunPlugins\TimeDate\Microsoft.PowerToys.Run.Plugin.TimeDate.dll' = 'Time Date plugin'
+ 'RunPlugins\ValueGenerator\Community.PowerToys.Run.Plugin.ValueGenerator.dll' = 'Value Generator plugin'
+ 'RunPlugins\WebSearch\Community.PowerToys.Run.Plugin.WebSearch.dll' = 'Web Search plugin'
+ 'RunPlugins\WindowsTerminal\Microsoft.PowerToys.Run.Plugin.WindowsTerminal.dll' = 'Windows Terminal plugin'
+ }
+
+ # Check essential core files (must exist)
+ Write-StatusMessage "Checking essential core files..." -Level Info
+ foreach ($file in $essentialCoreFiles) {
+ $filePath = Join-Path $InstallPath $file
+ $exists = Test-Path $filePath
+ Add-CheckResult -Category "Core Files" -CheckName "$file ($Scope)" -Status $(if ($exists) { 'Pass' } else { 'Fail' }) -Message "Essential file: $filePath"
+ }
+
+ # Check critical signed files in root directory
+ Write-StatusMessage "Checking critical signed files in root directory..." -Level Info
+ foreach ($file in $criticalSignedFiles) {
+ $filePath = Join-Path $InstallPath $file
+ $exists = Test-Path $filePath
+ # Most signed files are critical, but some may be optional depending on configuration
+ $status = if ($exists) { 'Pass' } else { 'Warning' }
+ Add-CheckResult -Category "Signed Files" -CheckName "$file ($Scope)" -Status $status -Message "Signed file: $filePath"
+ }
+
+ # Check WinUI3Apps signed files
+ Write-StatusMessage "Checking WinUI3Apps signed files..." -Level Info
+ foreach ($file in $winUI3SignedFiles) {
+ $filePath = Join-Path $InstallPath "WinUI3Apps\$file"
+ $exists = Test-Path $filePath
+ $status = if ($exists) { 'Pass' } else { 'Warning' }
+ Add-CheckResult -Category "Signed Files" -CheckName "WinUI3Apps\$file ($Scope)" -Status $status -Message "WinUI3 signed file: $filePath"
+ }
+
+ # Check Tools signed files
+ Write-StatusMessage "Checking Tools signed files..." -Level Info
+ foreach ($file in $toolsSignedFiles) {
+ $filePath = Join-Path $InstallPath "Tools\$file"
+ $exists = Test-Path $filePath
+ $status = if ($exists) { 'Pass' } else { 'Warning' }
+ Add-CheckResult -Category "Signed Files" -CheckName "Tools\$file ($Scope)" -Status $status -Message "Tools signed file: $filePath"
+ }
+
+ # Check KeyboardManager files
+ Write-StatusMessage "Checking KeyboardManager signed files..." -Level Info
+ foreach ($relativePath in $keyboardManagerFiles.Keys) {
+ $filePath = Join-Path $InstallPath $relativePath
+ $exists = Test-Path $filePath
+ $status = if ($exists) { 'Pass' } else { 'Warning' }
+ $description = $keyboardManagerFiles[$relativePath]
+ Add-CheckResult -Category "Signed Files" -CheckName "$relativePath ($Scope)" -Status $status -Message "KeyboardManager $description signed file: $filePath"
+ }
+
+ # Check Run plugins files
+ Write-StatusMessage "Checking PowerToys Run plugin files..." -Level Info
+ foreach ($relativePath in $runPluginFiles.Keys) {
+ $filePath = Join-Path $InstallPath $relativePath
+ $exists = Test-Path $filePath
+ $status = if ($exists) { 'Pass' } else { 'Warning' }
+ $description = $runPluginFiles[$relativePath]
+ Add-CheckResult -Category "Signed Files" -CheckName "$relativePath ($Scope)" -Status $status -Message "PowerToys Run $description signed file: $filePath"
+ }
+}
+
+function Test-ModuleFiles {
+ param(
+ [string]$InstallPath,
+ [string]$Scope
+ )
+
+ # PowerToys does not actually install modules in a "modules" subfolder.
+ # Instead, modules are integrated directly into the main installation or specific subfolders.
+ # Check for key module directories that should exist:
+
+ # Check KeyboardManager components (installed as separate folders)
+ $keyboardManagerEditor = Join-Path $InstallPath "KeyboardManagerEditor"
+ $keyboardManagerEngine = Join-Path $InstallPath "KeyboardManagerEngine"
+
+ if (Test-Path $keyboardManagerEditor) {
+ Add-CheckResult -Category "Modules" -CheckName "KeyboardManager Editor ($Scope)" -Status 'Pass' -Message "KeyboardManager Editor folder exists: $keyboardManagerEditor"
+ }
+ else {
+ Add-CheckResult -Category "Modules" -CheckName "KeyboardManager Editor ($Scope)" -Status 'Warning' -Message "KeyboardManager Editor folder not found: $keyboardManagerEditor"
+ }
+
+ if (Test-Path $keyboardManagerEngine) {
+ Add-CheckResult -Category "Modules" -CheckName "KeyboardManager Engine ($Scope)" -Status 'Pass' -Message "KeyboardManager Engine folder exists: $keyboardManagerEngine"
+ }
+ else {
+ Add-CheckResult -Category "Modules" -CheckName "KeyboardManager Engine ($Scope)" -Status 'Warning' -Message "KeyboardManager Engine folder not found: $keyboardManagerEngine"
+ }
+
+ # Check RunPlugins folder (contains PowerToys Run modules)
+ $runPluginsPath = Join-Path $InstallPath "RunPlugins"
+ if (Test-Path $runPluginsPath) {
+ Add-CheckResult -Category "Modules" -CheckName "Run Plugins Folder ($Scope)" -Status 'Pass' -Message "Run plugins folder exists: $runPluginsPath"
+ }
+ else {
+ Add-CheckResult -Category "Modules" -CheckName "Run Plugins Folder ($Scope)" -Status 'Warning' -Message "Run plugins folder not found: $runPluginsPath"
+ }
+
+ # Check Tools folder
+ $toolsPath = Join-Path $InstallPath "Tools"
+ if (Test-Path $toolsPath) {
+ Add-CheckResult -Category "Modules" -CheckName "Tools Folder ($Scope)" -Status 'Pass' -Message "Tools folder exists: $toolsPath"
+ }
+ else {
+ Add-CheckResult -Category "Modules" -CheckName "Tools Folder ($Scope)" -Status 'Warning' -Message "Tools folder not found: $toolsPath"
+ }
+}
+
+function Test-RegistryHandlers {
+ param(
+ [string]$Scope
+ )
+
+ $registryRoot = if ($Scope -eq 'PerMachine') { 'HKLM:' } else { 'HKCU:' }
+ # Test URL protocol handler
+ $protocolPath = "$registryRoot\SOFTWARE\Classes\powertoys"
+ if (Test-RegistryKey -Path $protocolPath) {
+ Add-CheckResult -Category "Registry Handlers" -CheckName "PowerToys URL Protocol ($Scope)" -Status 'Pass' -Message "URL protocol registered"
+
+ # Check command handler
+ $commandPath = "$protocolPath\shell\open\command"
+ if (Test-RegistryKey -Path $commandPath) {
+ Add-CheckResult -Category "Registry Handlers" -CheckName "PowerToys URL Command ($Scope)" -Status 'Pass' -Message "URL command handler registered"
+ }
+ else {
+ Add-CheckResult -Category "Registry Handlers" -CheckName "PowerToys URL Command ($Scope)" -Status 'Fail' -Message "URL command handler not found"
+ }
+ }
+ else {
+ Add-CheckResult -Category "Registry Handlers" -CheckName "PowerToys URL Protocol ($Scope)" -Status 'Fail' -Message "URL protocol not registered"
+ }
+
+ # Test CLSID registration for toast notifications
+ $toastClsidPath = "$registryRoot\SOFTWARE\Classes\CLSID\{DD5CACDA-7C2E-4997-A62A-04A597B58F76}"
+ if (Test-RegistryKey -Path $toastClsidPath) {
+ Add-CheckResult -Category "Registry Handlers" -CheckName "Toast Notification CLSID ($Scope)" -Status 'Pass' -Message "Toast notification CLSID registered"
+ }
+ else {
+ Add-CheckResult -Category "Registry Handlers" -CheckName "Toast Notification CLSID ($Scope)" -Status 'Warning' -Message "Toast notification CLSID not found"
+ }
+}
+
+function Test-DSCModule {
+ param(
+ [string]$Scope
+ )
+
+ if ($Scope -eq 'PerUser') {
+ # For per-user installations, DSC module is installed via custom action to user's Documents
+ $userModulesPath = "$env:USERPROFILE\Documents\PowerShell\Modules\Microsoft.PowerToys.Configure"
+ if (Test-Path $userModulesPath) {
+ Add-CheckResult -Category "DSC Module" -CheckName "DSC Module (PerUser)" -Status 'Pass' -Message "DSC module found in user profile: $userModulesPath"
+ }
+ else {
+ Add-CheckResult -Category "DSC Module" -CheckName "DSC Module (PerUser)" -Status 'Fail' -Message "DSC module not found in user profile: $userModulesPath"
+ }
+ }
+ else {
+ # For per-machine installations, DSC module is installed to system WindowsPowerShell modules
+ $systemModulesPath = "${env:ProgramFiles}\WindowsPowerShell\Modules\Microsoft.PowerToys.Configure"
+ if (Test-Path $systemModulesPath) {
+ Add-CheckResult -Category "DSC Module" -CheckName "DSC Module (PerMachine)" -Status 'Pass' -Message "DSC module found in system modules: $systemModulesPath"
+ }
+ else {
+ Add-CheckResult -Category "DSC Module" -CheckName "DSC Module (PerMachine)" -Status 'Fail' -Message "DSC module not found in system modules: $systemModulesPath"
+ }
+ }
+}
+
+function Test-CommandPalettePackages {
+ param(
+ [string]$InstallPath
+ )
+
+ $cmdPalPath = Join-Path $InstallPath "WinUI3Apps\CmdPal"
+ if (Test-Path $cmdPalPath) {
+ # Check for MSIX package file (the actual Command Palette installation)
+ $msixFiles = Get-ChildItem $cmdPalPath -Filter "*.msix" -ErrorAction SilentlyContinue
+ if ($msixFiles) {
+ Add-CheckResult -Category "Command Palette" -CheckName "CmdPal MSIX Package" -Status 'Pass' -Message "Found $($msixFiles.Count) Command Palette MSIX package(s)"
+ }
+ else {
+ Add-CheckResult -Category "Command Palette" -CheckName "CmdPal MSIX Package" -Status 'Warning' -Message "No Command Palette MSIX packages found"
+ }
+ }
+ else {
+ Add-CheckResult -Category "Command Palette" -CheckName "CmdPal Module" -Status 'Warning' -Message "Command Palette module not found at: $cmdPalPath"
+ }
+}
+
+function Test-ContextMenuPackages {
+ param(
+ [string]$InstallPath
+ )
+
+ # Context menu packages are installed as sparse packages
+ # These MSIX packages should be present in the installation
+ $contextMenuPackages = @{
+ "ImageResizerContextMenuPackage.msix" = @{ Name = "Image Resizer context menu package"; Location = "Root" }
+ "FileLocksmithContextMenuPackage.msix" = @{ Name = "File Locksmith context menu package"; Location = "WinUI3Apps" }
+ "PowerRenameContextMenuPackage.msix" = @{ Name = "PowerRename context menu package"; Location = "WinUI3Apps" }
+ "NewPlusPackage.msix" = @{ Name = "New+ context menu package"; Location = "WinUI3Apps" }
+ }
+
+ # Check for packages based on their expected location
+ foreach ($packageFile in $contextMenuPackages.Keys) {
+ $packageInfo = $contextMenuPackages[$packageFile]
+
+ if ($packageInfo.Location -eq "Root") {
+ $packagePath = Join-Path $InstallPath $packageFile
+ }
+ else {
+ $packagePath = Join-Path $InstallPath "WinUI3Apps\$packageFile"
+ }
+
+ if (Test-Path $packagePath) {
+ Add-CheckResult -Category "Context Menu Packages" -CheckName $packageInfo.Name -Status 'Pass' -Message "Context menu package found: $packagePath"
+ }
+ else {
+ Add-CheckResult -Category "Context Menu Packages" -CheckName $packageInfo.Name -Status 'Fail' -Message "Context menu package not found: $packagePath"
+ }
+ }
+}
+
+# Main execution
+function Main {
+ Write-StatusMessage "Starting PowerToys Installation Verification" -Level Info
+ Write-StatusMessage "Scope: $InstallScope" -Level Info
+
+ # Check the specified scope - no fallbacks, only what installer should create
+ $installationFound = $false
+
+ if ($InstallScope -eq 'PerMachine') {
+ if (Test-PowerToysInstallation -Scope 'PerMachine') {
+ $installationFound = $true
+ Test-RegistryHandlers -Scope 'PerMachine'
+ Test-DSCModule -Scope 'PerMachine'
+ $installPath = Get-PowerToysInstallPath -Scope 'PerMachine'
+ if ($installPath) {
+ Test-CommandPalettePackages -InstallPath $installPath
+ Test-ContextMenuPackages -InstallPath $installPath
+ }
+ }
+ }
+ else { # PerUser
+ if (Test-PowerToysInstallation -Scope 'PerUser') {
+ $installationFound = $true
+ Test-RegistryHandlers -Scope 'PerUser'
+ Test-DSCModule -Scope 'PerUser'
+ $installPath = Get-PowerToysInstallPath -Scope 'PerUser'
+ if ($installPath) {
+ Test-CommandPalettePackages -InstallPath $installPath
+ Test-ContextMenuPackages -InstallPath $installPath
+ }
+ }
+ }
+
+ if ($installationFound) {
+ # Common tests (only run if installation found)
+ # Note: Scheduled tasks are not created by installer, they're created at runtime
+ }
+
+ # Calculate overall status
+ if ($script:Results.Summary.FailedChecks -eq 0) {
+ if ($script:Results.Summary.WarningChecks -eq 0) {
+ $script:Results.Summary.OverallStatus = "Healthy"
+ }
+ else {
+ $script:Results.Summary.OverallStatus = "Healthy with Warnings"
+ }
+ }
+ else {
+ $script:Results.Summary.OverallStatus = "Issues Detected"
+ }
+
+ # Display summary
+ Write-Host "`n" -NoNewline
+ Write-StatusMessage "=== VERIFICATION SUMMARY ===" -Level Info
+ Write-StatusMessage "Total Checks: $($script:Results.Summary.TotalChecks)" -Level Info
+ Write-StatusMessage "Passed: $($script:Results.Summary.PassedChecks)" -Level Success
+ Write-StatusMessage "Failed: $($script:Results.Summary.FailedChecks)" -Level Error
+ Write-StatusMessage "Warnings: $($script:Results.Summary.WarningChecks)" -Level Warning
+ Write-StatusMessage "Overall Status: $($script:Results.Summary.OverallStatus)" -Level $(
+ switch ($script:Results.Summary.OverallStatus) {
+ 'Healthy' { 'Success' }
+ 'Healthy with Warnings' { 'Warning' }
+ default { 'Error' }
+ }
+ )
+
+ Write-StatusMessage "PowerToys Installation Verification Complete" -Level Info
+}
+
+# Run the main function
+Main
diff --git a/tools/build/build-installer.ps1 b/tools/build/build-installer.ps1
index f185ddfb0f..a1a1f8d5f1 100644
--- a/tools/build/build-installer.ps1
+++ b/tools/build/build-installer.ps1
@@ -7,7 +7,7 @@ This script automates the end-to-end build and packaging process for PowerToys,
- Restoring and building all necessary solutions (CmdPal, BugReportTool, etc.)
- Cleaning up old output
- Signing generated .msix packages
-- Building the WiX-based MSI and bootstrapper installers
+- Building the WiX v5 (VNext) MSI and bootstrapper installers
It is designed to work in local development.
The cert used to sign the packages is generated by
@@ -21,9 +21,12 @@ Specifies the build configuration (e.g., 'Debug', 'Release'). Default is 'Releas
.PARAMETER PerUser
Specifies whether to build a per-user installer (true) or machine-wide installer (false). Default is true (per-user).
+.PARAMETER InstallerSuffix
+Specifies the suffix for the installer naming (e.g., 'wix5', 'vnext'). Default is 'wix5'.
+
.EXAMPLE
.\build-installer.ps1
-Runs the installer build pipeline for ARM64 Release (default).
+Runs the installer build pipeline for x64 Release with default suffix (wix5).
.EXAMPLE
.\build-installer.ps1 -Platform x64 -Configuration Release
@@ -33,12 +36,16 @@ Runs the pipeline for x64 Release.
.\build-installer.ps1 -Platform x64 -Configuration Release -PerUser false
Runs the pipeline for x64 Release with machine-wide installer.
+.EXAMPLE
+.\build-installer.ps1 -Platform x64 -Configuration Release -InstallerSuffix vnext
+Runs the pipeline for x64 Release with 'vnext' suffix.
+
.NOTES
- Make sure to run this script from a Developer PowerShell (e.g., VS2022 Developer PowerShell).
- Generated MSIX files will be signed using cert-sign-package.ps1.
- This script will clean previous outputs under the build directories and installer directory (except *.exe files).
- First time run need admin permission to trust the certificate.
-- The built installer will be placed under: installer/PowerToysSetup/[Platform]/[Configuration]/User[Machine]Setup
+- The built installer will be placed under: installer/PowerToysSetupVNext/[Platform]/[Configuration]/User[Machine]Setup
relative to the solution root directory.
- To run the full installation in other machines, call "./cert-management.ps1" to export the cert used to sign the packages.
And trust the cert in the target machine.
@@ -47,7 +54,8 @@ Runs the pipeline for x64 Release with machine-wide installer.
param (
[string]$Platform = 'x64',
[string]$Configuration = 'Release',
- [string]$PerUser = 'true'
+ [string]$PerUser = 'true',
+ [string]$InstallerSuffix = 'wix5'
)
# Find the PowerToys repository root automatically
@@ -114,8 +122,7 @@ function RestoreThenBuild {
RunMSBuild $Solution '/m'
}
-Write-Host ("Make sure wix is installed and available")
-& (Join-Path $PSScriptRoot "ensure-wix.ps1")
+# WiX v5 projects use WixToolset.Sdk via NuGet/MSBuild; a separate WiX 3 installation is not required here.
Write-Host ("[PIPELINE] Start | Platform={0} Configuration={1} PerUser={2}" -f $Platform, $Configuration, $PerUser)
Write-Host ''
@@ -154,8 +161,8 @@ try {
RunMSBuild 'installer\PowerToysSetup.sln' '/t:restore /p:RestorePackagesConfig=true'
-RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysInstaller /p:PerUser=$PerUser"
+RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysInstallerVNext /p:PerUser=$PerUser /p:InstallerSuffix=$InstallerSuffix"
-RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysBootstrapper /p:PerUser=$PerUser"
+RunMSBuild 'installer\PowerToysSetup.sln' "/m /t:PowerToysBootstrapperVNext /p:PerUser=$PerUser /p:InstallerSuffix=$InstallerSuffix"
Write-Host '[PIPELINE] Completed'