diff --git a/Directory.Build.targets b/Directory.Build.targets index 9efab5a9a585..c9fac8a05b63 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -65,4 +65,19 @@ + + + + + <_UnusedVCRuntimeDlls Include="$(OutDir)mfc140*.dll" /> + <_UnusedVCRuntimeDlls Include="$(OutDir)mfcm140*.dll" /> + <_UnusedVCRuntimeDlls Include="$(OutDir)vcamp140*.dll" /> + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index d083f672a1e8..c0a1d15e9ef1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -111,8 +111,7 @@ - - + diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp index 39786a16d886..75a3dceba50f 100644 --- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "../../src/common/logger/logger.h" @@ -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) { diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.def b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def index 86efe34aa6b9..1b8b7d55f8a4 100644 --- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.def +++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def @@ -36,3 +36,5 @@ EXPORTS SetBundleInstallLocationCA InstallPackageIdentityMSIXCA UninstallPackageIdentityMSIXCA + CreateWinAppSDKHardlinksCA + DeleteWinAppSDKHardlinksCA diff --git a/installer/PowerToysSetupVNext/Product.wxs b/installer/PowerToysSetupVNext/Product.wxs index 584d61c4497f..0086709578ae 100644 --- a/installer/PowerToysSetupVNext/Product.wxs +++ b/installer/PowerToysSetupVNext/Product.wxs @@ -111,6 +111,8 @@ + + @@ -123,6 +125,7 @@ + @@ -136,6 +139,7 @@ + @@ -188,8 +192,10 @@ + + diff --git a/installer/PowerToysSetupVNext/WinUI3Applications.wxs b/installer/PowerToysSetupVNext/WinUI3Applications.wxs index 4c177b960a76..c0d3afb6f4ca 100644 --- a/installer/PowerToysSetupVNext/WinUI3Applications.wxs +++ b/installer/PowerToysSetupVNext/WinUI3Applications.wxs @@ -7,11 +7,18 @@ + + + + + + + diff --git a/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 b/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 index 81445753690c..76e37020d4c6 100644 --- a/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 +++ b/installer/PowerToysSetupVNext/generateAllFileComponents.ps1 @@ -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) { @@ -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" @@ -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 entries + $winui3Wxs = $winui3Wxs -replace "\<\?define WinUI3ApplicationsFiles=[^?]*\?\>", "" + } else { + $winui3Wxs = $winui3Wxs -replace "\<\?define WinUI3ApplicationsFiles=[^?]*\?\>", "" + } + 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 diff --git a/src/modules/MouseWithoutBorders/App/Class/Program.cs b/src/modules/MouseWithoutBorders/App/Class/Program.cs index 144007e92f56..129fba3ce31a 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Program.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Program.cs @@ -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; diff --git a/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj b/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj index 4b3fc7fd50af..edc46f1118c3 100644 --- a/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj +++ b/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj @@ -66,7 +66,7 @@ - + diff --git a/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj b/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj index 675e927334e9..cd038f25fabd 100644 --- a/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj +++ b/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj @@ -214,7 +214,7 @@ - + diff --git a/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj b/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj index 53af161e16a5..b8787ba55958 100644 --- a/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj +++ b/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj @@ -71,7 +71,7 @@ - +