fix: resolve silent failures in AppX removal and PS7 DISM bugs (#4822)

* fix: resolve silent failures in AppX removal and PS7 DISM bugs

- Fixed a bug where Remove-AppxPackage failed silently in PowerShell 7 due to pipeline binding issues. We now explicitly loop through Get-AppxPackage results and pass PackageFullName directly.
- Added wildcard matching to Get-AppxPackage and Get-AppxProvisionedPackage to reliably locate packages regardless of short name vs family name variations.
- Offloaded Get-AppxProvisionedPackage and Remove-AppxProvisionedPackage execution to Windows PowerShell 5.1 (powershell.exe) to bypass PowerShell 7 DISM COM exceptions ("Class not registered") and performance hangs.
- Deduplicated AppX removal logic by calling Remove-WinUtilAPPX directly from Invoke-WPFAppxRemoval instead of copying the implementation.

* refactor(Remove-WinUtilAPPX): use script block to improve readability

* feat: enhance AppX removal process and add provisioned package removal function

* fix(tests): update AppX removal tests

This also adds tests for the new Remove-WinUtilProvisionedAPPX functionality

* Move AppX removal under Tweaks

* Address AppX removal review feedback

* Complete AppX removal error handling

* Prevent AppX cleanup confirmation prompts

---------

Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
Omar
2026-07-13 13:16:35 -05:00
committed by GitHub
co-authored by Chris Titus
parent c8e756dc11
commit 9791386f53
10 changed files with 236 additions and 38 deletions
@@ -78,6 +78,7 @@ function Invoke-WinUtilTweaks {
$sync.configs.tweaks.$CheckBox.appx | ForEach-Object {
Remove-WinUtilAPPX -Name $psitem
}
Remove-WinUtilProvisionedAPPX -PackageList $sync.configs.tweaks.$CheckBox.appx
}
}
Write-WinUtilLog -Component "Tweaks" -Message "$action tweak completed: $CheckBox"
+15 -2
View File
@@ -17,7 +17,20 @@ function Remove-WinUtilAPPX {
Write-Host "Removing $Name"
Write-WinUtilLog -Component "AppX" -Message "Removing AppX package pattern: $Name"
Get-AppxPackage $Name -AllUsers | Remove-AppxPackage -AllUsers
Get-AppxProvisionedPackage -Online | Where-Object DisplayName -like $Name | Remove-AppxProvisionedPackage -Online
# We explicitly loop through packages instead of using the pipeline because PowerShell 7 pipeline binding
# for Remove-AppxPackage fails silently, and Get-AppxPackage -AllUsers returns duplicate objects for each user profile.
$pkgs = Get-AppxPackage "*$Name*" -AllUsers | Sort-Object -Property PackageFullName -Unique
if ($null -ne $pkgs) {
foreach ($pkg in $pkgs) {
try {
Remove-AppxPackage -Package $pkg.PackageFullName -AllUsers -ErrorAction Stop
}
catch {
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "Failed to remove AppX package $($pkg.PackageFullName): $($_.Exception.Message)"
}
}
}
Write-WinUtilLog -Component "AppX" -Message "AppX removal completed for package pattern: $Name"
}
@@ -0,0 +1,61 @@
function Remove-WinUtilProvisionedAPPX {
<#
.SYNOPSIS
Removes all AppX provisioned packages that match the given names
.PARAMETER PackageList
An array of names of the APPX packages to remove
.EXAMPLE
Remove-WinUtilProvisionedAPPX -PackageList @("Microsoft.Microsoft3DViewer", "Microsoft.WindowsCalculator")
#>
param (
[string[]]$PackageList
)
if ($null -eq $PackageList -or $PackageList.Count -eq 0) {
return
}
Write-Host "`nRemoving provisioned packages..."
Write-WinUtilLog -Component "AppX" -Message "Removing AppX provisioned packages: $($PackageList -join ', ')"
# DISM cmdlets like Get-AppxProvisionedPackage often fail with "Class not registered" or hang in PowerShell 7.
# We shell out to Windows PowerShell 5.1 (powershell.exe) to reliably remove the provisioned packages.
$ps5Command = {
$pkgs = $args
$provisionedPackages = Get-AppxProvisionedPackage -Online -ErrorAction SilentlyContinue
$failures = [System.Collections.Generic.List[string]]::new()
foreach ($Package in $pkgs) {
$provs = $provisionedPackages |
Where-Object DisplayName -Like "*$Package*"
if ($null -ne $provs) {
foreach ($prov in $provs) {
try {
Remove-AppxProvisionedPackage -Online -PackageName $prov.PackageName -ErrorAction Stop | Out-Null
}
catch {
$failures.Add("Failed to remove provisioned AppX package $($prov.PackageName): $($_.Exception.Message)")
}
}
}
}
if ($failures.Count -gt 0) {
throw ($failures -join [Environment]::NewLine)
}
}
$removalOutput = powershell.exe -NoProfile -NonInteractive -Command $ps5Command -args $PackageList 2>&1
if ($LASTEXITCODE -ne 0 -or $null -ne $removalOutput) {
$failureDetails = ($removalOutput | Out-String).Trim()
Write-WinUtilLog -Level "ERROR" -Component "AppX" -Message "AppX provisioned package removal failed: $failureDetails"
return
}
Write-WinUtilLog -Component "AppX" -Message "AppX provisioned package removal completed."
}