Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,19 @@
<!-- Note: For C++ skipped test projects, build is effectively skipped by removing all compile items above.
We don't define empty Build/Rebuild/Clean targets here because MSBuild Target definitions with Condition
on the Target element still override the default targets even when condition is false. -->

<!-- Clean up unused VC++ runtime DLLs that CopyCppRuntimeToOutputDir copies from the full
VCRedist tree (MFC, C++ AMP, OpenMP). No PowerToys binary links against these — verified
with dumpbin /dependents across all installed binaries. -->
<Target Name="RemoveUnusedVCRuntimeDlls"
AfterTargets="Build"
Condition="'$(CopyCppRuntimeToOutputDir)' == 'true' and '$(MSBuildProjectExtension)' == '.vcxproj'">
<ItemGroup>
<_UnusedVCRuntimeDlls Include="$(OutDir)mfc140*.dll" />
<_UnusedVCRuntimeDlls Include="$(OutDir)mfcm140*.dll" />
<_UnusedVCRuntimeDlls Include="$(OutDir)vcamp140*.dll" />
</ItemGroup>
<Delete Files="@(_UnusedVCRuntimeDlls)" Condition="'@(_UnusedVCRuntimeDlls)' != ''" />
<Message Importance="normal" Text="Cleaned up unused VC runtime DLLs: @(_UnusedVCRuntimeDlls)" Condition="'@(_UnusedVCRuntimeDlls)' != ''" />
</Target>
</Project>
3 changes: 1 addition & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.10" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.10" />
<PackageVersion Include="System.Data.OleDb" Version="9.0.10" />
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
<PackageVersion Include="System.Data.SqlClient" Version="4.9.0" />

<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.10" />
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
Expand Down
121 changes: 121 additions & 0 deletions installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <ProjectTelemetry.h>
#include <spdlog/sinks/base_sink.h>
#include <filesystem>
#include <fstream>
#include <string_view>

#include "../../src/common/logger/logger.h"
Expand Down Expand Up @@ -1806,6 +1807,126 @@ void initSystemLogger()
} });
}

UINT __stdcall CreateWinAppSDKHardlinksCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
std::wstring installationFolder;

hr = WcaInitialize(hInstall, "CreateWinAppSDKHardlinks");
ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");

{
namespace fs = std::filesystem;
const fs::path installDir(installationFolder);
const fs::path winui3Dir = installDir / L"WinUI3Apps";
const fs::path manifestPath = winui3Dir / L"hardlinks.txt";

if (!fs::exists(manifestPath))
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: No hardlinks.txt manifest found, skipping.");
goto LExit;
}

std::wifstream manifestFile(manifestPath);
std::wstring fileName;
int created = 0;
int failed = 0;

while (std::getline(manifestFile, fileName))
{
if (fileName.empty())
{
continue;
}

const fs::path source = installDir / fileName;
const fs::path target = winui3Dir / fileName;

if (!fs::exists(source))
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Source not found: %ls", source.c_str());
failed++;
continue;
}

// Remove existing file if present (leftover from previous install)
std::error_code ec;
fs::remove(target, ec);

if (CreateHardLinkW(target.c_str(), source.c_str(), nullptr))
{
created++;
}
else
{
// Hard-link failed (e.g. cross-volume). Copy as fallback.
fs::copy_file(source, target, fs::copy_options::overwrite_existing, ec);
if (ec)
{
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Failed to link or copy: %ls", fileName.c_str());
failed++;
}
else
{
created++;
}
}
}

WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Created %d links, %d failures", created, failed);
}

LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}

UINT __stdcall DeleteWinAppSDKHardlinksCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
std::wstring installationFolder;

hr = WcaInitialize(hInstall, "DeleteWinAppSDKHardlinks");
ExitOnFailure(hr, "Failed to initialize");
hr = getInstallFolder(hInstall, installationFolder);
ExitOnFailure(hr, "Failed to get installFolder.");

{
namespace fs = std::filesystem;
const fs::path winui3Dir = fs::path(installationFolder) / L"WinUI3Apps";
const fs::path manifestPath = winui3Dir / L"hardlinks.txt";

if (!fs::exists(manifestPath))
{
goto LExit;
}

std::wifstream manifestFile(manifestPath);
std::wstring fileName;

while (std::getline(manifestFile, fileName))
{
if (fileName.empty())
{
continue;
}

std::error_code ec;
fs::remove(winui3Dir / fileName, ec);
}

WcaLog(LOGMSG_STANDARD, "DeleteWinAppSDKHardlinks: Cleaned up hard-linked files");
}

LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}

// DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
{
Expand Down
2 changes: 2 additions & 0 deletions installer/PowerToysSetupCustomActionsVNext/CustomAction.def
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ EXPORTS
SetBundleInstallLocationCA
InstallPackageIdentityMSIXCA
UninstallPackageIdentityMSIXCA
CreateWinAppSDKHardlinksCA
DeleteWinAppSDKHardlinksCA
6 changes: 6 additions & 0 deletions installer/PowerToysSetupVNext/Product.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@
<Custom Action="SetInstallCmdPalPackageParam" Before="InstallCmdPalPackage" />
<Custom Action="SetUninstallCommandNotFoundParam" Before="UninstallCommandNotFound" />
<Custom Action="SetUpgradeCommandNotFoundParam" Before="UpgradeCommandNotFound" />
<Custom Action="SetCreateWinAppSDKHardlinksParam" Before="CreateWinAppSDKHardlinks" />
<Custom Action="SetDeleteWinAppSDKHardlinksParam" Before="DeleteWinAppSDKHardlinks" />
<Custom Action="SetApplyModulesRegistryChangeSetsParam" Before="ApplyModulesRegistryChangeSets" />
<Custom Action="SetInstallPackageIdentityMSIXParam" Before="InstallPackageIdentityMSIX" />

Expand All @@ -123,6 +125,7 @@
<Custom Action="SetBundleInstallLocationData" Before="SetBundleInstallLocation" Condition="NOT Installed OR WIX_UPGRADE_DETECTED" />
<Custom Action="SetBundleInstallLocation" After="InstallFiles" Condition="NOT Installed OR WIX_UPGRADE_DETECTED" />
<Custom Action="ApplyModulesRegistryChangeSets" After="InstallFiles" Condition="NOT Installed" />
<Custom Action="CreateWinAppSDKHardlinks" After="InstallFiles" Condition="NOT Installed OR WIX_UPGRADE_DETECTED" />
<Custom Action="InstallCmdPalPackage" After="InstallFiles" Condition="NOT Installed" />
<Custom Action="InstallPackageIdentityMSIX" After="InstallFiles" Condition="NOT Installed AND WINDOWSBUILDNUMBER &gt;= 22000" />
<Custom Action="override Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Before="RemoveFiles" />
Expand All @@ -136,6 +139,7 @@
<?endif?>
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize" Condition="NOT Installed" />
<Custom Action="TelemetryLogUninstallSuccess" After="InstallFinalize" Condition="Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="DeleteWinAppSDKHardlinks" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="UnApplyModulesRegistryChangeSets" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
<Custom Action="CleanImageResizerRuntimeRegistry" Before="RemoveFiles" Condition="Installed AND (REMOVE=&quot;ALL&quot;)" />
Expand Down Expand Up @@ -188,8 +192,10 @@
<CustomAction Id="SetUpgradeCommandNotFoundParam" Property="UpgradeCommandNotFound" Value="[INSTALLFOLDER]" />

<CustomAction Id="SetCreateWinAppSDKHardlinksParam" Property="CreateWinAppSDKHardlinks" Value="[INSTALLFOLDER]" />
<CustomAction Id="CreateWinAppSDKHardlinks" Return="ignore" Impersonate="yes" Execute="deferred" DllEntry="CreateWinAppSDKHardlinksCA" BinaryRef="PTCustomActions" />

<CustomAction Id="SetDeleteWinAppSDKHardlinksParam" Property="DeleteWinAppSDKHardlinks" Value="[INSTALLFOLDER]" />
<CustomAction Id="DeleteWinAppSDKHardlinks" Return="ignore" Impersonate="yes" Execute="deferred" DllEntry="DeleteWinAppSDKHardlinksCA" BinaryRef="PTCustomActions" />

<CustomAction Id="SetCreatePTInteropHardlinksParam" Property="CreatePTInteropHardlinks" Value="[INSTALLFOLDER]" />

Expand Down
7 changes: 7 additions & 0 deletions installer/PowerToysSetupVNext/WinUI3Applications.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@

<Fragment>
<DirectoryRef Id="WinUI3AppsInstallFolder">
<Component Id="WinUI3Apps_Hardlinks_Manifest" Guid="F7A2C3D1-8E4B-4F6A-9D2E-1B3C5A7F8E90" Bitness="always64">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="WinUI3Apps_Hardlinks_Manifest" Value="" KeyPath="yes" />
</RegistryKey>
<File Id="WinUI3Apps_hardlinks_txt" Source="$(var.BinDir)\WinUI3Apps\hardlinks.txt" />
</Component>
<!-- Generated by generateFileComponents.ps1 -->
<!--WinUI3ApplicationsFiles_Component_Def-->
</DirectoryRef>

<ComponentGroup Id="WinUI3ApplicationsComponentGroup">
<ComponentRef Id="WinUI3Apps_Hardlinks_Manifest" />
</ComponentGroup>

</Fragment>
Expand Down
54 changes: 53 additions & 1 deletion installer/PowerToysSetupVNext/generateAllFileComponents.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Function Generate-FileList() {

$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri")

# MFC DLLs leak into the output via WindowsAppSDKSelfContained but no PowerToys binary imports them.
# Verified with dumpbin /dependents across all 2176 binaries — zero consumers.
$fileExclusionList += @("mfc140.dll", "mfc140u.dll", "mfcm140.dll", "mfcm140u.dll")

$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")

if ($fileDepsJson -eq [string]::Empty) {
Expand Down Expand Up @@ -85,11 +89,16 @@ Function Generate-FileComponents() {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList',
Justification = 'variable is used in another scope')]

$fileList = $matches[2] -split ';'
$fileList = $matches[2] -split ';' | Where-Object { $_ -ne '' }
return
}
}

if ($null -eq $fileList -or $fileList.Count -eq 0) {
# No files to generate components for — leave placeholder intact
return
}

$componentId = "$($fileListName)_Component"

$componentDefs = "`r`n"
Expand Down Expand Up @@ -154,6 +163,49 @@ Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSSc

#WinUI3Applications
Generate-FileList -fileDepsJson "" -fileListName WinUI3ApplicationsFiles -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"

# Deduplicate: Remove files from WinUI3Apps that are identical to root (same name + same hash).
# These will be created as hard-links at install time by CreateWinAppSDKHardlinksCA.
$rootPath = "$PSScriptRoot..\..\..\$platform\Release"
$winui3Path = "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"
$winui3WxsPath = "$PSScriptRoot\WinUI3Applications.wxs"
$winui3Wxs = Get-Content $winui3WxsPath -Raw
$manifestPath = Join-Path $winui3Path "hardlinks.txt"

if ($winui3Wxs -match "\<\?define WinUI3ApplicationsFiles=([^?]*)\?\>") {
$winui3FileList = $matches[1] -split ';' | Where-Object { $_ -ne '' }
$hardlinkFiles = @()

foreach ($file in $winui3FileList) {
$rootFile = Join-Path $rootPath $file
$winui3File = Join-Path $winui3Path $file
if ((Test-Path $rootFile) -and (Test-Path $winui3File)) {
$rootHash = (Get-FileHash $rootFile -Algorithm SHA256).Hash
$winui3Hash = (Get-FileHash $winui3File -Algorithm SHA256).Hash
if ($rootHash -eq $winui3Hash) {
$hardlinkFiles += $file
}
}
}

if ($hardlinkFiles.Count -gt 0) {
# Remove deduplicated files from WinUI3Apps file list
$remainingFiles = $winui3FileList | Where-Object { $_ -notin $hardlinkFiles }
if ($remainingFiles.Count -eq 0) {
# All files are duplicates — keep at least a dummy entry won't be emitted
# Generate-FileComponents handles empty defines by producing no <File> entries
$winui3Wxs = $winui3Wxs -replace "\<\?define WinUI3ApplicationsFiles=[^?]*\?\>", "<?define WinUI3ApplicationsFiles=?>"
} else {
$winui3Wxs = $winui3Wxs -replace "\<\?define WinUI3ApplicationsFiles=[^?]*\?\>", "<?define WinUI3ApplicationsFiles=$($remainingFiles -join ';')?>"
}
Set-Content -Path $winui3WxsPath -Value $winui3Wxs
Write-Host "Deduplicated $($hardlinkFiles.Count) files from WinUI3Apps (will be hard-linked at install time)"
}

# Always write hardlinks.txt (may be empty — CA handles that gracefully)
$hardlinkFiles | Set-Content -Path $manifestPath
}

Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs

#AdvancedPaste
Expand Down
1 change: 0 additions & 1 deletion src/modules/MouseWithoutBorders/App/Class/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
using System.Linq;
using System.Security.Authentication.ExtendedProtection;
using System.Security.Principal;
using System.ServiceModel.Channels;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="Microsoft.Windows.Compatibility" />
<PackageReference Include="StreamJsonRpc" />
<PackageReference Include="System.Data.SqlClient" /> <!-- It's a dependency of Microsoft.Windows.Compatibility. We're adding it here to force it to the version specified in Directory.Packages.props -->

</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="Microsoft.Windows.Compatibility" />
<PackageReference Include="StreamJsonRpc" />
<PackageReference Include="System.Data.SqlClient" /> <!-- It's a dependency of Microsoft.Windows.Compatibility. We're adding it here to force it to the version specified in Directory.Packages.props -->

</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" />
<PackageReference Include="StreamJsonRpc" />
<PackageReference Include="System.Data.SqlClient" /> <!-- It's a dependency of Microsoft.Windows.Compatibility. We're adding it here to force it to the version specified in Directory.Packages.props -->

<!-- HACK: To make sure the version pulled in by Microsoft.Extensions.Hosting is current. -->
<PackageReference Include="System.Text.Json" />
</ItemGroup>
Expand Down
Loading