From 718c21ce6d63ff192bc312e583bdd168a41e19cc Mon Sep 17 00:00:00 2001 From: "shaun.greatbatch" Date: Fri, 19 Sep 2025 16:16:44 +0100 Subject: [PATCH] =?UTF-8?q?remove=20nested=20helper=20and=20use=20single?= =?UTF-8?q?=20locale-safe=20parser=20-=20Delete=20inline=20Invoke-DismWith?= =?UTF-8?q?Progress=20inside=20Invoke-WindowsUpdateRepair=20-=20Keep=20one?= =?UTF-8?q?=20global=20helper=20with=20integer-only=20%=20parsing=20(handl?= =?UTF-8?q?es=20=E2=80=9C4.9%=E2=80=9D,=20=E2=80=9C4,9%=E2=80=9D)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SRGAdminMenu.ps1 | 105 +++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/SRGAdminMenu.ps1 b/SRGAdminMenu.ps1 index 059afd6..7da0d87 100644 --- a/SRGAdminMenu.ps1 +++ b/SRGAdminMenu.ps1 @@ -125,53 +125,17 @@ function Remove-ProvisionedAndExisting { function Invoke-WindowsUpdateRepair { param([string]$Computer) - $sb = { + $isLocal = [string]::IsNullOrWhiteSpace($Computer) -or $Computer -in @('localhost','127.0.0.1','.') + + if ($isLocal) { + # ==== LOCAL run (with real progress) ==== function Test-Admin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object Security.Principal.WindowsPrincipal($id) - $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } - if (-not (Test-Admin)) { throw "You must run this elevated (Administrator)." } - # Local helper for progress (only available when running locally) - function Invoke-DismWithProgress { - param([string[]]$Arguments) - $ts = Get-Date -Format 'yyyyMMdd_HHmmss' - $outf = Join-Path $env:TEMP "dism_out_$ts.txt" - $errf = Join-Path $env:TEMP "dism_err_$ts.txt" - if (-not ($Arguments -match '/LogPath:')) { - $logFile = Join-Path $env:TEMP "dism_log_$ts.log" - $Arguments += "/LogPath:$logFile" - } - $p = Start-Process dism.exe -ArgumentList ($Arguments -join ' ') -NoNewWindow -RedirectStandardOutput $outf -RedirectStandardError $errf -PassThru - $lastPct = -1 - while (-not $p.HasExited) { - if (Test-Path $outf) { - $content = Get-Content $outf -Raw -ErrorAction SilentlyContinue - if ($content) { - $m = [regex]::Matches($content, '(\d{1,3}(?:\.\d+)?)%') - if ($m.Count -gt 0) { - $pct = [int][math]::Min(100, [double]$m[$m.Count-1].Value) - if ($pct -ne $lastPct) { - Write-Progress -Activity "DISM $($Arguments -join ' ')" -Status "$pct%" -PercentComplete $pct - $lastPct = $pct - } - } else { - Write-Progress -Activity "DISM $($Arguments -join ' ')" -Status "Working..." -PercentComplete 0 - } - } - } - Start-Sleep -Milliseconds 400 - } - Write-Progress -Activity "DISM $($Arguments -join ' ')" -Completed - if ($p.ExitCode -ne 0) { - Write-Warning "DISM exited with code $($p.ExitCode)" - if (Test-Path $errf) { Get-Content $errf -Tail 25 | Write-Warning } - } - return $p.ExitCode - } - Write-Host "=== Step 1: DISM (ScanHealth) ===" -ForegroundColor Cyan $e1 = Invoke-DismWithProgress -Arguments @('/Online','/Cleanup-Image','/ScanHealth') if ($e1 -ne 0) { throw "DISM ScanHealth failed with code $e1" } @@ -190,8 +154,8 @@ function Invoke-WindowsUpdateRepair { $sd = Join-Path $env:SystemRoot 'SoftwareDistribution' $cat = Join-Path $env:SystemRoot 'System32\catroot2' $ts = Get-Date -Format 'yyyyMMdd_HHmmss' - if (Test-Path $sd) { try { Rename-Item $sd ("SoftwareDistribution.bak_$ts") -ErrorAction Stop } catch { } } - if (Test-Path $cat) { try { Rename-Item $cat ("catroot2.bak_$ts") -ErrorAction Stop } catch { } } + if (Test-Path $sd) { try { Rename-Item -Path $sd -NewName ("SoftwareDistribution.bak_$ts") -ErrorAction Stop } catch { } } + if (Test-Path $cat) { try { Rename-Item -Path $cat -NewName ("catroot2.bak_$ts") -ErrorAction Stop } catch { } } foreach ($s in $services) { try { Start-Service -Name $s -ErrorAction Stop } catch { } } Write-Host "`n=== Step 4: Trigger update scan ===" -ForegroundColor Cyan @@ -204,16 +168,51 @@ function Invoke-WindowsUpdateRepair { )) { try { & $cmd | Out-Null } catch { } } Write-Host "`nWindows Update repair sequence completed." -ForegroundColor Green + return } - if ([string]::IsNullOrWhiteSpace($Computer) -or $Computer -in @('localhost','127.0.0.1','.')) { - & $sb - } else { - # Over remoting, Write-Progress doesn't render well—still run the repair, - # but you'll see normal text output. (If you want, we can add a remote-side - # log tail that writes periodic progress messages.) - Invoke-Remote -Computer $Computer -ScriptBlock $sb + # ==== REMOTE run ==== + $sb = { + function Test-Admin { + $id = [Security.Principal.WindowsIdentity]::GetCurrent() + $p = New-Object Security.Principal.WindowsPrincipal($id) + return $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + } + if (-not (Test-Admin)) { throw "You must run this elevated (Administrator)." } + + Write-Host "=== Step 1: DISM (ScanHealth) ===" + & dism.exe /Online /Cleanup-Image /ScanHealth | Out-Host + + Write-Host "`n=== Step 1b: DISM (RestoreHealth) ===" + & dism.exe /Online /Cleanup-Image /RestoreHealth /NoRestart | Out-Host + + Write-Host "`n=== Step 2: SFC ===" + & sfc.exe /scannow | Out-Host + + Write-Host "`n=== Step 3: Reset Windows Update components ===" + $services = 'wuauserv','bits','cryptsvc','msiserver' + foreach ($s in $services) { try { Stop-Service -Name $s -Force -ErrorAction Stop } catch { } } + + $sd = Join-Path $env:SystemRoot 'SoftwareDistribution' + $cat = Join-Path $env:SystemRoot 'System32\catroot2' + $ts = Get-Date -Format 'yyyyMMdd_HHmmss' + if (Test-Path $sd) { try { Rename-Item -Path $sd -NewName ("SoftwareDistribution.bak_$ts") -ErrorAction Stop } catch { } } + if (Test-Path $cat) { try { Rename-Item -Path $cat -NewName ("catroot2.bak_$ts") -ErrorAction Stop } catch { } } + foreach ($s in $services) { try { Start-Service -Name $s -ErrorAction Stop } catch { } } + + Write-Host "`n=== Step 4: Trigger update scan ===" + foreach ($cmd in @( + { & usoclient.exe StartScan }, + { & usoclient.exe StartDownload }, + { & usoclient.exe StartInstall }, + { & wuauclt.exe /detectnow }, + { & wuauclt.exe /reportnow } + )) { try { & $cmd | Out-Null } catch { } } + + Write-Host "`nWindows Update repair sequence completed." } + + Invoke-Remote -Computer $Computer -ScriptBlock $sb } function Invoke-DismWithProgress { @@ -247,11 +246,11 @@ function Invoke-DismWithProgress { if (Test-Path $outf) { $content = Get-Content -Path $outf -Raw -ErrorAction SilentlyContinue if ($content) { - # Capture "NN", optionally followed by decimal with . or , then % - # Examples matched: "4.9%", "4,9 %", "62%" -> group 1 = integer part + # Match "NN", optional decimal with . or , then %, allowing whitespace + # Examples: "4.9%", "4,9 %", "62%" $m = [regex]::Matches($content, '(\d{1,3})(?:[.,]\d+)?\s*%') if ($m.Count -gt 0) { - $pctText = $m[$m.Count-1].Groups[1].Value + $pctText = $m[$m.Count-1].Groups[1].Value # integer part only $pct = 0 [void][int]::TryParse($pctText, [ref]$pct) if ($pct -gt 100) { $pct = 100 }