mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-08-29 10:09:43 +02:00
Remove unused dependencies and shrink installer size (#47233)
## Summary of the Pull Request Two related installer changes to (1) eliminate genuinely-unused dependencies and (2) deduplicate shared WinAppSDK files between `<install>\` and `<install>\WinUI3Apps\` to shrink the installer download. ### 1. Remove unused dependencies (~11 MB savings per output location) - **System.Data.SqlClient**: Removed from MouseWithoutBorders projects and the central `Directory.Packages.props` pin. It was a transitive dependency of `Microsoft.Windows.Compatibility` but PowerToys has zero SQL database usage. - **Unused `using` import**: Removed `using System.ServiceModel.Channels` from MouseWithoutBorders `Program.cs` (no WCF usage). - **MFC / C++ AMP / OpenMP DLLs**: Added `RemoveUnusedVCRuntimeDlls` target in `Directory.Build.targets` to clean up `mfc140*`, `mfcm140*`, `vcamp140*`, and `vcomp140*` DLLs that leak from the VC++ Redistributable tree but are not imported by any PowerToys binary (verified with `dumpbin /dependents` across all installed binaries). Also excluded MFC DLLs from installer file collection. ### 2. WinAppSDK file deduplication (build-time only; install-time uses copy) **Background**: The `WinUI3Apps` subfolder must remain a real directory because MSIX sparse package registration applies DACL changes to the `ExternalLocation` folder (PR #47177). Flattening is not viable. **Build-time** (`generateAllFileComponents.ps1`): computes the SHA256 intersection of root and `WinUI3Apps` files, and for each file that is also present in the BaseApplications WXS file list, removes the duplicate from the WinUI3Apps WXS component list and writes its name to a `hardlinks.txt` manifest. The BaseApplications cross-check ensures we never deduplicate a file the MSI does not actually deploy at the install root, which would otherwise leave both copies missing post-install. The manifest is written as UTF-8 without BOM (via `[System.IO.File]::WriteAllLines` with `UTF8Encoding($false)`) so its encoding is identical regardless of the build host's PowerShell version. This step produces the **MSI download-size win** (~97 MB smaller cab; LZX:21 was already deduplicating most byte-identical content automatically inside the cab). **Install-time** (`CreateWinAppSDKHardlinksCA` custom action): - Reads `hardlinks.txt` after `InstallFiles` as a raw byte stream and converts each line to a `std::wstring` via `MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, ...)`. Avoids `std::wifstream`'s ANSI-codepage codecvt so non-ASCII paths can never be silently mangled. - For each entry, computes `(installDir / fileName).lexically_normal()` and `(winui3Dir / fileName).lexically_normal()`, then verifies via `std::mismatch` that each resolved path is still rooted at its respective folder. Manifest entries containing `..`, absolute paths, or alternate-stream syntax are logged and skipped. - Materialises each validated entry from `<install>\<name>` into `<install>\WinUI3Apps\<name>` via `fs::copy_file` (overwrite_existing). - Reports the per-file copy / failure counts to the install log. If every entry failed (`created == 0 && failed > 0`), the CA escalates to `E_FAIL` so the install does not silently succeed with an unusable WinUI3Apps tree. `DeleteWinAppSDKHardlinksCA` removes the materialised copies before `RemoveFiles` on uninstall, using the same UTF-8 reader and per-entry containment check. **WiX sequencing**: `CreateWinAppSDKHardlinks` runs `After="InstallFiles"` with `Condition="NOT Installed OR WIX_UPGRADE_DETECTED OR REINSTALL"` so a `msiexec /fa` repair refreshes the deduplicated copies (otherwise `RemoveFiles` would orphan them). #### Why copy and not hard-link A hard-linked variant of this CA was originally proposed but caused a Monaco preview-handler regression. Hard-links share an NTFS inode (and therefore one DACL) between `<install>\<file>` and `<install>\WinUI3Apps\<file>`. The MSIX sparse-package registrations for PowerRename / ImageResizer / FileLocksmith / NewPlus run after the dedup CA and propagate the `WinUI3Apps` parent's rich DACL (Capability SID, 5× Package SIDs, 5× conditional SYSAPPID ACE, RC SID) onto the shared inode. The root path then also exposes the rich DACL, which trips a kernel "stricter access evaluation" path that blocks the LOW-IL `prevhost.exe` from `LoadLibrary`-ing `hostfxr.dll` (and the rest of the .NET runtime), turning the Monaco preview pane blank for `.json` / `.md` / `.cs` / `.xaml` / `.svg` / `.xml` files. `fs::copy_file` creates a **fresh inode** for the WinUI3Apps copy. The root inode keeps its simple DACL (`SY:F + BA:F + owner:F` + inherited `BU:RX`) so LOW-IL `prevhost.exe` can still load it — Monaco preview works. The WinUI3Apps copy inherits the WinUI3Apps parent's rich DACL via normal NTFS inheritance (matches 0.99.1 behaviour exactly) — MSIX context-menu shells continue to work. #### Trade-off | Metric | Hard-link variant (rejected) | This PR (file copy) | 0.99.1 (no dedup) | |---|---|---|---| | MSI size | ~296 MB | ~296 MB | ~393 MB | | On-disk after install | ~2,475 MB | ~2,772 MB | ~2,772 MB | | DACL contamination risk | YES (broke Monaco) | NO | NO | The on-disk savings (~297 MB) are given up in exchange for eliminating the DACL contamination risk; the **installer download savings (~97 MB)** are preserved by the build-time WiX/cab dedup. #### Edge cases handled - Empty duplicate list: `hardlinks.txt` always written, CA handles empty. - All files duplicated: `Generate-FileComponents` returns early for empty list. - File stripped from BaseApplications by an earlier build step: BaseApplications cross-check skips it during dedup so neither copy goes missing. - Manifest entry escapes install root (`..`, absolute path): rejected per-entry, install continues. - Manifest line is non-UTF-8: rejected per-entry, install continues. - Source missing at install time: per-entry skip, install continues. - All copies fail: install aborts loudly via `E_FAIL` (catastrophic-case escalation). - Upgrade or `msiexec /fa` repair: CA fires (`NOT Installed OR WIX_UPGRADE_DETECTED OR REINSTALL`). **MSI repair risk**: Burn bundle uses `SuppressRepair=yes` and `MajorUpgrade` (full uninstall + reinstall) for all version upgrades, so the standard upgrade path is unaffected. The `OR REINSTALL` clause covers power users running `msiexec /fa` directly. ## PR Checklist - [x] **Communication:** Discussed approach via PRs #46866, #47177, #46745 - [ ] **Tests:** Installer infrastructure only — no runtime behaviour changes - [ ] **Localization:** N/A - [ ] **Dev docs:** N/A - [ ] **New binaries:** N/A ## Detailed Description of the Pull Request / Additional comments Based on the approach from PR #46745 by @yeelam-gordon, rebased onto latest main and switched from hard-links to file copies after the DACL contamination root cause was identified. Hardening (UTF-8 read, path containment, catastrophic-case escalation, REINSTALL repair, BaseApplications-filtered dedup) added in response to review feedback. These changes are purely build/installer infrastructure — no runtime behaviour changes to any PowerToys module. ## Validation Steps Performed Validated on a 0.99.4 / 0.99.5 local install (per-user `%LocalAppData%\PowerToys`): - ✅ `dumpbin /dependents` across the installed PowerToys tree confirmed zero binaries import `mfc140*`, `mfcm140*`, `vcamp140*`, or `vcomp140*` — the cleanup target removes ~11 MB of genuinely unused VC runtime DLLs. - ✅ `System.Data.SqlClient` has zero call-sites in PowerToys source. - ✅ Local installer build produces a 296 MB MSI (down from 393 MB pre-dedup, ~97 MB cab savings purely from the build-time WiX dedup). - ✅ MSI table inspection (`wix msi decompile`) confirms the deferred CAs are present (`CreateWinAppSDKHardlinks`, `DeleteWinAppSDKHardlinks`) and the `hardlinks.txt` File row is registered. - ✅ MSI table inspection confirms .NET runtime DLLs (`hostfxr.dll`, `coreclr.dll`, `hostpolicy.dll`, `clretwrc.dll`, `Accessibility.dll`, `backup_restore_settings.json`) appear ONLY in `BaseApplicationsFiles_File_*`, NOT in `WinUI3ApplicationsFiles_File_*` — proving the build-time dedup worked. - ✅ Post-install verification: deduplicated files materialised at both root and WinUI3Apps with byte-identical SHA256 hashes, and `fsutil hardlink list` returns link-count == 1 for each — proving the install-time copy approach worked, not hard-link. - ✅ DACL on root .NET runtime DLLs is clean: no Package SID, no Capability SID, no SYSAPPID conditional ACE, no `ALL APPLICATION PACKAGES` ACE — Monaco preview load path is safe. - ✅ DACL on WinUI3Apps copies has the rich MSIX inheritance — context-menu shells continue to work (matches 0.99.1). - ✅ All four MSIX sparse packages (PowerRename, ImageResizer, FileLocksmith, NewPlus) registered after install. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
3
.github/actions/spell-check/expect.txt
vendored
3
.github/actions/spell-check/expect.txt
vendored
@@ -440,6 +440,7 @@ DString
|
|||||||
DSVG
|
DSVG
|
||||||
dto
|
dto
|
||||||
DUMMYUNIONNAME
|
DUMMYUNIONNAME
|
||||||
|
dumpbin
|
||||||
dutil
|
dutil
|
||||||
DVASPECT
|
DVASPECT
|
||||||
DVASPECTINFO
|
DVASPECTINFO
|
||||||
@@ -710,6 +711,7 @@ HOOKPROC
|
|||||||
HORZRES
|
HORZRES
|
||||||
HORZSIZE
|
HORZSIZE
|
||||||
Hostbackdropbrush
|
Hostbackdropbrush
|
||||||
|
hostfxr
|
||||||
hostsfileeditor
|
hostsfileeditor
|
||||||
hotfixes
|
hotfixes
|
||||||
hotkeycontrol
|
hotkeycontrol
|
||||||
@@ -1022,6 +1024,7 @@ Metadatas
|
|||||||
metafile
|
metafile
|
||||||
metapackage
|
metapackage
|
||||||
mfc
|
mfc
|
||||||
|
mfcm
|
||||||
Mgmt
|
Mgmt
|
||||||
Microwaved
|
Microwaved
|
||||||
middleclickaction
|
middleclickaction
|
||||||
|
|||||||
@@ -65,4 +65,20 @@
|
|||||||
<!-- Note: For C++ skipped test projects, build is effectively skipped by removing all compile items above.
|
<!-- 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
|
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. -->
|
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" />
|
||||||
|
<_UnusedVCRuntimeDlls Include="$(OutDir)vcomp140*.dll" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Delete Files="@(_UnusedVCRuntimeDlls)" Condition="'@(_UnusedVCRuntimeDlls)' != ''" />
|
||||||
|
<Message Importance="normal" Text="Cleaned up unused VC runtime DLLs: @(_UnusedVCRuntimeDlls)" Condition="'@(_UnusedVCRuntimeDlls)' != ''" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -110,8 +110,6 @@
|
|||||||
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.7" />
|
<PackageVersion Include="System.ComponentModel.Composition" Version="10.0.7" />
|
||||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="10.0.7" />
|
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="10.0.7" />
|
||||||
<PackageVersion Include="System.Data.OleDb" Version="10.0.7" />
|
<PackageVersion Include="System.Data.OleDb" Version="10.0.7" />
|
||||||
<!-- 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.1" />
|
|
||||||
<!-- 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. -->
|
<!-- 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="10.0.7" />
|
<PackageVersion Include="System.Diagnostics.EventLog" Version="10.0.7" />
|
||||||
<!-- 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. -->
|
<!-- 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. -->
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <ProjectTelemetry.h>
|
#include <ProjectTelemetry.h>
|
||||||
#include <spdlog/sinks/base_sink.h>
|
#include <spdlog/sinks/base_sink.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "../../src/common/logger/logger.h"
|
#include "../../src/common/logger/logger.h"
|
||||||
@@ -1807,6 +1808,223 @@ void initSystemLogger()
|
|||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Naming note: the *Hardlinks* names in this CA, the matching WiX CustomAction Ids
|
||||||
|
// in Product.wxs, and the manifest filename "hardlinks.txt" are kept for continuity
|
||||||
|
// with the original PR design. The implementation uses fs::copy_file -- not
|
||||||
|
// CreateHardLinkW -- because hard-links share an inode (and DACL) between root and
|
||||||
|
// WinUI3Apps, which lets MSIX sparse-package registration propagate a rich DACL onto
|
||||||
|
// the root copy of files like hostfxr.dll and break LOW-IL prevhost.exe loads,
|
||||||
|
// turning the Monaco preview pane blank. Copies create a fresh inode in WinUI3Apps so the root
|
||||||
|
// copy keeps its simple DACL. See the in-body comment for the full RCA reference.
|
||||||
|
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::ifstream manifestFile(manifestPath); // Read as bytes, then convert UTF-8 -> wide explicitly.
|
||||||
|
std::string narrowLine;
|
||||||
|
int created = 0;
|
||||||
|
int failed = 0;
|
||||||
|
|
||||||
|
// INSTALLFOLDER from MSI typically arrives with a trailing backslash. lexically_normal
|
||||||
|
// preserves that as an empty trailing path component, which would later make the
|
||||||
|
// per-component std::mismatch containment check below reject every legitimate entry.
|
||||||
|
// Strip any trailing separators before normalizing.
|
||||||
|
auto stripTrailingSep = [](fs::path p) {
|
||||||
|
auto s = p.native();
|
||||||
|
while (s.size() > 1 && (s.back() == L'\\' || s.back() == L'/')) s.pop_back();
|
||||||
|
return fs::path(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalize once so the per-line containment check below is cheap.
|
||||||
|
const fs::path installDirNorm = stripTrailingSep(installDir).lexically_normal();
|
||||||
|
const fs::path winui3DirNorm = stripTrailingSep(winui3Dir).lexically_normal();
|
||||||
|
|
||||||
|
while (std::getline(manifestFile, narrowLine))
|
||||||
|
{
|
||||||
|
if (narrowLine.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Strip CR if the manifest uses CRLF line endings.
|
||||||
|
if (narrowLine.back() == '\r')
|
||||||
|
{
|
||||||
|
narrowLine.pop_back();
|
||||||
|
if (narrowLine.empty()) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest is written as UTF-8 (no BOM) -- convert to wide string explicitly
|
||||||
|
// rather than relying on the locale-default codecvt of std::wifstream, which is
|
||||||
|
// the ANSI code page on Windows and would silently mangle any non-ASCII path.
|
||||||
|
const int wideLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, nullptr, 0);
|
||||||
|
if (wideLen <= 0)
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Skipping non-UTF-8 entry: %hs", narrowLine.c_str());
|
||||||
|
failed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::wstring fileName(static_cast<size_t>(wideLen) - 1, L'\0');
|
||||||
|
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, fileName.data(), wideLen);
|
||||||
|
|
||||||
|
// Defense-in-depth: reject manifest entries that would escape the install root
|
||||||
|
// via "..", absolute paths, or alternate stream syntax. lexically_normal collapses
|
||||||
|
// any "." / ".." / repeated separators, then std::mismatch verifies the resolved
|
||||||
|
// path is still rooted at installDir / winui3Dir respectively.
|
||||||
|
const fs::path source = (installDir / fileName).lexically_normal();
|
||||||
|
const fs::path target = (winui3Dir / fileName).lexically_normal();
|
||||||
|
const auto sourceIn = std::mismatch(installDirNorm.begin(), installDirNorm.end(), source.begin(), source.end());
|
||||||
|
const auto targetIn = std::mismatch(winui3DirNorm.begin(), winui3DirNorm.end(), target.begin(), target.end());
|
||||||
|
if (sourceIn.first != installDirNorm.end() || targetIn.first != winui3DirNorm.end())
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Rejecting entry outside install root: %ls", fileName.c_str());
|
||||||
|
failed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Use a regular file copy (not a hard-link). Hard-links share an
|
||||||
|
// NTFS inode -- and therefore one DACL -- between root and
|
||||||
|
// WinUI3Apps, which lets MSIX sparse-package registration
|
||||||
|
// propagate the WinUI3Apps parent's rich (Capability/Package SID)
|
||||||
|
// DACL onto the root path. That trips a kernel "stricter access
|
||||||
|
// evaluation" path that blocks LOW-IL prevhost.exe from loading
|
||||||
|
// hostfxr.dll, so File Explorer Monaco preview goes blank on
|
||||||
|
// Windows 11 23H2. Copying creates a fresh inode in WinUI3Apps,
|
||||||
|
// so the root copy keeps its simple DACL while the WinUI3Apps
|
||||||
|
// copy inherits the rich DACL from its parent (matches 0.99.1
|
||||||
|
// behaviour). See Documents\PR-47233-Handoff.md for full RCA.
|
||||||
|
fs::copy_file(source, target, fs::copy_options::overwrite_existing, ec);
|
||||||
|
if (ec)
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Failed to copy: %ls (%hs)", fileName.c_str(), ec.message().c_str());
|
||||||
|
failed++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
created++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WcaLog(LOGMSG_STANDARD, "CreateWinAppSDKHardlinks: Copied %d files, %d failures", created, failed);
|
||||||
|
|
||||||
|
// Catastrophic-case escalation: if every copy failed, the WinUI3Apps tree is
|
||||||
|
// unusable (Monaco preview / context-menu shells will break). Surface this rather
|
||||||
|
// than reporting install success. Per-file failures remain tolerated.
|
||||||
|
if (created == 0 && failed > 0)
|
||||||
|
{
|
||||||
|
hr = E_FAIL;
|
||||||
|
ExitOnFailure(hr, "All WinAppSDK file copies failed; aborting install.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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::ifstream manifestFile(manifestPath); // Read as bytes; convert UTF-8 -> wide explicitly.
|
||||||
|
std::string narrowLine;
|
||||||
|
|
||||||
|
// INSTALLFOLDER from MSI typically arrives with a trailing backslash; strip it before
|
||||||
|
// normalizing so the per-line containment check doesn't false-reject every entry.
|
||||||
|
auto stripTrailingSep = [](fs::path p) {
|
||||||
|
auto s = p.native();
|
||||||
|
while (s.size() > 1 && (s.back() == L'\\' || s.back() == L'/')) s.pop_back();
|
||||||
|
return fs::path(s);
|
||||||
|
};
|
||||||
|
const fs::path winui3DirNorm = stripTrailingSep(winui3Dir).lexically_normal();
|
||||||
|
|
||||||
|
while (std::getline(manifestFile, narrowLine))
|
||||||
|
{
|
||||||
|
if (narrowLine.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (narrowLine.back() == '\r')
|
||||||
|
{
|
||||||
|
narrowLine.pop_back();
|
||||||
|
if (narrowLine.empty()) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int wideLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, nullptr, 0);
|
||||||
|
if (wideLen <= 0)
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "DeleteWinAppSDKHardlinks: Skipping non-UTF-8 entry: %hs", narrowLine.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::wstring fileName(static_cast<size_t>(wideLen) - 1, L'\0');
|
||||||
|
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, narrowLine.c_str(), -1, fileName.data(), wideLen);
|
||||||
|
|
||||||
|
// Defense-in-depth: reject entries whose resolved target escapes WinUI3Apps.
|
||||||
|
const fs::path target = (winui3Dir / fileName).lexically_normal();
|
||||||
|
const auto inWinui3 = std::mismatch(winui3DirNorm.begin(), winui3DirNorm.end(), target.begin(), target.end());
|
||||||
|
if (inWinui3.first != winui3DirNorm.end())
|
||||||
|
{
|
||||||
|
WcaLog(LOGMSG_STANDARD, "DeleteWinAppSDKHardlinks: Rejecting entry outside WinUI3Apps: %ls", fileName.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
fs::remove(target, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
WcaLog(LOGMSG_STANDARD, "DeleteWinAppSDKHardlinks: Cleaned up deduplicated copy files");
|
||||||
|
}
|
||||||
|
|
||||||
|
LExit:
|
||||||
|
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
|
||||||
|
return WcaFinalize(er);
|
||||||
|
}
|
||||||
|
|
||||||
// DllMain - Initialize and cleanup WiX custom action utils.
|
// DllMain - Initialize and cleanup WiX custom action utils.
|
||||||
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
|
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,3 +36,5 @@ EXPORTS
|
|||||||
SetBundleInstallLocationCA
|
SetBundleInstallLocationCA
|
||||||
InstallPackageIdentityMSIXCA
|
InstallPackageIdentityMSIXCA
|
||||||
UninstallPackageIdentityMSIXCA
|
UninstallPackageIdentityMSIXCA
|
||||||
|
CreateWinAppSDKHardlinksCA
|
||||||
|
DeleteWinAppSDKHardlinksCA
|
||||||
|
|||||||
@@ -112,6 +112,8 @@
|
|||||||
<Custom Action="SetInstallCmdPalPackageParam" Before="InstallCmdPalPackage" />
|
<Custom Action="SetInstallCmdPalPackageParam" Before="InstallCmdPalPackage" />
|
||||||
<Custom Action="SetUninstallCommandNotFoundParam" Before="UninstallCommandNotFound" />
|
<Custom Action="SetUninstallCommandNotFoundParam" Before="UninstallCommandNotFound" />
|
||||||
<Custom Action="SetUpgradeCommandNotFoundParam" Before="UpgradeCommandNotFound" />
|
<Custom Action="SetUpgradeCommandNotFoundParam" Before="UpgradeCommandNotFound" />
|
||||||
|
<Custom Action="SetCreateWinAppSDKHardlinksParam" Before="CreateWinAppSDKHardlinks" />
|
||||||
|
<Custom Action="SetDeleteWinAppSDKHardlinksParam" Before="DeleteWinAppSDKHardlinks" />
|
||||||
<Custom Action="SetApplyModulesRegistryChangeSetsParam" Before="ApplyModulesRegistryChangeSets" />
|
<Custom Action="SetApplyModulesRegistryChangeSetsParam" Before="ApplyModulesRegistryChangeSets" />
|
||||||
<Custom Action="SetInstallPackageIdentityMSIXParam" Before="InstallPackageIdentityMSIX" />
|
<Custom Action="SetInstallPackageIdentityMSIXParam" Before="InstallPackageIdentityMSIX" />
|
||||||
|
|
||||||
@@ -124,6 +126,7 @@
|
|||||||
<Custom Action="SetBundleInstallLocationData" Before="SetBundleInstallLocation" Condition="NOT Installed OR WIX_UPGRADE_DETECTED" />
|
<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="SetBundleInstallLocation" After="InstallFiles" Condition="NOT Installed OR WIX_UPGRADE_DETECTED" />
|
||||||
<Custom Action="ApplyModulesRegistryChangeSets" After="InstallFiles" Condition="NOT Installed" />
|
<Custom Action="ApplyModulesRegistryChangeSets" After="InstallFiles" Condition="NOT Installed" />
|
||||||
|
<Custom Action="CreateWinAppSDKHardlinks" After="InstallFiles" Condition="NOT Installed OR WIX_UPGRADE_DETECTED OR REINSTALL" />
|
||||||
<Custom Action="InstallCmdPalPackage" After="InstallFiles" Condition="NOT Installed" />
|
<Custom Action="InstallCmdPalPackage" After="InstallFiles" Condition="NOT Installed" />
|
||||||
<Custom Action="InstallPackageIdentityMSIX" After="InstallFiles" Condition="NOT Installed AND WINDOWSBUILDNUMBER >= 22000" />
|
<Custom Action="InstallPackageIdentityMSIX" After="InstallFiles" Condition="NOT Installed AND WINDOWSBUILDNUMBER >= 22000" />
|
||||||
<Custom Action="override Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Before="RemoveFiles" />
|
<Custom Action="override Wix4CloseApplications_$(sys.BUILDARCHSHORT)" Before="RemoveFiles" />
|
||||||
@@ -137,6 +140,7 @@
|
|||||||
<?endif?>
|
<?endif?>
|
||||||
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize" Condition="NOT Installed" />
|
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize" Condition="NOT Installed" />
|
||||||
<Custom Action="TelemetryLogUninstallSuccess" After="InstallFinalize" Condition="Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")" />
|
<Custom Action="TelemetryLogUninstallSuccess" After="InstallFinalize" Condition="Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")" />
|
||||||
|
<Custom Action="DeleteWinAppSDKHardlinks" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
||||||
<Custom Action="UnApplyModulesRegistryChangeSets" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
<Custom Action="UnApplyModulesRegistryChangeSets" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
||||||
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
||||||
<Custom Action="CleanImageResizerRuntimeRegistry" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
<Custom Action="CleanImageResizerRuntimeRegistry" Before="RemoveFiles" Condition="Installed AND (REMOVE="ALL")" />
|
||||||
@@ -189,8 +193,10 @@
|
|||||||
<CustomAction Id="SetUpgradeCommandNotFoundParam" Property="UpgradeCommandNotFound" Value="[INSTALLFOLDER]" />
|
<CustomAction Id="SetUpgradeCommandNotFoundParam" Property="UpgradeCommandNotFound" Value="[INSTALLFOLDER]" />
|
||||||
|
|
||||||
<CustomAction Id="SetCreateWinAppSDKHardlinksParam" Property="CreateWinAppSDKHardlinks" Value="[INSTALLFOLDER]" />
|
<CustomAction Id="SetCreateWinAppSDKHardlinksParam" Property="CreateWinAppSDKHardlinks" Value="[INSTALLFOLDER]" />
|
||||||
|
<CustomAction Id="CreateWinAppSDKHardlinks" Return="check" Impersonate="yes" Execute="deferred" DllEntry="CreateWinAppSDKHardlinksCA" BinaryRef="PTCustomActions" />
|
||||||
|
|
||||||
<CustomAction Id="SetDeleteWinAppSDKHardlinksParam" Property="DeleteWinAppSDKHardlinks" Value="[INSTALLFOLDER]" />
|
<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]" />
|
<CustomAction Id="SetCreatePTInteropHardlinksParam" Property="CreatePTInteropHardlinks" Value="[INSTALLFOLDER]" />
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,18 @@
|
|||||||
|
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<DirectoryRef Id="WinUI3AppsInstallFolder">
|
<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 -->
|
<!-- Generated by generateFileComponents.ps1 -->
|
||||||
<!--WinUI3ApplicationsFiles_Component_Def-->
|
<!--WinUI3ApplicationsFiles_Component_Def-->
|
||||||
</DirectoryRef>
|
</DirectoryRef>
|
||||||
|
|
||||||
<ComponentGroup Id="WinUI3ApplicationsComponentGroup">
|
<ComponentGroup Id="WinUI3ApplicationsComponentGroup">
|
||||||
|
<ComponentRef Id="WinUI3Apps_Hardlinks_Manifest" />
|
||||||
</ComponentGroup>
|
</ComponentGroup>
|
||||||
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@@ -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")
|
$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")
|
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
|
||||||
|
|
||||||
if ($fileDepsJson -eq [string]::Empty) {
|
if ($fileDepsJson -eq [string]::Empty) {
|
||||||
@@ -85,11 +89,16 @@ Function Generate-FileComponents() {
|
|||||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList',
|
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'fileList',
|
||||||
Justification = 'variable is used in another scope')]
|
Justification = 'variable is used in another scope')]
|
||||||
|
|
||||||
$fileList = $matches[2] -split ';'
|
$fileList = $matches[2] -split ';' | Where-Object { $_ -ne '' }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($null -eq $fileList -or $fileList.Count -eq 0) {
|
||||||
|
# No files to generate components for — leave placeholder intact
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
$componentId = "$($fileListName)_Component"
|
$componentId = "$($fileListName)_Component"
|
||||||
|
|
||||||
$componentDefs = "`r`n"
|
$componentDefs = "`r`n"
|
||||||
@@ -154,6 +163,67 @@ Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSSc
|
|||||||
|
|
||||||
#WinUI3Applications
|
#WinUI3Applications
|
||||||
Generate-FileList -fileDepsJson "" -fileListName WinUI3ApplicationsFiles -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"
|
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 re-created as plain file copies at install time by CreateWinAppSDKHardlinksCA.
|
||||||
|
# (The CA's name is historical: it now uses fs::copy_file rather than CreateHardLinkW to avoid
|
||||||
|
# DACL contamination across the shared inode -- see CustomAction.cpp for details.)
|
||||||
|
$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 = @()
|
||||||
|
|
||||||
|
# Read the BaseApplications WXS file list so we only deduplicate files that the MSI
|
||||||
|
# is actually deploying to the install root. If a file was stripped from BaseApplications
|
||||||
|
# by an earlier step (e.g., the ImageResizer leaked-apphost workaround above), the
|
||||||
|
# install-time CA's source would be missing and both copies would disappear.
|
||||||
|
$baseAppsWxs = Get-Content $baseAppWxsPath -Raw
|
||||||
|
$baseAppsFileList = @()
|
||||||
|
if ($baseAppsWxs -match "\<\?define BaseApplicationsFiles=([^?]*)\?\>") {
|
||||||
|
$baseAppsFileList = $matches[1] -split ';' | Where-Object { $_ -ne '' }
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($file in $winui3FileList) {
|
||||||
|
# Skip files that were intentionally not deployed to root by the build
|
||||||
|
if ($baseAppsFileList -notcontains $file) { continue }
|
||||||
|
|
||||||
|
$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 copied at install time)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Always write hardlinks.txt (may be empty — CA handles that gracefully)
|
||||||
|
# Write as UTF-8 without BOM so the install-time CA can read it via std::ifstream
|
||||||
|
# + MultiByteToWideChar(CP_UTF8) without dealing with PS-version-dependent default
|
||||||
|
# encodings or a leading BOM.
|
||||||
|
[System.IO.File]::WriteAllLines($manifestPath, [string[]]$hardlinkFiles, (New-Object System.Text.UTF8Encoding($false)))
|
||||||
|
}
|
||||||
|
|
||||||
Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs
|
Generate-FileComponents -fileListName "WinUI3ApplicationsFiles" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs
|
||||||
|
|
||||||
#AdvancedPaste
|
#AdvancedPaste
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ using System.IO.Pipes;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Authentication.ExtendedProtection;
|
using System.Security.Authentication.ExtendedProtection;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.ServiceModel.Channels;
|
|
||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|||||||
@@ -66,7 +66,6 @@
|
|||||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||||
<PackageReference Include="Microsoft.Windows.Compatibility" />
|
<PackageReference Include="Microsoft.Windows.Compatibility" />
|
||||||
<PackageReference Include="StreamJsonRpc" />
|
<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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
<ProjectReference Include="..\..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||||
|
|||||||
@@ -214,7 +214,6 @@
|
|||||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||||
<PackageReference Include="Microsoft.Windows.Compatibility" />
|
<PackageReference Include="Microsoft.Windows.Compatibility" />
|
||||||
<PackageReference Include="StreamJsonRpc" />
|
<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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||||
|
|||||||
@@ -71,7 +71,6 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" />
|
||||||
<PackageReference Include="StreamJsonRpc" />
|
<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>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
<ProjectReference Include="..\..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||||
|
|||||||
Reference in New Issue
Block a user