mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-04-03 14:06:27 +00:00
Add support for multiple AppIds for app removal (#526)
This commit is contained in:
@@ -647,7 +647,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"FriendlyName": "Microsoft Edge",
|
"FriendlyName": "Microsoft Edge",
|
||||||
"AppId": "Microsoft.Edge",
|
"AppId": ["Microsoft.Edge", "XPFFTQ037JWMHS"],
|
||||||
"Description": "Windows' default browser, WARNING: Removing this app also removes the only browser from Windows Sandbox and could affect other apps",
|
"Description": "Windows' default browser, WARNING: Removing this app also removes the only browser from Windows Sandbox and could affect other apps",
|
||||||
"SelectedByDefault": false,
|
"SelectedByDefault": false,
|
||||||
"Recommendation": "unsafe"
|
"Recommendation": "unsafe"
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ function RemoveApps {
|
|||||||
|
|
||||||
$appIndex = 0
|
$appIndex = 0
|
||||||
$appCount = @($appsList).Count
|
$appCount = @($appsList).Count
|
||||||
|
$edgeIds = @('Microsoft.Edge', 'XPFFTQ037JWMHS')
|
||||||
|
$edgeUninstallSucceeded = $false
|
||||||
|
$edgeScheduledTaskAdded = $false
|
||||||
|
|
||||||
Foreach ($app in $appsList) {
|
Foreach ($app in $appsList) {
|
||||||
if ($script:CancelRequested) {
|
if ($script:CancelRequested) {
|
||||||
@@ -25,20 +28,27 @@ function RemoveApps {
|
|||||||
Write-Host "Attempting to remove $app..."
|
Write-Host "Attempting to remove $app..."
|
||||||
|
|
||||||
# Use WinGet only to remove OneDrive and Edge
|
# Use WinGet only to remove OneDrive and Edge
|
||||||
if (($app -eq "Microsoft.OneDrive") -or ($app -eq "Microsoft.Edge")) {
|
if (($app -eq "Microsoft.OneDrive") -or ($edgeIds -contains $app)) {
|
||||||
if ($script:WingetInstalled -eq $false) {
|
if ($script:WingetInstalled -eq $false) {
|
||||||
Write-Host "WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red
|
Write-Host "WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
$appName = $app -replace '\.', '_'
|
$isEdgeId = $edgeIds -contains $app
|
||||||
|
$appName = if ($isEdgeId) { 'Microsoft_Edge' } else { $app -replace '\.', '_' }
|
||||||
|
|
||||||
# Uninstall app via WinGet, or create a scheduled task to uninstall it later
|
# Uninstall app via WinGet, or create a scheduled task to uninstall it later
|
||||||
if ($script:Params.ContainsKey("User")) {
|
if ($script:Params.ContainsKey("User")) {
|
||||||
|
if (-not ($isEdgeId -and $edgeScheduledTaskAdded)) {
|
||||||
ImportRegistryFile "Adding scheduled task to uninstall $app for user $(GetUserName)..." "Uninstall_$($appName).reg"
|
ImportRegistryFile "Adding scheduled task to uninstall $app for user $(GetUserName)..." "Uninstall_$($appName).reg"
|
||||||
|
if ($isEdgeId) { $edgeScheduledTaskAdded = $true }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
elseif ($script:Params.ContainsKey("Sysprep")) {
|
elseif ($script:Params.ContainsKey("Sysprep")) {
|
||||||
|
if (-not ($isEdgeId -and $edgeScheduledTaskAdded)) {
|
||||||
ImportRegistryFile "Adding scheduled task to uninstall $app after for new users..." "Uninstall_$($appName).reg"
|
ImportRegistryFile "Adding scheduled task to uninstall $app after for new users..." "Uninstall_$($appName).reg"
|
||||||
|
if ($isEdgeId) { $edgeScheduledTaskAdded = $true }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
# Uninstall app via WinGet
|
# Uninstall app via WinGet
|
||||||
@@ -47,7 +57,20 @@ function RemoveApps {
|
|||||||
winget uninstall --accept-source-agreements --disable-interactivity --id $appId
|
winget uninstall --accept-source-agreements --disable-interactivity --id $appId
|
||||||
} -ArgumentList $app
|
} -ArgumentList $app
|
||||||
|
|
||||||
If (($app -eq "Microsoft.Edge") -and (Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code")) {
|
$wingetFailed = Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code|No installed package found matching input criteria|No package found matching input criteria" -SimpleMatch:$false
|
||||||
|
if ($isEdgeId) {
|
||||||
|
if (-not $wingetFailed) {
|
||||||
|
$edgeUninstallSucceeded = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompt immediately after the final selected Edge ID attempt (if all attempts failed)
|
||||||
|
$hasRemainingEdgeIds = $false
|
||||||
|
if ($appIndex -lt $appCount) {
|
||||||
|
$remainingApps = @($appsList)[($appIndex)..($appCount - 1)]
|
||||||
|
$hasRemainingEdgeIds = @($remainingApps | Where-Object { $edgeIds -contains $_ }).Count -gt 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $hasRemainingEdgeIds -and -not $edgeUninstallSucceeded) {
|
||||||
Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red
|
Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red
|
||||||
|
|
||||||
if ($script:GuiWindow) {
|
if ($script:GuiWindow) {
|
||||||
@@ -64,6 +87,7 @@ function RemoveApps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,24 +16,36 @@ function LoadAppsDetailsFromJson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($appData in $jsonContent.Apps) {
|
foreach ($appData in $jsonContent.Apps) {
|
||||||
$appId = $appData.AppId.Trim()
|
# Handle AppId as array (could be single or multiple IDs)
|
||||||
if ($appId.length -eq 0) { continue }
|
$appIdArray = if ($appData.AppId -is [array]) { $appData.AppId } else { @($appData.AppId) }
|
||||||
|
$appIdArray = $appIdArray | ForEach-Object { $_.Trim() } | Where-Object { $_.length -gt 0 }
|
||||||
|
if ($appIdArray.Count -eq 0) { continue }
|
||||||
|
|
||||||
if ($OnlyInstalled) {
|
if ($OnlyInstalled) {
|
||||||
if (-not ($InstalledList -like ("*$appId*")) -and -not (Get-AppxPackage -Name $appId)) {
|
$isInstalled = $false
|
||||||
continue
|
foreach ($appId in $appIdArray) {
|
||||||
|
if (($InstalledList -like ("*$appId*")) -or (Get-AppxPackage -Name $appId)) {
|
||||||
|
$isInstalled = $true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if (($appId -eq "Microsoft.Edge") -and -not ($InstalledList -like "* Microsoft.Edge *")) {
|
if (($appId -eq "Microsoft.Edge") -and ($InstalledList -like "* Microsoft.Edge *")) {
|
||||||
continue
|
$isInstalled = $true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (-not $isInstalled) { continue }
|
||||||
|
}
|
||||||
|
|
||||||
$friendlyName = if ($appData.FriendlyName) { $appData.FriendlyName } else { $appId }
|
# Use first AppId for fallback names, join all for display
|
||||||
$displayName = if ($appData.FriendlyName) { "$($appData.FriendlyName) ($appId)" } else { $appId }
|
$primaryAppId = $appIdArray[0]
|
||||||
|
$appIdDisplay = $appIdArray -join ', '
|
||||||
|
$friendlyName = if ($appData.FriendlyName) { $appData.FriendlyName } else { $primaryAppId }
|
||||||
|
$displayName = if ($appData.FriendlyName) { "$($appData.FriendlyName) ($appIdDisplay)" } else { $appIdDisplay }
|
||||||
$isChecked = if ($InitialCheckedFromJson) { $appData.SelectedByDefault } else { $false }
|
$isChecked = if ($InitialCheckedFromJson) { $appData.SelectedByDefault } else { $false }
|
||||||
|
|
||||||
$apps += [PSCustomObject]@{
|
$apps += [PSCustomObject]@{
|
||||||
AppId = $appId
|
AppId = $appIdArray
|
||||||
|
AppIdDisplay = $appIdDisplay
|
||||||
FriendlyName = $friendlyName
|
FriendlyName = $friendlyName
|
||||||
DisplayName = $displayName
|
DisplayName = $displayName
|
||||||
IsChecked = $isChecked
|
IsChecked = $isChecked
|
||||||
|
|||||||
@@ -16,10 +16,12 @@ function LoadAppsFromFile {
|
|||||||
# JSON file format
|
# JSON file format
|
||||||
$jsonContent = Get-Content -Path $appsFilePath -Raw | ConvertFrom-Json
|
$jsonContent = Get-Content -Path $appsFilePath -Raw | ConvertFrom-Json
|
||||||
Foreach ($appData in $jsonContent.Apps) {
|
Foreach ($appData in $jsonContent.Apps) {
|
||||||
$appId = $appData.AppId.Trim()
|
# Handle AppId as array (could be single or multiple IDs)
|
||||||
|
$appIdArray = if ($appData.AppId -is [array]) { $appData.AppId } else { @($appData.AppId) }
|
||||||
|
$appIdArray = $appIdArray | ForEach-Object { $_.Trim() } | Where-Object { $_.length -gt 0 }
|
||||||
$selectedByDefault = $appData.SelectedByDefault
|
$selectedByDefault = $appData.SelectedByDefault
|
||||||
if ($selectedByDefault -and $appId.length -gt 0) {
|
if ($selectedByDefault -and $appIdArray.Count -gt 0) {
|
||||||
$appsList += $appId
|
$appsList += $appIdArray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ function ValidateAppslist {
|
|||||||
$appsList
|
$appsList
|
||||||
)
|
)
|
||||||
|
|
||||||
$supportedAppsList = (LoadAppsDetailsFromJson | ForEach-Object { $_.AppId })
|
$supportedAppsList = @(LoadAppsDetailsFromJson | ForEach-Object { @($_.AppId) }) | ForEach-Object { $_.Trim() } | Where-Object { $_.Length -gt 0 }
|
||||||
$validatedAppsList = @()
|
$validatedAppsList = @()
|
||||||
|
|
||||||
# Validate provided appsList against supportedAppsList
|
# Validate provided appsList against supportedAppsList
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ function Show-AppSelectionWindow {
|
|||||||
$checkbox = New-Object System.Windows.Controls.CheckBox
|
$checkbox = New-Object System.Windows.Controls.CheckBox
|
||||||
$checkbox.Content = $_.DisplayName
|
$checkbox.Content = $_.DisplayName
|
||||||
$checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $_.DisplayName)
|
$checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $_.DisplayName)
|
||||||
$checkbox.Tag = $_.AppId
|
$checkbox.Tag = $_.AppIdDisplay
|
||||||
|
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIds' -Value @($_.AppId)
|
||||||
$checkbox.IsChecked = $_.IsChecked
|
$checkbox.IsChecked = $_.IsChecked
|
||||||
$checkbox.ToolTip = $_.Description
|
$checkbox.ToolTip = $_.Description
|
||||||
$checkbox.Style = $window.Resources["AppsPanelCheckBoxStyle"]
|
$checkbox.Style = $window.Resources["AppsPanelCheckBoxStyle"]
|
||||||
@@ -118,9 +119,10 @@ function Show-AppSelectionWindow {
|
|||||||
$selectedApps = @()
|
$selectedApps = @()
|
||||||
foreach ($child in $appsPanel.Children) {
|
foreach ($child in $appsPanel.Children) {
|
||||||
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
|
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
|
||||||
$selectedApps += $child.Tag
|
$selectedApps += @($child.AppIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$selectedApps = @($selectedApps | Where-Object { $_ } | Select-Object -Unique)
|
||||||
|
|
||||||
# Close form without saving if no apps were selected
|
# Close form without saving if no apps were selected
|
||||||
if ($selectedApps.Count -eq 0) {
|
if ($selectedApps.Count -eq 0) {
|
||||||
|
|||||||
@@ -241,7 +241,7 @@ function Show-MainWindow {
|
|||||||
$check = ($this.IsChecked -eq $true)
|
$check = ($this.IsChecked -eq $true)
|
||||||
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
|
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
|
||||||
$presetIds = $this.PresetAppIds
|
$presetIds = $this.PresetAppIds
|
||||||
ApplyPresetToApps -MatchFilter { param($c) $presetIds -contains $c.Tag }.GetNewClosure() -Check $check
|
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $presetIds -contains $_ }).Count -gt 0 }.GetNewClosure() -Check $check
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ function Show-MainWindow {
|
|||||||
$key = switch ($script:SortColumn) {
|
$key = switch ($script:SortColumn) {
|
||||||
'Name' { { $_.AppName } }
|
'Name' { { $_.AppName } }
|
||||||
'Description' { { $_.AppDescription } }
|
'Description' { { $_.AppDescription } }
|
||||||
'AppId' { { $_.Tag } }
|
'AppId' { { $_.AppIdDisplay } }
|
||||||
}
|
}
|
||||||
$sorted = $children | Sort-Object $key -Descending:(-not $script:SortAscending)
|
$sorted = $children | Sort-Object $key -Descending:(-not $script:SortAscending)
|
||||||
$appsPanel.Children.Clear()
|
$appsPanel.Children.Clear()
|
||||||
@@ -379,14 +379,6 @@ function Show-MainWindow {
|
|||||||
function UpdatePresetStates {
|
function UpdatePresetStates {
|
||||||
$script:UpdatingPresets = $true
|
$script:UpdatingPresets = $true
|
||||||
try {
|
try {
|
||||||
# Build a set of currently checked app tags for fast lookup
|
|
||||||
$checkedTags = @{}
|
|
||||||
foreach ($child in $appsPanel.Children) {
|
|
||||||
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
|
|
||||||
$checkedTags[$child.Tag] = $true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Helper: count matching and checked apps, set checkbox state
|
# Helper: count matching and checked apps, set checkbox state
|
||||||
function SetPresetState($checkbox, [scriptblock]$MatchFilter) {
|
function SetPresetState($checkbox, [scriptblock]$MatchFilter) {
|
||||||
$total = 0; $checked = 0
|
$total = 0; $checked = 0
|
||||||
@@ -394,7 +386,7 @@ function Show-MainWindow {
|
|||||||
if ($child -is [System.Windows.Controls.CheckBox]) {
|
if ($child -is [System.Windows.Controls.CheckBox]) {
|
||||||
if (& $MatchFilter $child) {
|
if (& $MatchFilter $child) {
|
||||||
$total++
|
$total++
|
||||||
if ($checkedTags.ContainsKey($child.Tag)) { $checked++ }
|
if ($child.IsChecked) { $checked++ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,12 +408,12 @@ function Show-MainWindow {
|
|||||||
SetPresetState $presetDefaultApps { param($c) $c.SelectedByDefault -eq $true }
|
SetPresetState $presetDefaultApps { param($c) $c.SelectedByDefault -eq $true }
|
||||||
foreach ($jsonCb in $script:JsonPresetCheckboxes) {
|
foreach ($jsonCb in $script:JsonPresetCheckboxes) {
|
||||||
$localIds = $jsonCb.PresetAppIds
|
$localIds = $jsonCb.PresetAppIds
|
||||||
SetPresetState $jsonCb { param($c) $localIds -contains $c.Tag }.GetNewClosure()
|
SetPresetState $jsonCb { param($c) (@($c.AppIds) | Where-Object { $localIds -contains $_ }).Count -gt 0 }.GetNewClosure()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Last used preset: only update if it's visible (has saved apps)
|
# Last used preset: only update if it's visible (has saved apps)
|
||||||
if ($presetLastUsed.Visibility -ne 'Collapsed' -and $script:SavedAppIds) {
|
if ($presetLastUsed.Visibility -ne 'Collapsed' -and $script:SavedAppIds) {
|
||||||
SetPresetState $presetLastUsed { param($c) $script:SavedAppIds -contains $c.Tag }
|
SetPresetState $presetLastUsed { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@@ -760,9 +752,9 @@ function Show-MainWindow {
|
|||||||
$app = $appsToAdd[$i]
|
$app = $appsToAdd[$i]
|
||||||
|
|
||||||
$checkbox = New-Object System.Windows.Controls.CheckBox
|
$checkbox = New-Object System.Windows.Controls.CheckBox
|
||||||
$automationName = if ($app.FriendlyName) { $app.FriendlyName } elseif ($app.AppId) { $app.AppId } else { $null }
|
$automationName = if ($app.FriendlyName) { $app.FriendlyName } elseif ($app.AppIdDisplay) { $app.AppIdDisplay } else { $null }
|
||||||
if ($automationName) { $checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $automationName) }
|
if ($automationName) { $checkbox.SetValue([System.Windows.Automation.AutomationProperties]::NameProperty, $automationName) }
|
||||||
$checkbox.Tag = $app.AppId
|
$checkbox.Tag = $app.AppIdDisplay
|
||||||
$checkbox.IsChecked = $app.IsChecked
|
$checkbox.IsChecked = $app.IsChecked
|
||||||
$checkbox.Style = $window.Resources['AppsPanelCheckBoxStyle']
|
$checkbox.Style = $window.Resources['AppsPanelCheckBoxStyle']
|
||||||
|
|
||||||
@@ -798,9 +790,9 @@ function Show-MainWindow {
|
|||||||
[System.Windows.Controls.Grid]::SetColumn($tbDesc, 2)
|
[System.Windows.Controls.Grid]::SetColumn($tbDesc, 2)
|
||||||
|
|
||||||
$tbId = New-Object System.Windows.Controls.TextBlock
|
$tbId = New-Object System.Windows.Controls.TextBlock
|
||||||
$tbId.Text = $app.AppId
|
$tbId.Text = $app.AppIdDisplay
|
||||||
$tbId.Style = $window.Resources['AppIdTextStyle']
|
$tbId.Style = $window.Resources["AppIdTextStyle"]
|
||||||
$tbId.ToolTip = $app.AppId
|
$tbId.ToolTip = $app.AppIdDisplay
|
||||||
[System.Windows.Controls.Grid]::SetColumn($tbId, 3)
|
[System.Windows.Controls.Grid]::SetColumn($tbId, 3)
|
||||||
|
|
||||||
$row.Children.Add($dot) | Out-Null
|
$row.Children.Add($dot) | Out-Null
|
||||||
@@ -812,6 +804,8 @@ function Show-MainWindow {
|
|||||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppName' -Value $app.FriendlyName
|
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppName' -Value $app.FriendlyName
|
||||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppDescription' -Value $app.Description
|
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppDescription' -Value $app.Description
|
||||||
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'SelectedByDefault' -Value $app.SelectedByDefault
|
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'SelectedByDefault' -Value $app.SelectedByDefault
|
||||||
|
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIds' -Value @($app.AppId)
|
||||||
|
Add-Member -InputObject $checkbox -MemberType NoteProperty -Name 'AppIdDisplay' -Value $app.AppIdDisplay
|
||||||
|
|
||||||
$checkbox.Add_Checked({ UpdateAppSelectionStatus })
|
$checkbox.Add_Checked({ UpdateAppSelectionStatus })
|
||||||
$checkbox.Add_Unchecked({ UpdateAppSelectionStatus })
|
$checkbox.Add_Unchecked({ UpdateAppSelectionStatus })
|
||||||
@@ -1537,9 +1531,10 @@ function Show-MainWindow {
|
|||||||
$selectedApps = @()
|
$selectedApps = @()
|
||||||
foreach ($child in $appsPanel.Children) {
|
foreach ($child in $appsPanel.Children) {
|
||||||
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
|
if ($child -is [System.Windows.Controls.CheckBox] -and $child.IsChecked) {
|
||||||
$selectedApps += $child.Tag
|
$selectedApps += @($child.AppIds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$selectedApps = @($selectedApps | Where-Object { $_ } | Select-Object -Unique)
|
||||||
|
|
||||||
if ($selectedApps.Count -gt 0) {
|
if ($selectedApps.Count -gt 0) {
|
||||||
# Check if Microsoft Store is selected
|
# Check if Microsoft Store is selected
|
||||||
@@ -1760,7 +1755,7 @@ function Show-MainWindow {
|
|||||||
if ($script:UpdatingPresets) { return }
|
if ($script:UpdatingPresets) { return }
|
||||||
$check = ($this.IsChecked -eq $true)
|
$check = ($this.IsChecked -eq $true)
|
||||||
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
|
if ($this.IsChecked -eq $null) { $this.IsChecked = $false; $check = $false }
|
||||||
ApplyPresetToApps -MatchFilter { param($c) $script:SavedAppIds -contains $c.Tag } -Check $check
|
ApplyPresetToApps -MatchFilter { param($c) (@($c.AppIds) | Where-Object { $script:SavedAppIds -contains $_ }).Count -gt 0 } -Check $check
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
Reference in New Issue
Block a user