11<# PSScriptInfo
2- .VERSION 2.10
2+ .VERSION 2.11
33.GUID 8f7c3b2a-1d4e-5f6a-9b8c-0d1e2f3a4b5c
44.AUTHOR bivlked
55.COMPANYNAME
1212.REQUIREDSCRIPTS
1313.EXTERNALSCRIPTDEPENDENCIES
1414.RELEASENOTES
15+ v2.11: Added timeouts for winget/DISM operations, fixed version display, improved reliability
1516 v2.10: Added auto-update check at startup (checks PSGallery for new version)
1617 v2.9: Fixed PSWindowsUpdate installation hanging (TLS 1.2, timeouts)
1718 v2.8: Fixed Disk Cleanup timeout issues
18- v2.7: UI improvements for final statistics
1919.PRIVATEDATA
2020#>
2121
2222<#
2323. SYNOPSIS
24- WinClean - Ultimate Windows 11 Maintenance Script v2.10
24+ WinClean - Ultimate Windows 11 Maintenance Script v2.11
2525. DESCRIPTION
2626 Комплексный скрипт для обновления и очистки Windows 11:
2727 - Обновление Windows (включая драйверы)
3535 - Подробный цветной вывод + лог-файл
3636. NOTES
3737 Author: biv
38- Version: 2.10
38+ Version: 2.11
3939 Requires: PowerShell 7.1+, Windows 11, Administrator rights
40+ Changes in 2.11:
41+ - Fixed version display bugs (banner and log showed v2.9 instead of current version)
42+ - Added timeouts for winget/DISM operations to prevent script hangs
43+ - Added force stop for Storage Sense when timeout exceeded
44+ - Improved Docker detection and browser cache statistics reliability
4045 Changes in 2.10:
4146 - Added auto-update check: script checks PSGallery for newer version at startup
4247 - Added Test-ScriptUpdate function: compares local version with PSGallery
@@ -205,7 +210,7 @@ if (-not $LogPath) {
205210}
206211
207212# Script version (single source of truth for version checking)
208- $script :Version = " 2.10 "
213+ $script :Version = " 2.11 "
209214
210215# Protected paths that should never be deleted
211216$script :ProtectedPaths = @ (
@@ -942,7 +947,8 @@ function New-SystemRestorePoint {
942947 }
943948"@
944949
945- $result = & " $env: SystemRoot \System32\WindowsPowerShell\v1.0\powershell.exe" `
950+ # Use Windows PowerShell 5.1 (Checkpoint-Computer not available in PS7)
951+ $result = & powershell.exe `
946952 - NoProfile - NoLogo - ExecutionPolicy Bypass - Command $scriptBlock 2>&1
947953
948954 if ($result -like " SUCCESS*" ) {
@@ -1014,7 +1020,7 @@ function Update-WindowsSystem {
10141020 # Clear any lingering progress bar before module installation
10151021 Write-Progress - Activity " Windows Update" - Completed - ErrorAction SilentlyContinue
10161022
1017- # Check PowerShell Gallery availability first (v2.9)
1023+ # Check PowerShell Gallery availability first
10181024 Write-Log " Checking PowerShell Gallery availability..." - Level INFO
10191025 if (-not (Test-PSGalleryConnection )) {
10201026 Write-Log " PowerShell Gallery is unavailable" - Level ERROR
@@ -1026,7 +1032,7 @@ function Update-WindowsSystem {
10261032
10271033 Write-Log " Installing PSWindowsUpdate module..." - Level INFO
10281034
1029- # Ensure NuGet provider with timeout (v2.9)
1035+ # Ensure NuGet provider with timeout
10301036 $nuget = Get-PackageProvider - Name NuGet - ErrorAction SilentlyContinue
10311037 if (-not $nuget -or $nuget.Version -lt [version ]" 2.8.5.201" ) {
10321038 Write-Log " Installing NuGet provider..." - Level INFO
@@ -1039,7 +1045,7 @@ function Update-WindowsSystem {
10391045 Write-Log " NuGet provider installed" - Level SUCCESS
10401046 }
10411047
1042- # Install PSWindowsUpdate module with timeout (v2.9)
1048+ # Install PSWindowsUpdate module with timeout
10431049 if (-not (Install-ModuleWithTimeout - ModuleName " PSWindowsUpdate" - TimeoutSeconds 120 )) {
10441050 Write-Log " Failed to install PSWindowsUpdate - Windows Update skipped" - Level ERROR
10451051 Write-Log " Try manual installation: Install-Module PSWindowsUpdate -Force -Scope CurrentUser" - Level INFO
@@ -1188,7 +1194,14 @@ function Update-Applications {
11881194 # Update sources only if not in ReportOnly mode (source update modifies state)
11891195 if (-not $ReportOnly ) {
11901196 Write-Log " Updating winget sources..." - Level INFO
1191- & $wingetPath source update 2>&1 | Out-Null
1197+ # Run with timeout to prevent hanging
1198+ $job = Start-Job - ScriptBlock { param ($path ) & $path source update 2>&1 } - ArgumentList $wingetPath
1199+ $completed = $job | Wait-Job - Timeout 120 # 2 minutes timeout
1200+ if (-not $completed ) {
1201+ $job | Stop-Job
1202+ Write-Log " Winget source update timed out" - Level WARNING
1203+ }
1204+ $job | Remove-Job - Force - ErrorAction SilentlyContinue
11921205 }
11931206
11941207 # Get available updates (use --include-unknown to match actual upgrade behavior)
@@ -1197,7 +1210,17 @@ function Update-Applications {
11971210 $tempFile = [System.IO.Path ]::GetTempFileName()
11981211 $tempErrorFile = [System.IO.Path ]::GetTempFileName()
11991212 $process = Start-Process - FilePath $wingetPath - ArgumentList " upgrade" , " --include-unknown" `
1200- - NoNewWindow - RedirectStandardOutput $tempFile - RedirectStandardError $tempErrorFile - PassThru - Wait
1213+ - NoNewWindow - RedirectStandardOutput $tempFile - RedirectStandardError $tempErrorFile - PassThru
1214+
1215+ # Wait with timeout (5 minutes for check operation)
1216+ $timeoutMs = 300000
1217+ if (-not $process.WaitForExit ($timeoutMs )) {
1218+ $process.Kill ()
1219+ Write-Log " Winget upgrade check timed out after 5 minutes" - Level WARNING
1220+ $script :Stats.WarningsCount ++
1221+ Remove-Item $tempFile , $tempErrorFile - Force - ErrorAction SilentlyContinue
1222+ return
1223+ }
12011224
12021225 $output = Get-Content $tempFile - Raw - Encoding UTF8 - ErrorAction SilentlyContinue
12031226 $errorOutput = Get-Content $tempErrorFile - Raw - Encoding UTF8 - ErrorAction SilentlyContinue
@@ -1267,7 +1290,16 @@ function Update-Applications {
12671290 )
12681291
12691292 $upgradeProcess = Start-Process - FilePath $wingetPath - ArgumentList $upgradeArgs `
1270- - NoNewWindow - PassThru - Wait
1293+ - NoNewWindow - PassThru
1294+
1295+ # Wait with timeout (20 minutes for upgrade operation - can take long with many updates)
1296+ $timeoutMs = 1200000
1297+ if (-not $upgradeProcess.WaitForExit ($timeoutMs )) {
1298+ $upgradeProcess.Kill ()
1299+ Write-Log " Winget upgrade timed out after 20 minutes" - Level WARNING
1300+ $script :Stats.WarningsCount ++
1301+ return
1302+ }
12711303
12721304 $script :Stats.AppUpdatesCount = $updateCount
12731305
@@ -1408,6 +1440,7 @@ function Clear-BrowserCaches {
14081440
14091441 # Measure size after cleanup to get actual freed space
14101442 $sizeAfter = ($allPaths | ForEach-Object { Get-FolderSize - Path $_.Path } | Measure-Object - Sum).Sum
1443+ $sizeAfter = [long ]($sizeAfter ?? 0 ) # Ensure non-null value
14111444 $freedSpace = $sizeBefore - $sizeAfter
14121445
14131446 # Update statistics with actual freed space (not estimated)
@@ -1476,7 +1509,7 @@ function Clear-WinCleanRecycleBin {
14761509 }
14771510
14781511 try {
1479- # Use full cmdlet path to avoid recursion (our function has same name as cmdlet)
1512+ # Use full cmdlet path to explicitly call the built-in cmdlet
14801513 Microsoft.PowerShell.Management\Clear-RecycleBin - Force - ErrorAction Stop
14811514 Write-Log " Recycle Bin emptied" - Level SUCCESS
14821515 } catch {
@@ -2028,8 +2061,11 @@ function Clear-DockerWSL {
20282061 $dockerRunning = $false
20292062 try {
20302063 $dockerInfo = docker info 2>&1
2031- $dockerRunning = $LASTEXITCODE -eq 0
2032- } catch { }
2064+ $exitCode = $LASTEXITCODE # Capture immediately after command
2065+ $dockerRunning = $exitCode -eq 0
2066+ } catch {
2067+ # Docker command failed to execute (not installed or path issue)
2068+ }
20332069
20342070 if ($dockerRunning ) {
20352071 if ($ReportOnly ) {
@@ -2250,7 +2286,16 @@ function Invoke-DISMCleanup {
22502286 try {
22512287 $dismProcess = Start-Process - FilePath " $env: SystemRoot \System32\Dism.exe" `
22522288 - ArgumentList " /Online" , " /Cleanup-Image" , " /StartComponentCleanup" , " /ResetBase" `
2253- - NoNewWindow - PassThru - Wait
2289+ - NoNewWindow - PassThru
2290+
2291+ # Wait with timeout (15 minutes for DISM operation)
2292+ $timeoutMs = 900000
2293+ if (-not $dismProcess.WaitForExit ($timeoutMs )) {
2294+ $dismProcess.Kill ()
2295+ Write-Log " DISM cleanup timed out after 15 minutes" - Level WARNING
2296+ $script :Stats.WarningsCount ++
2297+ return
2298+ }
22542299
22552300 switch ($dismProcess.ExitCode ) {
22562301 0 { Write-Log " DISM cleanup completed successfully" - Level SUCCESS }
@@ -2321,7 +2366,13 @@ function Invoke-StorageSense {
23212366 }
23222367
23232368 if ($elapsed -ge $timeout ) {
2324- Write-Log " Storage Sense timed out after $timeout seconds (may still be running)" - Level WARNING
2369+ Write-Log " Storage Sense timed out after $timeout seconds" - Level WARNING
2370+ # Force stop the task if still running
2371+ $task = Get-ScheduledTask - TaskPath $ssTaskPath - TaskName $ssTaskName - ErrorAction SilentlyContinue
2372+ if ($task -and $task.State -eq ' Running' ) {
2373+ Stop-ScheduledTask - TaskPath $ssTaskPath - TaskName $ssTaskName - ErrorAction SilentlyContinue
2374+ Write-Log " Storage Sense task stopped" - Level INFO
2375+ }
23252376 $script :Stats.WarningsCount ++
23262377 }
23272378 } else {
@@ -2398,7 +2449,7 @@ function Show-Banner {
23982449 ║ ╚██████╗███████╗███████╗██║ ██║██║ ╚████║ ║
23992450 ║ ╚═════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ║
24002451 ║ ║
2401- ║ Ultimate Windows 11 Maintenance Script v2.9 ║
2452+ ║ Ultimate Windows 11 Maintenance Script v $ ( $ script :Version ) ║
24022453 ║ ║
24032454 ╚══════════════════════════════════════════════════════════════════════╝
24042455
@@ -2573,7 +2624,7 @@ function Show-FinalStatistics {
25732624
25742625function Start-WinClean {
25752626 # Initialize log
2576- " WinClean v2.9 - Started at $ ( Get-Date ) " | Out-File - FilePath $script :LogPath - Encoding utf8
2627+ " WinClean v $ ( $ script :Version ) - Started at $ ( Get-Date ) " | Out-File - FilePath $script :LogPath - Encoding utf8
25772628 " =" * 70 | Out-File - FilePath $script :LogPath - Append - Encoding utf8
25782629
25792630 # Enable TLS 1.2 for all HTTPS connections (required by PowerShell Gallery, NuGet, etc.)
0 commit comments