diff --git a/Scripts/AppRemoval/RemoveApps.ps1 b/Scripts/AppRemoval/RemoveApps.ps1 index 3904a8f..3ebb9a7 100644 --- a/Scripts/AppRemoval/RemoveApps.ps1 +++ b/Scripts/AppRemoval/RemoveApps.ps1 @@ -5,11 +5,10 @@ .DESCRIPTION Iterates over the provided list of app identifiers and removes each one. The removal method (winget vs. Appx cmdlets) is determined per-app from - Apps.json. Microsoft Edge is deferred to the end of the loop so that all - winget attempts run before any force-remove prompt. A scheduled task is - only created when the User or Sysprep parameter was passed. - After each winget removal, the system is checked to confirm whether the - app is still installed before reporting an error. + Apps.json. A scheduled task is only created when the User or Sysprep + parameter was passed. After winget removal, the system is checked to + confirm whether the app is still installed before reporting an error. + Returns early if the CancelRequested flag is set. .PARAMETER appsList An array of app package identifiers to remove (e.g. 'Microsoft.BingNews'). @@ -39,7 +38,6 @@ function RemoveApps { $appIndex = 0 $edgeIds = @('Microsoft.Edge', 'XPFFTQ037JWMHS') - $edgeAppsInList = @() $wingetRemovedApps = @() Foreach ($app in $appsList) { @@ -51,12 +49,6 @@ function RemoveApps { & $script:ApplySubStepCallback "Removing apps ($appIndex/$appCount)" $appIndex $appCount } - # Microsoft Edge is handled after the loop to avoid duplicate scheduled tasks and allow fallback if winget fails - if ($edgeIds -contains $app) { - $edgeAppsInList += $app - continue - } - Write-Host "Removing $app" if ((Get-AppRemovalMethod $app) -eq 'WinGet') { @@ -68,32 +60,32 @@ function RemoveApps { } } - # Remove Microsoft Edge - if ($edgeAppsInList.Count -gt 0) { - Remove-EdgeApp -edgeAppsInList $edgeAppsInList + if ($script:CancelRequested) { + Write-Host "" + return } # Check whether any winget-removed apps are still present, and report errors for each one. - if ($wingetRemovedApps.Count -gt 0 -or $edgeAppsInList.Count -gt 0) { + if ($wingetRemovedApps.Count -gt 0) { $postRemovalList = if ($script:WingetInstalled) { GetInstalledAppsViaWinget -TimeOut 10 -NonBlocking } else { $null } + $edgeForceRemoveRequested = $false + foreach ($app in $wingetRemovedApps) { - if (Test-AppStillInstalled -appId $app -InstalledList $postRemovalList) { + if (-not (Test-AppStillInstalled -appId $app -InstalledList $postRemovalList)) { + continue + } + + if ($edgeIds -contains $app) { + Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red + if (-not $edgeForceRemoveRequested) { + Request-EdgeForceRemove + $edgeForceRemoveRequested = $true + } + } + else { Write-Host "Unable to uninstall $app via WinGet" -ForegroundColor Red } } - - # Verify Edge separately (triggers its own force-remove path if still installed) - $edgeStillInstalled = $false - foreach ($edgeApp in $edgeAppsInList) { - if (Test-AppStillInstalled -appId $edgeApp -InstalledList $postRemovalList) { - $edgeStillInstalled = $true - break - } - } - if ($edgeStillInstalled) { - Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red - Request-EdgeForceRemove - } } Write-Host "" @@ -101,15 +93,11 @@ function RemoveApps { <# .SYNOPSIS - Uninstalls a non-Edge app via WinGet and/or schedules its removal. + Uninstalls an app via WinGet and/or schedules its removal. .DESCRIPTION Runs winget uninstall for a single app. If the User or Sysprep parameter was passed, also schedules removal for future logins. - After uninstall, the system is checked to confirm whether the app - is still present — winget output is not trusted on its - own, as it sometimes reports failure after a successful removal. - Edge apps are handled separately after the main loop. .PARAMETER app The WinGet package ID to uninstall (e.g. 'Microsoft.BingNews'). @@ -122,6 +110,11 @@ function Remove-WinGetApp { return } + Invoke-NonBlocking -ScriptBlock { + param($appId) + winget uninstall --accept-source-agreements --disable-interactivity --id $appId + } -ArgumentList $app + if ($script:Params.ContainsKey("User")) { Write-Host "Adding scheduled task to uninstall $app for user $(GetUserName)..." Set-RunOnceWingetTask -appId $app @@ -130,51 +123,6 @@ function Remove-WinGetApp { Write-Host "Adding scheduled task to uninstall $app for new users..." Set-RunOnceWingetTask -appId $app } - - Invoke-NonBlocking -ScriptBlock { - param($appId) - winget uninstall --accept-source-agreements --disable-interactivity --id $appId - } -ArgumentList $app -} - -<# - .SYNOPSIS - Removes Microsoft Edge via WinGet (both AppIds), with fallback to force-remove. - - .DESCRIPTION - Edge has multiple package IDs. Runs winget uninstall for each one, - then creates a single scheduled task if the User or Sysprep parameter - was passed. After all attempts, the system is checked to confirm - whether Edge is still present. The force-remove prompt only - appears if Edge remains installed — winget false positives are ignored. - - .PARAMETER edgeAppsInList - The Edge AppIds that appear in the removal list (one or both). -#> -function Remove-EdgeApp { - param([string[]]$edgeAppsInList) - - if (-not $script:WingetInstalled) { - Write-Host "ERROR: WinGet is either not installed or is outdated, Microsoft Edge could not be removed" -ForegroundColor Red - return - } - - if ($script:Params.ContainsKey("User")) { - Write-Host "Adding scheduled task to uninstall Microsoft Edge for user $(GetUserName)..." - Set-RunOnceWingetTask -appId 'Microsoft.Edge' - } - elseif ($script:Params.ContainsKey("Sysprep")) { - Write-Host "Adding scheduled task to uninstall Microsoft Edge for new users..." - Set-RunOnceWingetTask -appId 'Microsoft.Edge' - } - - foreach ($edgeApp in $edgeAppsInList) { - Write-Host "Removing $edgeApp" - Invoke-NonBlocking -ScriptBlock { - param($appId) - winget uninstall --accept-source-agreements --disable-interactivity --id $appId - } -ArgumentList $edgeApp - } } <# @@ -389,4 +337,4 @@ function Set-RunOnceWingetTask { catch { Write-Host "Failed to schedule uninstall task for $($appId): $_" -ForegroundColor Red } -} \ No newline at end of file +} diff --git a/Scripts/Features/TelemetryScheduledTasks.ps1 b/Scripts/Features/TelemetryScheduledTasks.ps1 index d780fd8..41b9eed 100644 --- a/Scripts/Features/TelemetryScheduledTasks.ps1 +++ b/Scripts/Features/TelemetryScheduledTasks.ps1 @@ -40,6 +40,8 @@ function Disable-TelemetryScheduledTasks { $tasks = Get-TelemetryScheduledTasks foreach ($task in $tasks) { + if ($script:CancelRequested) { return } + if ($script:Params.ContainsKey("WhatIf")) { Write-Host "[WhatIf] Disable Scheduled Task: $($task.Path)$($task.Name)" -ForegroundColor Cyan continue @@ -92,6 +94,8 @@ function Enable-TelemetryScheduledTasks { $tasks = Get-TelemetryScheduledTasks foreach ($task in $tasks) { + if ($script:CancelRequested) { return } + if ($script:Params.ContainsKey("WhatIf")) { Write-Host "[WhatIf] Enable Scheduled Task: $($task.Path)$($task.Name)" -ForegroundColor Cyan continue diff --git a/Win11Debloat.ps1 b/Win11Debloat.ps1 index 11a27be..7c18042 100644 --- a/Win11Debloat.ps1 +++ b/Win11Debloat.ps1 @@ -563,6 +563,10 @@ if (($controlParamsCount -eq $script:Params.Keys.Count) -or ($script:Params.Keys # (This also handles restore point creation if requested) Invoke-AllChanges +if ($script:CancelRequested) { + Write-Warning "Script execution was cancelled by the user. Any remaining changes were not applied." + AwaitKeyToExit +} # Restart Explorer process unless running in Sysprep or User context if (-not ($script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User"))) {