diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json
index 2a1760d94e..d99fabf0eb 100644
--- a/.pipelines/ESRPSigning_core.json
+++ b/.pipelines/ESRPSigning_core.json
@@ -235,7 +235,9 @@
"*Microsoft.CmdPal.UI_*.msix",
"PowerToys.DSC.dll",
- "PowerToys.DSC.exe"
+ "PowerToys.DSC.exe",
+
+ "PowerToysSparse.msix"
],
"SigningInfo": {
"Operations": [
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9ce4168538..129abadb00 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -61,7 +61,8 @@
-
+
+
diff --git a/PowerToys.sln b/PowerToys.sln
index da92a9e4a4..ece51b958c 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -3016,7 +3016,6 @@ Global
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9} = {264B412F-DB8B-4CF8-A74B-96998B183045}
{1A066C63-64B3-45F8-92FE-664E1CCE8077} = {1AFB6476-670D-4E80-A464-657E01DFF482}
- {E2A5A82E-1E5B-4C8D-9A4F-2B1A8F9E5C3D} = {1AFB6476-670D-4E80-A464-657E01DFF482}
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{B25AC7A5-FB9F-4789-B392-D5C85E948670} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3}
diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp
index 308b304591..0cfc3b1765 100644
--- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp
+++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.cpp
@@ -594,6 +594,216 @@ LExit:
return WcaFinalize(er);
}
+UINT __stdcall InstallPackageIdentityMSIXCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ LPWSTR customActionData = nullptr;
+ std::wstring installFolderPath;
+ std::wstring installScope;
+ std::wstring msixPath;
+ std::wstring data;
+ size_t delimiterPos;
+ bool isMachineLevel = false;
+
+ hr = WcaInitialize(hInstall, "InstallPackageIdentityMSIXCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ hr = WcaGetProperty(L"CustomActionData", &customActionData);
+ ExitOnFailure(hr, "Failed to get CustomActionData property");
+
+ // Parse CustomActionData: "[INSTALLFOLDER];[InstallScope]"
+ data = customActionData;
+ delimiterPos = data.find(L';');
+ installFolderPath = data.substr(0, delimiterPos);
+ installScope = data.substr(delimiterPos + 1);
+
+ // Check if this is a machine-level installation
+ if (installScope == L"perMachine")
+ {
+ isMachineLevel = true;
+ }
+
+ Logger::info(L"Installing PackageIdentity MSIX - perUser: {}", !isMachineLevel);
+
+ // Construct path to PackageIdentity MSIX
+ msixPath = installFolderPath;
+ msixPath += L"PowerToysSparse.msix";
+
+ if (std::filesystem::exists(msixPath))
+ {
+ using namespace winrt::Windows::Management::Deployment;
+ using namespace winrt::Windows::Foundation;
+
+ try
+ {
+
+ std::wstring externalLocation = installFolderPath; // External content location (PowerToys install folder)
+ Uri externalUri{ externalLocation }; // External location URI for sparse package content
+ Uri packageUri{ msixPath }; // The MSIX file URI
+
+ PackageManager packageManager;
+
+ if (isMachineLevel)
+ {
+ // Machine-level installation
+
+ StagePackageOptions stageOptions;
+ stageOptions.ExternalLocationUri(externalUri);
+
+ auto stageResult = packageManager.StagePackageByUriAsync(packageUri, stageOptions).get();
+
+ uint32_t stageErrorCode = static_cast(stageResult.ExtendedErrorCode());
+ if (stageErrorCode == 0)
+ {
+ std::wstring packageFamilyName = L"Microsoft.PowerToys.SparseApp_8wekyb3d8bbwe";
+
+ try
+ {
+ auto provisionResult = packageManager.ProvisionPackageForAllUsersAsync(packageFamilyName).get();
+ uint32_t provisionErrorCode = static_cast(provisionResult.ExtendedErrorCode());
+
+ if (provisionErrorCode != 0)
+ {
+ Logger::error(L"Machine-level provisioning failed: 0x{:08X}", provisionErrorCode);
+ }
+ }
+ catch (const winrt::hresult_error& ex)
+ {
+ Logger::error(L"Provisioning exception: HRESULT 0x{:08X}", static_cast(ex.code()));
+ }
+ }
+ else
+ {
+ Logger::error(L"Package staging failed: 0x{:08X}", stageErrorCode);
+ }
+ }
+ else
+ {
+ AddPackageOptions addOptions;
+ addOptions.ExternalLocationUri(externalUri);
+
+ auto addResult = packageManager.AddPackageByUriAsync(packageUri, addOptions).get();
+
+ if (!addResult.IsRegistered())
+ {
+ uint32_t errorCode = static_cast(addResult.ExtendedErrorCode());
+ Logger::error(L"Per-user installation failed: 0x{:08X}", errorCode);
+ }
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ Logger::error(L"PackageIdentity MSIX installation failed - Exception: {}",
+ winrt::to_hstring(ex.what()).c_str());
+ }
+ }
+ else
+ {
+ Logger::error(L"PackageIdentity MSIX not found: " + msixPath);
+ }
+
+LExit:
+ ReleaseStr(customActionData);
+ er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
+ return WcaFinalize(er);
+}
+
+UINT __stdcall UninstallPackageIdentityMSIXCA(MSIHANDLE hInstall)
+{
+ HRESULT hr = S_OK;
+ UINT er = ERROR_SUCCESS;
+ using namespace winrt::Windows::Management::Deployment;
+ using namespace winrt::Windows::Foundation;
+
+ LPWSTR installScope = nullptr;
+ bool isMachineLevel = false;
+
+ PackageManager pm;
+
+ hr = WcaInitialize(hInstall, "UninstallPackageIdentityMSIXCA");
+ ExitOnFailure(hr, "Failed to initialize");
+
+ // Check if this was a machine-level installation
+ hr = WcaGetProperty(L"InstallScope", &installScope);
+ if (SUCCEEDED(hr) && installScope && wcscmp(installScope, L"perMachine") == 0)
+ {
+ isMachineLevel = true;
+ }
+
+ Logger::info(L"Uninstalling PackageIdentity MSIX - perUser: {}", !isMachineLevel);
+
+ try
+ {
+ std::wstring packageFamilyName = L"Microsoft.PowerToys.SparseApp_8wekyb3d8bbwe";
+
+ if (isMachineLevel)
+ {
+ // Machine-level uninstallation: deprovision + remove for all users
+
+ // First deprovision the package
+ try
+ {
+ auto deprovisionResult = pm.DeprovisionPackageForAllUsersAsync(packageFamilyName).get();
+ if (deprovisionResult.IsRegistered())
+ {
+ Logger::warn(L"Machine-level deprovisioning completed with warnings");
+ }
+ }
+ catch (const winrt::hresult_error& ex)
+ {
+ Logger::warn(L"Machine-level deprovisioning failed: HRESULT 0x{:08X}", static_cast(ex.code()));
+ }
+
+ // Then remove packages for all users
+ auto packages = pm.FindPackagesForUserWithPackageTypes({}, packageFamilyName, PackageTypes::Main);
+ for (const auto& package : packages)
+ {
+ try
+ {
+ auto machineResult = pm.RemovePackageAsync(package.Id().FullName(), RemovalOptions::RemoveForAllUsers).get();
+ if (machineResult.IsRegistered())
+ {
+ uint32_t errorCode = static_cast(machineResult.ExtendedErrorCode());
+ Logger::error(L"Machine-level removal failed: 0x{:08X} - {}", errorCode, machineResult.ErrorText());
+ }
+ }
+ catch (const winrt::hresult_error& ex)
+ {
+ Logger::error(L"Machine-level removal exception: HRESULT 0x{:08X}", static_cast(ex.code()));
+ }
+ }
+ }
+ else
+ {
+ // Per-user uninstallation: standard removal
+
+ auto packages = pm.FindPackagesForUserWithPackageTypes({}, packageFamilyName, PackageTypes::Main);
+ for (const auto& package : packages)
+ {
+ auto userResult = pm.RemovePackageAsync(package.Id().FullName()).get();
+ if (userResult.IsRegistered())
+ {
+ uint32_t errorCode = static_cast(userResult.ExtendedErrorCode());
+ Logger::error(L"Per-user removal failed: 0x{:08X} - {}", errorCode, userResult.ErrorText());
+ }
+ }
+ }
+ }
+ catch (const std::exception& ex)
+ {
+ std::string errorMsg = "Failed to uninstall PackageIdentity MSIX: " + std::string(ex.what());
+ Logger::error(errorMsg);
+ // Don't fail the entire uninstallation if PackageIdentity fails
+ Logger::warn(L"Continuing uninstallation despite PackageIdentity MSIX error");
+ }
+
+LExit:
+ ReleaseStr(installScope);
+ 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);
diff --git a/installer/PowerToysSetupCustomActionsVNext/CustomAction.def b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def
index 931a555953..4bad107f16 100644
--- a/installer/PowerToysSetupCustomActionsVNext/CustomAction.def
+++ b/installer/PowerToysSetupCustomActionsVNext/CustomAction.def
@@ -33,3 +33,5 @@ EXPORTS
CleanPowerRenameRuntimeRegistryCA
CleanNewPlusRuntimeRegistryCA
SetBundleInstallLocationCA
+ InstallPackageIdentityMSIXCA
+ UninstallPackageIdentityMSIXCA
diff --git a/installer/PowerToysSetupVNext/Core.wxs b/installer/PowerToysSetupVNext/Core.wxs
index f7da6162f9..213ae304ea 100644
--- a/installer/PowerToysSetupVNext/Core.wxs
+++ b/installer/PowerToysSetupVNext/Core.wxs
@@ -73,6 +73,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -134,6 +144,7 @@
+
diff --git a/installer/PowerToysSetupVNext/Product.wxs b/installer/PowerToysSetupVNext/Product.wxs
index 2505557d77..556fddc7f4 100644
--- a/installer/PowerToysSetupVNext/Product.wxs
+++ b/installer/PowerToysSetupVNext/Product.wxs
@@ -112,6 +112,7 @@
+
@@ -123,6 +124,7 @@
+
@@ -144,6 +146,7 @@
+