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 @@ +