diff --git a/functions/public/Invoke-WPFButton.ps1 b/functions/public/Invoke-WPFButton.ps1 index b90c63d3..32903468 100644 --- a/functions/public/Invoke-WPFButton.ps1 +++ b/functions/public/Invoke-WPFButton.ps1 @@ -34,7 +34,7 @@ function Invoke-WPFButton { "WPFRemoveUltPerf" {Invoke-WPFUltimatePerformance -State "Disable"} "WPFundoall" {Invoke-WPFundoall} "WPFFeatureInstall" {Invoke-WPFFeatureInstall} - "WPFPanelDISM" {Invoke-WPFPanelDISM} + "WPFPanelDISM" {Invoke-WPFSystemRepair} "WPFPanelAutologin" {Invoke-WPFPanelAutologin} "WPFPanelcontrol" {Invoke-WPFControlPanel -Panel $button} "WPFPanelnetwork" {Invoke-WPFControlPanel -Panel $button} diff --git a/functions/public/Invoke-WPFFixesUpdate.ps1 b/functions/public/Invoke-WPFFixesUpdate.ps1 index 85beac60..a703ef65 100644 --- a/functions/public/Invoke-WPFFixesUpdate.ps1 +++ b/functions/public/Invoke-WPFFixesUpdate.ps1 @@ -6,19 +6,7 @@ function Invoke-WPFFixesUpdate { Performs various tasks in an attempt to repair Windows Update .DESCRIPTION - 1. (Aggressive Only) Scans the system for corruption using chkdsk, SFC, and DISM - Steps: - 1. Runs chkdsk /scan /perf - /scan - Runs an online scan on the volume - /perf - Uses more system resources to complete a scan as fast as possible - 2. Runs SFC /scannow - /scannow - Scans integrity of all protected system files and repairs files with problems when possible - 3. Runs DISM /Online /Cleanup-Image /RestoreHealth - /Online - Targets the running operating system - /Cleanup-Image - Performs cleanup and recovery operations on the image - /RestoreHealth - Scans the image for component store corruption and attempts to repair the corruption using Windows Update - 4. Runs SFC /scannow - Ran twice in case DISM repaired SFC + 1. (Aggressive Only) Scans the system for corruption using the Invoke-WPFSystemRepair function 2. Stops Windows Update Services 3. Remove the QMGR Data file, which stores BITS jobs 4. (Aggressive Only) Renames the DataStore and CatRoot2 folders @@ -46,104 +34,7 @@ function Invoke-WPFFixesUpdate { Start-Sleep -Milliseconds 200 if ($Aggressive) { - # Scan system for corruption - Write-Progress -Id 0 -Activity "Repairing Windows Update" -Status "Scanning for corruption..." -PercentComplete 0 - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running chkdsk..." -PercentComplete 0 - # 2>&1 redirects stdout, alowing iteration over the output - chkdsk.exe /scan /perf 2>&1 | ForEach-Object { - # Write stdout to the Verbose stream - Write-Verbose $_ - - # Get the index of the total percentage - $index = $_.IndexOf("Total:") - if ( - # If the percent is found - ($percent = try {( - $_.Substring( - $index + 6, - $_.IndexOf("%", $index) - $index - 6 - ) - ).Trim()} catch {0}) ` - <# And the current percentage is greater than the previous one #>` - -and $percent -gt $oldpercent - ) { - # Update the progress bar - $oldpercent = $percent - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running chkdsk... ($percent%)" -PercentComplete $percent - } - } - - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running SFC..." -PercentComplete 0 - $oldpercent = 0 - # SFC has a bug when redirected which causes it to output only when the stdout buffer is full, causing the progress bar to move in chunks - sfc /scannow 2>&1 | ForEach-Object { - # Write stdout to the Verbose stream - Write-Verbose $_ - - # Filter for lines that contain a percentage that is greater than the previous one - if ( - ( - # Use a different method to get the percentage that accounts for SFC's Unicode output - [int]$percent = try {( - ( - $_.Substring( - $_.IndexOf("n") + 2, - $_.IndexOf("%") - $_.IndexOf("n") - 2 - ).ToCharArray() | Where-Object {$_} - ) -join '' - ).TrimStart()} catch {0} - ) -and $percent -gt $oldpercent - ) { - # Update the progress bar - $oldpercent = $percent - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running SFC... ($percent%)" -PercentComplete $percent - } - } - - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running DISM..." -PercentComplete 0 - $oldpercent = 0 - DISM /Online /Cleanup-Image /RestoreHealth | ForEach-Object { - # Write stdout to the Verbose stream - Write-Verbose $_ - - # Filter for lines that contain a percentage that is greater than the previous one - if ( - ($percent = try { - [int]($_ -replace "\[" -replace "=" -replace " " -replace "%" -replace "\]") - } catch {0}) ` - -and $percent -gt $oldpercent - ) { - # Update the progress bar - $oldpercent = $percent - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running DISM... ($percent%)" -PercentComplete $percent - } - } - - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running SFC again..." -PercentComplete 0 - $oldpercent = 0 - sfc /scannow 2>&1 | ForEach-Object { - # Write stdout to the Verbose stream - Write-Verbose $_ - - # Filter for lines that contain a percentage that is greater than the previous one - if ( - ( - [int]$percent = try {( - ( - $_.Substring( - $_.IndexOf("n") + 2, - $_.IndexOf("%") - $_.IndexOf("n") - 2 - ).ToCharArray() | Where-Object {$_} - ) -join '' - ).TrimStart()} catch {0} - ) -and $percent -gt $oldpercent - ) { - # Update the progress bar - $oldpercent = $percent - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running SFC... ($percent%)" -PercentComplete $percent - } - } - Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Completed" -PercentComplete 100 + Invoke-WPFSystemRepair } diff --git a/functions/public/Invoke-WPFPanelDISM.ps1 b/functions/public/Invoke-WPFPanelDISM.ps1 deleted file mode 100644 index 31b36364..00000000 --- a/functions/public/Invoke-WPFPanelDISM.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -function Invoke-WPFPanelDISM { - <# - - .SYNOPSIS - Checks for system corruption using Chkdsk, SFC, and DISM - - .DESCRIPTION - 1. Chkdsk - Fixes disk and filesystem corruption - 2. SFC Run 1 - Fixes system file corruption, and fixes DISM if it was corrupted - 3. DISM - Fixes system image corruption, and fixes SFC's system image if it was corrupted - 4. SFC Run 2 - Fixes system file corruption, this time with an almost guaranteed uncorrupted system image - - .NOTES - Command Arguments: - 1. Chkdsk - /Scan - Runs an online scan on the system drive, attempts to fix any corruption, and queues other corruption for fixing on reboot - 2. SFC - /ScanNow - Performs a scan of the system files and fixes any corruption - 3. DISM - Fixes system image corruption, and fixes SFC's system image if it was corrupted - /Online - Fixes the currently running system image - /Cleanup-Image - Performs cleanup operations on the image, could remove some unneeded temporary files - /Restorehealth - Performs a scan of the image and fixes any corruption - - #> - Start-Process PowerShell -ArgumentList "Write-Host '(1/4) Chkdsk' -ForegroundColor Green; Chkdsk /scan; - Write-Host '`n(2/4) SFC - 1st scan' -ForegroundColor Green; sfc /scannow; - Write-Host '`n(3/4) DISM' -ForegroundColor Green; DISM /Online /Cleanup-Image /Restorehealth; - Write-Host '`n(4/4) SFC - 2nd scan' -ForegroundColor Green; sfc /scannow; - Read-Host '`nPress Enter to Continue'" -verb runas -} diff --git a/functions/public/Invoke-WPFSystemRepair.ps1 b/functions/public/Invoke-WPFSystemRepair.ps1 new file mode 100644 index 00000000..64476ed3 --- /dev/null +++ b/functions/public/Invoke-WPFSystemRepair.ps1 @@ -0,0 +1,165 @@ +function Invoke-WPFSystemRepair { + <# + + .SYNOPSIS + Checks for system corruption using Chkdsk, SFC, and DISM + + .DESCRIPTION + 1. Chkdsk - Fixes disk and filesystem corruption + 2. SFC Run 1 - Fixes system file corruption, and fixes DISM if it was corrupted + 3. DISM - Fixes system image corruption, and fixes SFC's system image if it was corrupted + 4. SFC Run 2 - Fixes system file corruption, this time with an almost guaranteed uncorrupted system image + #> + + Write-Progress -Id 0 -Activity "Repairing Windows" -PercentComplete 0 + # Wait for the first progress bar to show, otherwise the second one won't show + Start-Sleep -Milliseconds 200 + + function run_chkdsk { + <# + .SYNOPSIS + Runs chkdsk on the system drive + .DESCRIPTION + Chkdsk /Scan - Runs an online scan on the system drive, attempts to fix any corruption, and queues other corruption for fixing on reboot + .PARAMETER verbose + If specified, print output from chkdsk + .NOTES + VerbosePreference is defined locally, so it only affects this function. This is done by wrapping the code inside a script block and calling it with & { ... } + #> + param( + [switch]$verbose + ) + & { + if ($verbose) { + $VerbosePreference = "Continue" + } + else { + $VerbosePreference = "SilentlyContinue" + } + + Write-Progress -Id 1 -Activity "Scanning for corruption" -Status "Running chkdsk..." -PercentComplete 0 + $oldpercent = 0 + # 2>&1 redirects stdout, allowing iteration over the output + chkdsk.exe /scan /perf 2>&1 | ForEach-Object { + Write-Verbose $_ + # Regex to match the total percentage regardless of windows locale (it's always the second percentage in the status output) + if ($_ -match "%.*?(\d+)%") { + [int]$percent = $matches[1] + if ($percent -gt $oldpercent) { + Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running chkdsk... ($percent%)" -PercentComplete $percent + $oldpercent = $percent + } + } + } + Write-Progress -Id 1 -Activity "Scanning for corruption" -Status "chkdsk Completed" -PercentComplete 100 + } + } + + function run_sfc { + <# + .SYNOPSIS + Runs sfc on the system drive + .DESCRIPTION + SFC /ScanNow - Performs a scan of the system files and fixes any corruption + .PARAMETER verbose + If specified, print output from sfc + .NOTES + VerbosePreference and ErrorPreference is defined locally, so it only affects this function. This is done by wrapping the code inside a script block and calling it with & { ... } + #> + param( + [switch]$verbose + ) + + & { + if ($verbose) { + $VerbosePreference = "Continue" + } + else { + $VerbosePreference = "SilentlyContinue" + } + $ErrorActionPreference = "SilentlyContinue" + Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running SFC..." -PercentComplete 0 + $oldpercent = 0 + # SFC has a bug when redirected which causes it to output only when the stdout buffer is full, causing the progress bar to move in chunks + sfc.exe /scannow 2>&1 | ForEach-Object { + Write-Verbose $_ + if ($_ -ne "") { + # sfc.exe /scannow outputs unicode characters, so we directly remove null characters for optimization + $utf8line = $_ -replace "`0", "" + if ($utf8line -match "(\d+)\s%") { + # Write-Host "$($matches[0]) $($matches[1])" + [int]$percent = $matches[1] + if ($percent -gt $oldpercent) { + Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running SFC... ($percent%)" -PercentComplete $percent + $oldpercent = $percent + } + } + } + } + Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "SFC Completed" -PercentComplete 100 + } + } + + function run_dism { + <# + .SYNOPSIS + Runs DISM on the system drive + .DESCRIPTION + DISM - Fixes system image corruption, and fixes SFC's system image if it was corrupted + /Online - Fixes the currently running system image + /Cleanup-Image - Performs cleanup operations on the image, could remove some unneeded temporary files + /Restorehealth - Performs a scan of the image and fixes any corruption + + .PARAMETER verbose + If specified, print output from DISM + .NOTES + VerbosePreference is defined locally, so it only affects this function. This is done by wrapping the code inside a script block and calling it with & { ... } + #> + param( + [switch]$verbose + ) + & { + if ($verbose) { + $VerbosePreference = "Continue" + } + else { + $VerbosePreference = "SilentlyContinue" + } + + Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running DISM..." -PercentComplete 0 + $oldpercent = 0 + DISM /Online /Cleanup-Image /RestoreHealth | ForEach-Object { + Write-Verbose $_ + + # Filter for lines that contain a percentage that is greater than the previous one + if ($_ -match "(\d+)[.,]\d+%") { + [int]$percent = $matches[1] + if ($percent -gt $oldpercent) { + # Update the progress bar + Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "Running DISM... ($percent%)" -PercentComplete $percent + $oldpercent = $percent + } + } + } + Write-Progress -Id 1 -ParentId 0 -Activity "Scanning for corruption" -Status "DISM Completed" -PercentComplete 100 + } + } + + # Scan system for corruption + Write-Progress -Id 0 -Activity "Repairing Windows" -Status "Scanning for corruption... " -PercentComplete 0 + # Step 1: Run chkdsk to fix disk and filesystem corruption before proceeding with system file repairs + run_chkdsk + Write-Progress -Id 0 -Activity "Repairing Windows" -Status "Scanning for corruption... (25%)" -PercentComplete 25 + + # Step 2: Run SFC to fix system file corruption and ensure DISM can operate correctly + run_sfc + Write-Progress -Id 0 -Activity "Repairing Windows" -Status "Scanning for corruption... (50%)" -PercentComplete 50 + + # Step 3: Run DISM to repair the system image, which SFC relies on for accurate repairs + run_dism + Write-Progress -Id 0 -Activity "Repairing Windows" -Status "Scanning for corruption... (75%)" -PercentComplete 75 + + # Step 4: Run SFC again to ensure system files are repaired using the now-fixed system image + run_sfc + Write-Progress -Id 0 -Activity "Repairing Windows" -Status "Scanning for corruption completed" -PercentComplete 100 +} \ No newline at end of file