commit 34dbb7f4f24508dfcaefeb223abb42901e5637d4 Author: Ryan Date: Tue Feb 17 05:33:00 2026 +0000 v1.0.0 - WinRepair Utility Initial upload of WinRepair in its current working, fully featured state! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c4d22c9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +WinRepair +Copyright (c) 2026 Ryan Harris / ZenDeuo. +All Rights Reserved. + +This License governs the use of the "WinRepair" software, including all associated scripts, files, and documentation (collectively, the "Software"). By downloading, accessing, or using the Software, you agree to be bound by the terms of this License. + +1. GRANT OF LICENSE +Subject to the restrictions in Section 2, the Author hereby grants you a limited, non-exclusive, non-transferable license to: +- Use: Run the Software for its intended purpose of Windows system repair and maintenance. +- Inspect: View and inspect the source code for educational purposes, transparency, and personal security audits. +- Share: Direct others to the official project repository or demonstrate the Software’s functionality to others. + +2. RESTRICTIONS +You are strictly prohibited from the following actions: +- Redistribution: You may NOT redistribute, sub-license, bundle, or host the Software or its source code on any other platform, website, or medium, in either source or binary form. +- Modification: You may NOT modify, alter, transform, or build upon the Software. Creating derivative works based on the Software is strictly prohibited. +- Commercial Use: You may NOT sell, rent, lease, or utilize the Software for any commercial gain or professional service for which you receive compensation. +- Authorship: You may NOT remove any copyright notices or claim authorship, ownership, or affiliation with the Software. + +3. OWNERSHIP +The Software is licensed, not sold. All title, ownership rights, and intellectual property rights in and to the Software (including but not limited to the PowerShell scripts, logic, and GUI design) remain the sole property of Ryan Harris / ZenDeuo. + +4. TERMINATION +This license terminates automatically if you fail to comply with any of its terms. Upon termination, you must cease all use of the Software and destroy all copies in your possession. + +5. DISCLAIMER OF WARRANTY +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b83739b --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# WinRepair + +A lightweight, portable Windows system repair utility designed to fix common OS corruption issues with a single click. + +ÂĐ 2026 Ryan Harris / ZenDeuo. +All Rights Reserved. + +The only reason I began this was because the other day my friend had suffered Internet issues which had just required a Windows Network Reset and it fixed his internet speeds immediately and then today (Monday 16th February) my other friend was suffering from some broken Windows System Files and had to manually get each DISM command and the SFC command from the web and he said he wished there were a simple GUI for it so I was just going to slop something together but I really got into it after I saw how successful AI was at building the application and making it work and I was so happy that I had to finish it to the end and now I'm really happy with it and I hope it helps anyone with corrupt Windows System files! + + + + + + +
WinRepair Application:WinRepair Advanced Configuration:
+ +## 🚀 Features + +* **One-Click Repair**: Automates the standard Windows repair sequence (DISM → SFC → CHKDSK). +* **Zero Dependencies**: Runs on stock Windows 10/11 using only built-in PowerShell and .NET. +* **Hidden Terminal**: Executes commands in the background without pop-ups or console windows. +* **Reset Network**: A dedicated tool to flush DNS, reset Winsock, and clear IP configurations. +* **Advanced Mode**: For more advanced users who only want to run specific steps. +* **Detailed Logging**: Real-time progress updates and exportable logs for troubleshooting. + +## 🛠ïļ Repair Steps + +WinRepair executes the following commands in a strictly ordered sequence: + +1. **System Integrity Check**: + * `DISM /CheckHealth` (Performs A Quick Scan) + * `DISM /ScanHealth` (Performs A Deep Scan) + * `DISM /RestoreHealth` (Repairs The Component Store (WinSxS Folder) Of Windows) + * `DISM /StartComponentCleanup` (Removes Superseded Updates) + * `sfc /scannow` (Uses The Fresh Image To Repair System Files) +2. **Disk Health**: + * `chkdsk C: /scan` (Online File System Check Which Checks The File System And Metadata For Logical And Physical Errors Without Repairing Them) +3. **Cleanup & Maintenance**: + * Clear Temporary Files (Windows Temp & Prefetch) + * Rebuild Icon/Thumbnail Cache (Windows Explorer Icons & Thumbnails) + +## ðŸ“Ķ Installation + +WinRepair is **portable** meaning no installation required. +1. Download the latest release (ZIP file). +2. Extract the folder to anywhere on your PC. +3. Run **`RunRepair.bat`**. + * *Note: Requires Administrative Privileges To Run The Commands It Needs (UAC Prompt Will Appear).* + +## ⚠ïļ Requirements + +* **OS**: Windows 10 or Windows 11 +* **PowerShell**: Version 5.1 (Standard on Windows 10/11) +* **Internet**: Required for DISM to download repair files from Windows Update. + +## 📜 License & Usage + +**Proprietary / All Rights Reserved** + +This project is **Source Available** for educational purposes and transparency. +By viewing or downloading this repository, you agree to the following terms: + +* **View Only**: You may view the source code for inspection and educational purposes. +* **No Modification**: You may NOT modify, bundle, or create derivative works from this software. +* **No Redistribution**: You may NOT redistribute this software, in source or binary form, without explicit written permission from the author. +* **No Commercial Use**: This software may not be sold or used for commercial gain. + +The full license text can be found in the [LICENSE](LICENSE) file. diff --git a/Run WinRepair.bat b/Run WinRepair.bat new file mode 100644 index 0000000..bb5588b --- /dev/null +++ b/Run WinRepair.bat @@ -0,0 +1,28 @@ +@echo off +title WinRepair Launcher +:: ═══════════════════════════════════════════════════════════════ +:: WinRepair Launcher +:: Handles UAC elevation + bypasses execution policy +:: ═══════════════════════════════════════════════════════════════ + +:: Check for admin rights +net session >nul 2>&1 +if %errorlevel% neq 0 ( + echo Requesting Administrator privileges... + powershell -NoProfile -Command "Start-Process cmd.exe -ArgumentList '/c \"\"%~f0\"\"' -Verb RunAs" + exit /b +) + +:: We are admin — change to script directory and run +pushd "%~dp0" +echo Starting WinRepair... +powershell.exe -NoProfile -ExecutionPolicy Bypass -File ".\WinRepair.ps1" + +:: If PowerShell exits with an error, show it +if %errorlevel% neq 0 ( + echo. + echo ERROR: WinRepair exited with error code %errorlevel% + echo Check WinRepair_Error.log for details. + pause +) +popd diff --git a/WinRepair.ps1 b/WinRepair.ps1 new file mode 100644 index 0000000..080c91c --- /dev/null +++ b/WinRepair.ps1 @@ -0,0 +1,1397 @@ +<# +.SYNOPSIS + WinRepair - Portable Windows OS Repair Utility +.DESCRIPTION + A GUI-based system repair tool that runs common Windows repair commands + in the correct order, with real-time progress tracking and hidden execution. + Detects system theme (light/dark) and styles accordingly. +.NOTES + Requires Administrator privileges. Use RunRepair.bat for easy launching. +#> + +param([switch]$DebugMode) + +# ═══════════════════════════════════════════════════════════════════════════════ +# ERROR LOGGING — catches any crash and writes to log file +# ═══════════════════════════════════════════════════════════════════════════════ +$ErrorActionPreference = "Stop" +$script:ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$script:ErrorLogPath = Join-Path $script:ScriptDir "WinRepair_Error.log" + +if ($DebugMode -or $env:WINREPAIR_DEBUG -eq "1") { + $Global:DebugMode = $true + Write-Host "DEBUG: Console Hiding DISABLED" -ForegroundColor Yellow +} + +trap { + $_ | Out-File -FilePath $script:ErrorLogPath -Append + $_.ScriptStackTrace | Out-File -FilePath $script:ErrorLogPath -Append + + # In debug mode, re-throw to the debug script's catch block instead of exiting + if ($Global:DebugMode) { + Write-Host "[TRAP] $($_.Exception.GetType().FullName): $($_.Exception.Message)" -ForegroundColor Red + Write-Host "[TRAP] $($_.ScriptStackTrace)" -ForegroundColor Yellow + break # Propagates error to caller's try/catch — keeps debug console open + } + + [System.Windows.Forms.MessageBox]::Show( + "WinRepair encountered an error:`n`n$($_.Exception.Message)`n`nDetails saved to WinRepair_Error.log", + "WinRepair - Error", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Error + ) | Out-Null + exit 1 +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# ADMIN SELF-ELEVATION +# ═══════════════════════════════════════════════════════════════════════════════ +if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + $scriptPath = $MyInvocation.MyCommand.Path + Start-Process "powershell.exe" -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`"" -Verb RunAs + exit +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# LOAD ASSEMBLIES +# ═══════════════════════════════════════════════════════════════════════════════ +Add-Type -AssemblyName System.Windows.Forms +Add-Type -AssemblyName System.Drawing +[System.Windows.Forms.Application]::EnableVisualStyles() + +# ═══════════════════════════════════════════════════════════════════════════════ +# HIDE CONSOLE WINDOW +# ═══════════════════════════════════════════════════════════════════════════════ +$signature = @' +[DllImport("Kernel32.dll")] +public static extern IntPtr GetConsoleWindow(); +[DllImport("user32.dll")] +public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); +'@ +$WinAPI = Add-Type -MemberDefinition $signature -Name "WinAPI" -Namespace "ConsoleHelper" -PassThru +$hwnd = $WinAPI::GetConsoleWindow() +if ($hwnd -ne [IntPtr]::Zero -and -not $Global:DebugMode) { + $WinAPI::ShowWindow($hwnd, 0) | Out-Null +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# THEME DETECTION +# ═══════════════════════════════════════════════════════════════════════════════ +function Get-SystemTheme { + try { + $regPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize" + $value = Get-ItemPropertyValue -Path $regPath -Name "AppsUseLightTheme" -ErrorAction Stop + if ($value -eq 0) { return "Dark" } else { return "Light" } + } catch { + return "Dark" + } +} + +$script:Theme = Get-SystemTheme + +# Theme color palettes +$script:Colors = @{} +if ($script:Theme -eq "Dark") { + $script:Colors = @{ + Background = [System.Drawing.Color]::FromArgb(10, 22, 40) + CardBg = [System.Drawing.Color]::FromArgb(18, 31, 51) + CardBgAlt = [System.Drawing.Color]::FromArgb(14, 26, 45) + Text = [System.Drawing.Color]::FromArgb(224, 230, 240) + TextSecondary = [System.Drawing.Color]::FromArgb(140, 160, 185) + Border = [System.Drawing.Color]::FromArgb(30, 58, 95) + Accent = [System.Drawing.Color]::FromArgb(59, 130, 246) + Green = [System.Drawing.Color]::FromArgb(0, 200, 83) + Red = [System.Drawing.Color]::FromArgb(255, 23, 68) + Yellow = [System.Drawing.Color]::FromArgb(255, 214, 0) + Gray = [System.Drawing.Color]::FromArgb(80, 95, 115) + ButtonBg = [System.Drawing.Color]::FromArgb(30, 58, 95) + ButtonHover = [System.Drawing.Color]::FromArgb(40, 75, 120) + LogBg = [System.Drawing.Color]::FromArgb(8, 16, 30) + ProgressBg = [System.Drawing.Color]::FromArgb(20, 35, 60) + ProgressFill = [System.Drawing.Color]::FromArgb(59, 130, 246) + } +} else { + $script:Colors = @{ + Background = [System.Drawing.Color]::FromArgb(240, 242, 245) + CardBg = [System.Drawing.Color]::FromArgb(255, 255, 255) + CardBgAlt = [System.Drawing.Color]::FromArgb(248, 249, 251) + Text = [System.Drawing.Color]::FromArgb(26, 26, 46) + TextSecondary = [System.Drawing.Color]::FromArgb(100, 110, 130) + Border = [System.Drawing.Color]::FromArgb(208, 213, 221) + Accent = [System.Drawing.Color]::FromArgb(37, 99, 235) + Green = [System.Drawing.Color]::FromArgb(22, 163, 74) + Red = [System.Drawing.Color]::FromArgb(220, 38, 38) + Yellow = [System.Drawing.Color]::FromArgb(202, 138, 4) + Gray = [System.Drawing.Color]::FromArgb(180, 190, 200) + ButtonBg = [System.Drawing.Color]::FromArgb(37, 99, 235) + ButtonHover = [System.Drawing.Color]::FromArgb(29, 78, 216) + LogBg = [System.Drawing.Color]::FromArgb(248, 249, 251) + ProgressBg = [System.Drawing.Color]::FromArgb(228, 233, 240) + ProgressFill = [System.Drawing.Color]::FromArgb(37, 99, 235) + } +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# FONTS (safe fallbacks — no comma-separated names) +# ═══════════════════════════════════════════════════════════════════════════════ +$script:FontTitle = New-Object System.Drawing.Font("Segoe UI", 16, [System.Drawing.FontStyle]::Bold) +$script:FontSubtitle = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Regular) +$script:FontStep = New-Object System.Drawing.Font("Segoe UI", 9.5, [System.Drawing.FontStyle]::Regular) +$script:FontStepBold = New-Object System.Drawing.Font("Segoe UI", 9.5, [System.Drawing.FontStyle]::Bold) +$script:FontButton = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold) +$script:FontSmall = New-Object System.Drawing.Font("Segoe UI", 8, [System.Drawing.FontStyle]::Regular) +$script:FontPhase = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold) + +# Log font — try Consolas (always available on Windows) +$script:FontLog = New-Object System.Drawing.Font("Consolas", 8.5, [System.Drawing.FontStyle]::Regular) + +# ═══════════════════════════════════════════════════════════════════════════════ +# REPAIR STEP DEFINITIONS +# ═══════════════════════════════════════════════════════════════════════════════ +$script:RepairSteps = @( + @{ + Name = "DISM - Check Health" + Description = "Quick health check of Windows image" + Command = "DISM /Online /Cleanup-Image /CheckHealth" + Phase = "System Integrity" + }, + @{ + Name = "DISM - Scan Health" + Description = "Deep scan of Windows image integrity" + Command = "DISM /Online /Cleanup-Image /ScanHealth" + Phase = "System Integrity" + }, + @{ + Name = "DISM - Restore Health" + Description = "Pull fresh image and repair component store" + Command = "DISM /Online /Cleanup-Image /RestoreHealth" + Phase = "System Integrity" + }, + @{ + Name = "DISM - Component Cleanup" + Description = "Clean up superseded components" + Command = "DISM /Online /Cleanup-Image /StartComponentCleanup" + Phase = "System Integrity" + }, + @{ + Name = "SFC - System File Checker" + Description = "Scan and repair system files using fresh image" + Command = "sfc /scannow" + Phase = "System Integrity" + }, + @{ + Name = "CHKDSK - Disk Scan" + Description = "Scan C: drive for filesystem errors" + Command = "chkdsk C: /scan" + Phase = "Disk Health" + }, + @{ + Name = "Clear Temporary Files" + Description = "Remove temp files to free disk space" + Command = "__SPECIAL_TEMP_CLEANUP__" + Phase = "Cleanup" + }, + @{ + Name = "Rebuild Icon Cache" + Description = "Fix broken or missing desktop icons" + Command = "__SPECIAL_ICON_CACHE__" + Phase = "Cleanup" + } +) + +$script:NetworkSteps = @( + @{ Name = "Flush DNS Cache"; Command = "ipconfig /flushdns" }, + @{ Name = "Reset Winsock"; Command = "netsh winsock reset" }, + @{ Name = "Reset TCP/IP Stack"; Command = "netsh int ip reset" }, + @{ Name = "Reset Firewall Rules"; Command = "netsh advfirewall reset" } +) + +# ═══════════════════════════════════════════════════════════════════════════════ +# STATE +# ═══════════════════════════════════════════════════════════════════════════════ +$script:IsRunning = $false +$script:CancelRequested = $false +$script:CurrentProcess = $null +$script:StepIndicators = @() +$script:StepLabels = @() +$script:StepEnabled = @() # Tracks which repair steps are enabled (Advanced mode) +$script:NetworkEnabled = @() # Tracks which network steps are enabled (Advanced mode) +$script:AdvancedMode = $false +$script:LogBuilder = New-Object System.Text.StringBuilder + +# ═══════════════════════════════════════════════════════════════════════════════ +# HELPER: STYLED BUTTON +# ═══════════════════════════════════════════════════════════════════════════════ +function New-StyledButton { + param( + [string]$Text, + [int]$X, [int]$Y, [int]$Width, [int]$Height, + [System.Drawing.Color]$BgColor, + [System.Drawing.Color]$FgColor, + [switch]$Danger + ) + $btn = New-Object System.Windows.Forms.Button + $btn.Text = $Text + $btn.Location = New-Object System.Drawing.Point($X, $Y) + $btn.Size = New-Object System.Drawing.Size($Width, $Height) + $btn.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat + $btn.FlatAppearance.BorderSize = 1 + if ($Danger) { + $btn.FlatAppearance.BorderColor = $script:Colors.Red + $btn.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::FromArgb(60, $script:Colors.Red.R, $script:Colors.Red.G, $script:Colors.Red.B) + } else { + $btn.FlatAppearance.BorderColor = $BgColor + $btn.FlatAppearance.MouseOverBackColor = $script:Colors.ButtonHover + } + $btn.BackColor = $BgColor + $btn.ForeColor = $FgColor + $btn.Font = $script:FontButton + $btn.Cursor = [System.Windows.Forms.Cursors]::Hand + return $btn +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# MAIN FORM +# ═══════════════════════════════════════════════════════════════════════════════ +$form = New-Object System.Windows.Forms.Form +$form.Text = "WinRepair - System Repair Utility" +$form.Size = New-Object System.Drawing.Size(780, 740) +$form.MinimumSize = New-Object System.Drawing.Size(700, 650) +$form.StartPosition = "CenterScreen" +$form.BackColor = $script:Colors.Background +$form.ForeColor = $script:Colors.Text +$form.Font = $script:FontStep +$form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Sizable +$form.Icon = [System.Drawing.SystemIcons]::Shield + +# ═══════════════════════════════════════════════════════════════════════════════ +# HEADER PANEL (explicit positioning — no docking, to avoid overlap with steps) +# ═══════════════════════════════════════════════════════════════════════════════ +$headerHeight = 76 + +$headerPanel = New-Object System.Windows.Forms.Panel +$headerPanel.Location = New-Object System.Drawing.Point(0, 0) +$headerPanel.Size = New-Object System.Drawing.Size($form.ClientSize.Width, $headerHeight) +$headerPanel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right +$headerPanel.BackColor = $script:Colors.CardBg +$headerPanel.Padding = New-Object System.Windows.Forms.Padding(20, 12, 20, 12) + +# Title +$lblTitle = New-Object System.Windows.Forms.Label +$lblTitle.Text = "WinRepair $([char]0x00A9) 2026 Ryan Harris / ZenDeuo" +$lblTitle.Font = $script:FontTitle +$lblTitle.ForeColor = $script:Colors.Accent +$lblTitle.AutoSize = $true +$lblTitle.Location = New-Object System.Drawing.Point(20, 12) +$headerPanel.Controls.Add($lblTitle) + +# Status label +$script:lblStatus = New-Object System.Windows.Forms.Label +$script:lblStatus.Text = "Ready - Click 'Run System Repair' to begin" +$script:lblStatus.Font = $script:FontSubtitle +$script:lblStatus.ForeColor = $script:Colors.TextSecondary +$script:lblStatus.AutoSize = $true +$script:lblStatus.Location = New-Object System.Drawing.Point(22, 48) +$headerPanel.Controls.Add($script:lblStatus) + +# ═══════════════════════════════════════════════════════════════════════════════ +# MAIN CONTENT (SplitContainer: steps top, log bottom) +# ═══════════════════════════════════════════════════════════════════════════════ +$splitContainer = New-Object System.Windows.Forms.SplitContainer +$splitContainer.Dock = [System.Windows.Forms.DockStyle]::None +$splitContainer.Orientation = [System.Windows.Forms.Orientation]::Horizontal +$splitContainer.SplitterDistance = 360 +$splitContainer.SplitterWidth = 6 +$splitContainer.BackColor = $script:Colors.Background +$splitContainer.Panel1.BackColor = $script:Colors.Background +$splitContainer.Panel2.BackColor = $script:Colors.Background +$splitContainer.BorderStyle = [System.Windows.Forms.BorderStyle]::None + +# ═══════════════════════════════════════════════════════════════════════════════ +# STEPS PANEL (scrollable) +# ═══════════════════════════════════════════════════════════════════════════════ +$stepsContainer = New-Object System.Windows.Forms.Panel +$stepsContainer.Dock = [System.Windows.Forms.DockStyle]::Fill +$stepsContainer.AutoScroll = $true +$stepsContainer.Padding = New-Object System.Windows.Forms.Padding(16, 10, 16, 10) +$stepsContainer.BackColor = $script:Colors.Background + +$yPos = 12 +$currentPhase = "" +$stepIndex = 0 + +foreach ($step in $script:RepairSteps) { + # Phase header + if ($step.Phase -ne $currentPhase) { + $currentPhase = $step.Phase + $phaseLabel = New-Object System.Windows.Forms.Label + $phaseLabel.Text = $currentPhase.ToUpper() + $phaseLabel.Font = $script:FontPhase + $phaseLabel.ForeColor = $script:Colors.Accent + $phaseLabel.Location = New-Object System.Drawing.Point(20, $yPos) + $phaseLabel.AutoSize = $true + $stepsContainer.Controls.Add($phaseLabel) + $yPos += 28 + } + + # Step row panel + $stepPanel = New-Object System.Windows.Forms.Panel + $stepPanel.Location = New-Object System.Drawing.Point(16, $yPos) + $stepPanel.Size = New-Object System.Drawing.Size(700, 50) + if ($stepIndex % 2 -eq 0) { + $stepPanel.BackColor = $script:Colors.CardBg + } else { + $stepPanel.BackColor = $script:Colors.CardBgAlt + } + $stepPanel.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right + + # Traffic light indicator (custom painted circle) + $indicator = New-Object System.Windows.Forms.PictureBox + $indicator.Location = New-Object System.Drawing.Point(14, 14) + $indicator.Size = New-Object System.Drawing.Size(20, 20) + $indicator.BackColor = [System.Drawing.Color]::Transparent + $indicator.Tag = "pending" + + $indicator.Add_Paint({ + param($s, $e) + $g = $e.Graphics + $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias + $clr = $script:Colors.Gray + switch ($s.Tag) { + "running" { $clr = $script:Colors.Yellow } + "success" { $clr = $script:Colors.Green } + "error" { $clr = $script:Colors.Red } + } + $br = New-Object System.Drawing.SolidBrush($clr) + $g.FillEllipse($br, 0, 0, 18, 18) + $br.Dispose() + + # Glow effect for running state + if ($s.Tag -eq "running") { + $glowBr = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(60, $clr.R, $clr.G, $clr.B)) + $g.FillEllipse($glowBr, -3, -3, 24, 24) + $glowBr.Dispose() + } + }) + $stepPanel.Controls.Add($indicator) + $script:StepIndicators += $indicator + + # Step number + $numLabel = New-Object System.Windows.Forms.Label + $numLabel.Text = "$($stepIndex + 1)." + $numLabel.Font = $script:FontStepBold + $numLabel.ForeColor = $script:Colors.TextSecondary + $numLabel.Location = New-Object System.Drawing.Point(42, 14) + $numLabel.Size = New-Object System.Drawing.Size(26, 20) + $stepPanel.Controls.Add($numLabel) + + # Step name + $nameLabel = New-Object System.Windows.Forms.Label + $nameLabel.Text = $step.Name + $nameLabel.Font = $script:FontStepBold + $nameLabel.ForeColor = $script:Colors.Text + $nameLabel.Location = New-Object System.Drawing.Point(68, 6) + $nameLabel.AutoSize = $true + $stepPanel.Controls.Add($nameLabel) + $script:StepLabels += $nameLabel + + # Step description + $descLabel = New-Object System.Windows.Forms.Label + $descLabel.Text = $step.Description + $descLabel.Font = $script:FontSmall + $descLabel.ForeColor = $script:Colors.TextSecondary + $descLabel.Location = New-Object System.Drawing.Point(68, 28) + $descLabel.AutoSize = $true + $stepPanel.Controls.Add($descLabel) + + $stepsContainer.Controls.Add($stepPanel) + $yPos += 54 + $stepIndex++ +} + +# Add bottom margin so step 8 description isn't clipped +$bottomSpacer = New-Object System.Windows.Forms.Panel +$bottomSpacer.Location = New-Object System.Drawing.Point(16, $yPos) +$bottomSpacer.Size = New-Object System.Drawing.Size(10, 20) +$stepsContainer.Controls.Add($bottomSpacer) + +$splitContainer.Panel1.Controls.Add($stepsContainer) + +# ═══════════════════════════════════════════════════════════════════════════════ +# PROGRESS BAR +# ═══════════════════════════════════════════════════════════════════════════════ +$progressPanel = New-Object System.Windows.Forms.Panel +$progressPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom +$progressPanel.Height = 36 +$progressPanel.BackColor = $script:Colors.Background +$progressPanel.Padding = New-Object System.Windows.Forms.Padding(24, 8, 24, 6) + +$script:progressBar = New-Object System.Windows.Forms.ProgressBar +$script:progressBar.Dock = [System.Windows.Forms.DockStyle]::Fill +$script:progressBar.Minimum = 0 +$script:progressBar.Maximum = $script:RepairSteps.Count +$script:progressBar.Value = 0 +$script:progressBar.Style = [System.Windows.Forms.ProgressBarStyle]::Continuous +$progressPanel.Controls.Add($script:progressBar) + +# ═══════════════════════════════════════════════════════════════════════════════ +# LOG PANEL +# ═══════════════════════════════════════════════════════════════════════════════ +$logHeaderPanel = New-Object System.Windows.Forms.Panel +$logHeaderPanel.Dock = [System.Windows.Forms.DockStyle]::Top +$logHeaderPanel.Height = 28 +$logHeaderPanel.BackColor = $script:Colors.CardBg + +$lblLogTitle = New-Object System.Windows.Forms.Label +$lblLogTitle.Text = " OUTPUT LOG" +$lblLogTitle.Font = $script:FontPhase +$lblLogTitle.ForeColor = $script:Colors.Accent +$lblLogTitle.Dock = [System.Windows.Forms.DockStyle]::Fill +$lblLogTitle.TextAlign = [System.Drawing.ContentAlignment]::MiddleLeft +$logHeaderPanel.Controls.Add($lblLogTitle) + +$script:txtLog = New-Object System.Windows.Forms.RichTextBox +$script:txtLog.Dock = [System.Windows.Forms.DockStyle]::Fill +$script:txtLog.BackColor = $script:Colors.LogBg +$script:txtLog.ForeColor = $script:Colors.Text +$script:txtLog.Font = $script:FontLog +$script:txtLog.ReadOnly = $true +$script:txtLog.BorderStyle = [System.Windows.Forms.BorderStyle]::None +$script:txtLog.ScrollBars = [System.Windows.Forms.RichTextBoxScrollBars]::Vertical + +$splitContainer.Panel2.Controls.Add($script:txtLog) +$splitContainer.Panel2.Controls.Add($logHeaderPanel) + + +# ═══════════════════════════════════════════════════════════════════════════════ +# BUTTON BAR +# ═══════════════════════════════════════════════════════════════════════════════ +$buttonPanel = New-Object System.Windows.Forms.Panel +$buttonPanel.Dock = [System.Windows.Forms.DockStyle]::Bottom +$buttonPanel.Height = 60 +$buttonPanel.Width = $form.ClientSize.Width # Crucial for Right Anchor to work correctly +$buttonPanel.BackColor = $script:Colors.CardBg +$buttonPanel.Padding = New-Object System.Windows.Forms.Padding(16, 10, 16, 10) + +# Bottom border (between progress bar and buttons) +$bottomBorder = New-Object System.Windows.Forms.Panel +$bottomBorder.Dock = [System.Windows.Forms.DockStyle]::Bottom +$bottomBorder.Height = 1 +$bottomBorder.BackColor = $script:Colors.Border + +# Run System Repair button +$script:btnRun = New-StyledButton -Text "Run System Repair" -X 16 -Y 10 -Width 200 -Height 38 ` + -BgColor $script:Colors.ButtonBg -FgColor ([System.Drawing.Color]::White) +$buttonPanel.Controls.Add($script:btnRun) + +# Network Reset button +$script:btnNetwork = New-StyledButton -Text "Network Reset" -X 226 -Y 10 -Width 160 -Height 38 ` + -BgColor $script:Colors.CardBgAlt -FgColor $script:Colors.Text +$script:btnNetwork.FlatAppearance.BorderColor = $script:Colors.Border +$buttonPanel.Controls.Add($script:btnNetwork) + +# Cancel button (starts disabled with muted style, turns red when active) +$script:btnCancel = New-StyledButton -Text "Cancel" -X 396 -Y 10 -Width 100 -Height 38 ` + -BgColor $script:Colors.CardBgAlt -FgColor $script:Colors.TextSecondary +$script:btnCancel.FlatAppearance.BorderColor = $script:Colors.Border +$script:btnCancel.Enabled = $false +$buttonPanel.Controls.Add($script:btnCancel) + +# Export Log button (anchored far right) +$script:btnExport = New-StyledButton -Text "Export Log" -X 616 -Y 10 -Width 110 -Height 38 ` + -BgColor $script:Colors.CardBgAlt -FgColor $script:Colors.Text +$script:btnExport.FlatAppearance.BorderColor = $script:Colors.Border +$script:btnExport.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right +$buttonPanel.Controls.Add($script:btnExport) + +# Advanced button (anchored right, next to Export Log) +$script:btnAdvanced = New-StyledButton -Text "Advanced" -X 506 -Y 10 -Width 100 -Height 38 ` + -BgColor $script:Colors.CardBgAlt -FgColor $script:Colors.Accent +$script:btnAdvanced.FlatAppearance.BorderColor = $script:Colors.Accent +$script:btnAdvanced.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Right +$buttonPanel.Controls.Add($script:btnAdvanced) + +# ═══════════════════════════════════════════════════════════════════════════════ +# ADVANCED CONFIGURATION PANEL (overlay on split container Panel1) +# ═══════════════════════════════════════════════════════════════════════════════ +$script:advancedPanel = New-Object System.Windows.Forms.Panel +$script:advancedPanel.Dock = [System.Windows.Forms.DockStyle]::Fill +$script:advancedPanel.BackColor = $script:Colors.Background +$script:advancedPanel.AutoScroll = $true +$script:advancedPanel.Visible = $false +$script:advancedPanel.Padding = New-Object System.Windows.Forms.Padding(20, 10, 20, 10) + +# Helper to update Run button text based on checkbox state +function Update-RunButtonText { + if (-not $script:AdvancedMode) { + $script:btnRun.Text = "Run System Repair" + return + } + $anyUnchecked = $false + $anyChecked = $false + for ($ci = 0; $ci -lt $script:StepEnabled.Count; $ci++) { + if ($script:StepEnabled[$ci]) { $anyChecked = $true } else { $anyUnchecked = $true } + } + if (-not $anyChecked) { + $script:btnRun.Text = "Run System Repair" + $script:btnRun.Enabled = $false + } elseif ($anyUnchecked) { + $script:btnRun.Text = "Run Selected Steps" + if (-not $script:IsRunning) { $script:btnRun.Enabled = $true } + } else { + $script:btnRun.Text = "Run System Repair" + if (-not $script:IsRunning) { $script:btnRun.Enabled = $true } + } +} + +# Build advanced panel content +$advYPos = 10 + +# Title row +$advTitle = New-Object System.Windows.Forms.Label +$advTitle.Text = "ADVANCED CONFIGURATION" +$advTitle.Font = $script:FontPhase +$advTitle.ForeColor = $script:Colors.Accent +$advTitle.AutoSize = $true +$advTitle.Location = New-Object System.Drawing.Point(16, $advYPos) +$script:advancedPanel.Controls.Add($advTitle) + +# Select All / Deselect All buttons +$btnSelectAll = New-Object System.Windows.Forms.LinkLabel +$btnSelectAll.Text = "Select All" +$btnSelectAll.Font = $script:FontSmall +$btnSelectAll.LinkColor = $script:Colors.Accent +$btnSelectAll.ActiveLinkColor = $script:Colors.Accent +$btnSelectAll.Location = New-Object System.Drawing.Point(280, ($advYPos + 2)) +$btnSelectAll.AutoSize = $true +$btnSelectAll.Add_LinkClicked({ + for ($ci = 0; $ci -lt $script:RepairCheckboxes.Count; $ci++) { + $script:RepairCheckboxes[$ci].Checked = $true + } + for ($ci = 0; $ci -lt $script:NetworkCheckboxes.Count; $ci++) { + $script:NetworkCheckboxes[$ci].Checked = $true + } +}) +$script:advancedPanel.Controls.Add($btnSelectAll) + +$btnDeselectAll = New-Object System.Windows.Forms.LinkLabel +$btnDeselectAll.Text = "Deselect All" +$btnDeselectAll.Font = $script:FontSmall +$btnDeselectAll.LinkColor = $script:Colors.TextSecondary +$btnDeselectAll.ActiveLinkColor = $script:Colors.TextSecondary +$btnDeselectAll.Location = New-Object System.Drawing.Point(360, ($advYPos + 2)) +$btnDeselectAll.AutoSize = $true +$btnDeselectAll.Add_LinkClicked({ + for ($ci = 0; $ci -lt $script:RepairCheckboxes.Count; $ci++) { + $script:RepairCheckboxes[$ci].Checked = $false + } + for ($ci = 0; $ci -lt $script:NetworkCheckboxes.Count; $ci++) { + $script:NetworkCheckboxes[$ci].Checked = $false + } +}) +$script:advancedPanel.Controls.Add($btnDeselectAll) + +$advYPos += 30 + +# Build repair step checkboxes grouped by phase +$script:RepairCheckboxes = @() +$lastPhase = "" + +for ($si = 0; $si -lt $script:RepairSteps.Count; $si++) { + $step = $script:RepairSteps[$si] + $script:StepEnabled += $true + + # Phase header + if ($step.Phase -ne $lastPhase) { + $phaseLabel = New-Object System.Windows.Forms.Label + $phaseLabel.Text = $step.Phase.ToUpper() + $phaseLabel.Font = $script:FontSmall + $phaseLabel.ForeColor = $script:Colors.Accent + $phaseLabel.AutoSize = $true + $phaseLabel.Location = New-Object System.Drawing.Point(16, $advYPos) + $script:advancedPanel.Controls.Add($phaseLabel) + $advYPos += 22 + $lastPhase = $step.Phase + } + + # Checkbox + $cb = New-Object System.Windows.Forms.CheckBox + $cb.Text = "$($step.Name) - $($step.Description)" + $cb.Font = $script:FontStep + $cb.ForeColor = $script:Colors.Text + $cb.Checked = $true + $cb.AutoSize = $true + $cb.Location = New-Object System.Drawing.Point(30, $advYPos) + $cb.Tag = $si # Store step index + $cb.Add_CheckedChanged({ + param($sender, $e) + $idx = $sender.Tag + $script:StepEnabled[$idx] = $sender.Checked + Update-RunButtonText + }) + $script:advancedPanel.Controls.Add($cb) + $script:RepairCheckboxes += $cb + $advYPos += 26 +} + +# Network steps section +$advYPos += 8 +$netPhaseLabel = New-Object System.Windows.Forms.Label +$netPhaseLabel.Text = "NETWORK RESET" +$netPhaseLabel.Font = $script:FontSmall +$netPhaseLabel.ForeColor = $script:Colors.Accent +$netPhaseLabel.AutoSize = $true +$netPhaseLabel.Location = New-Object System.Drawing.Point(16, $advYPos) +$script:advancedPanel.Controls.Add($netPhaseLabel) +$advYPos += 22 + +$script:NetworkCheckboxes = @() +for ($ni = 0; $ni -lt $script:NetworkSteps.Count; $ni++) { + $netStep = $script:NetworkSteps[$ni] + $script:NetworkEnabled += $true + + $ncb = New-Object System.Windows.Forms.CheckBox + $ncb.Text = $netStep.Name + $ncb.Font = $script:FontStep + $ncb.ForeColor = $script:Colors.Text + $ncb.Checked = $true + $ncb.AutoSize = $true + $ncb.Location = New-Object System.Drawing.Point(30, $advYPos) + $ncb.Tag = $ni + $ncb.Add_CheckedChanged({ + param($sender, $e) + $idx = $sender.Tag + $script:NetworkEnabled[$idx] = $sender.Checked + }) + $script:advancedPanel.Controls.Add($ncb) + $script:NetworkCheckboxes += $ncb + $advYPos += 26 +} + +# Add bottom spacer +$advYPos += 10 +$advSpacer = New-Object System.Windows.Forms.Panel +$advSpacer.Location = New-Object System.Drawing.Point(0, $advYPos) +$advSpacer.Size = New-Object System.Drawing.Size(10, 10) +$script:advancedPanel.Controls.Add($advSpacer) + +# Add advanced panel to splitContainer Panel1 (on top of stepsContainer) +$splitContainer.Panel1.Controls.Add($script:advancedPanel) +$script:advancedPanel.BringToFront() # Ensure it sits on top of stepsContainer + +# Toggle Advanced panel +$script:btnAdvanced.Add_Click({ + if ($script:IsRunning) { return } + $script:AdvancedMode = -not $script:AdvancedMode + $script:advancedPanel.Visible = $script:AdvancedMode + if ($script:AdvancedMode) { $script:advancedPanel.BringToFront() } + + if ($script:AdvancedMode) { + $script:btnAdvanced.ForeColor = [System.Drawing.Color]::White + $script:btnAdvanced.BackColor = $script:Colors.Accent + $script:btnAdvanced.FlatAppearance.BorderColor = $script:Colors.Accent + } else { + $script:btnAdvanced.ForeColor = $script:Colors.Accent + $script:btnAdvanced.BackColor = $script:Colors.CardBgAlt + $script:btnAdvanced.FlatAppearance.BorderColor = $script:Colors.Accent + } + Update-RunButtonText +}) + +# ═══════════════════════════════════════════════════════════════════════════════ +# ADD CONTROLS TO FORM +# Header and SplitContainer use explicit positioning. +# Bottom controls (progress, border, buttons) are docked Bottom. +# ═══════════════════════════════════════════════════════════════════════════════ +$form.SuspendLayout() + +# Bottom section (docked — add first so they process correctly) +$form.Controls.Add($progressPanel) # Bottom, closest to content +$form.Controls.Add($bottomBorder) # Bottom, between progress and buttons +$form.Controls.Add($buttonPanel) # Bottom, very bottom edge + +# Calculate the bottom docked area height +$bottomDockedHeight = $progressPanel.Height + $bottomBorder.Height + $buttonPanel.Height # 36 + 1 + 60 = 97 + +# SplitContainer — explicit position below header, anchored all 4 sides +$splitContainer.Location = New-Object System.Drawing.Point(0, $headerHeight) +$splitContainer.Size = New-Object System.Drawing.Size( + $form.ClientSize.Width, + ($form.ClientSize.Height - $headerHeight - $bottomDockedHeight) +) +$splitContainer.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Right -bor [System.Windows.Forms.AnchorStyles]::Bottom +$form.Controls.Add($splitContainer) + +# Header — explicit position at top, rendered on top (last added = front of Z-order) +$form.Controls.Add($headerPanel) + +$form.ResumeLayout() + +# ═══════════════════════════════════════════════════════════════════════════════ +# LOGGING FUNCTION +# ═══════════════════════════════════════════════════════════════════════════════ +function Write-Log { + param( + [string]$Message, + [string]$Level = "INFO" + ) + + $timestamp = Get-Date -Format "HH:mm:ss" + $prefix = switch ($Level) { + "CMD" { "[CMD] " } + "SUCCESS" { "[OK] " } + "ERROR" { "[ERROR] " } + "WARN" { "[WARN] " } + "STEP" { "[STEP] " } + default { "[INFO] " } + } + + $color = switch ($Level) { + "CMD" { $script:Colors.Accent } + "SUCCESS" { $script:Colors.Green } + "ERROR" { $script:Colors.Red } + "WARN" { $script:Colors.Yellow } + "STEP" { $script:Colors.Accent } + default { $script:Colors.Text } + } + + $fullMsg = "$timestamp $prefix $Message" + $script:LogBuilder.AppendLine($fullMsg) | Out-Null + + $script:txtLog.SelectionStart = $script:txtLog.TextLength + $script:txtLog.SelectionLength = 0 + $script:txtLog.SelectionColor = $color + $script:txtLog.AppendText("$fullMsg`r`n") + $script:txtLog.ScrollToCaret() +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# UPDATE STEP INDICATOR +# ═══════════════════════════════════════════════════════════════════════════════ +function Set-StepStatus { + param([int]$Index, [string]$Status) + if ($Index -ge 0 -and $Index -lt $script:StepIndicators.Count) { + $script:StepIndicators[$Index].Tag = $Status + $script:StepIndicators[$Index].Invalidate() + } +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# RUN A COMMAND IN HIDDEN PROCESS WITH REAL-TIME OUTPUT +# ═══════════════════════════════════════════════════════════════════════════════ +function Invoke-RepairCommand { + param([string]$Command, [int]$StepIndex) + + Write-Log "Executing: $Command" -Level "CMD" + Set-StepStatus -Index $StepIndex -Status "running" + $script:lblStatus.Text = "Running - $($script:RepairSteps[$StepIndex].Name)" + [System.Windows.Forms.Application]::DoEvents() + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = "cmd.exe" + $psi.Arguments = "/c $Command 2>&1" + $psi.UseShellExecute = $false + $psi.RedirectStandardOutput = $true + $psi.RedirectStandardError = $false + $psi.CreateNoWindow = $true + $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden + + try { + $process = New-Object System.Diagnostics.Process + $process.StartInfo = $psi + + $process.Start() | Out-Null + $script:CurrentProcess = $process + + # Use ReadLineAsync to poll output without blocking UI and avoiding + # the thread-safety crashes of OutputDataReceived events in PowerShell. + $reader = $process.StandardOutput + $readTask = $reader.ReadLineAsync() + + while (-not $process.HasExited -or ($readTask -ne $null -and $readTask.IsCompleted)) { + # Process output + if ($readTask -ne $null -and $readTask.IsCompleted) { + try { + $line = $readTask.Result + if ($line -ne $null) { + # Sanitize output: Remove control chars (like backspaces/nulls) but keep tabs + $cleanLine = $line -replace "[\x00-\x08\x0B-\x1F\x7F]", "" + if ($cleanLine.Trim() -ne "") { Write-Log $cleanLine.Trim() } + $readTask = $reader.ReadLineAsync() + } else { + $readTask = $null # End of stream + } + } catch { + $readTask = $null + } + } + + if ($script:CancelRequested) { + try { if (-not $process.HasExited) { $process.Kill() } } catch {} + Write-Log "Operation cancelled by user" -Level "WARN" + Set-StepStatus -Index $StepIndex -Status "error" + $script:CurrentProcess = $null + try { $process.Dispose() } catch {} + return $false + } + + [System.Windows.Forms.Application]::DoEvents() + Start-Sleep -Milliseconds 50 + } + + # Drain any remaining output after exit + if ($readTask -ne $null) { + # process has exited, so just read to end synchronously (it should be buffered) + $remaining = $reader.ReadToEnd() + if ($remaining) { + foreach ($l in ($remaining -split "`n")) { + if ($l.Trim()) { Write-Log $l.Trim() } + } + } + } + + $exitCode = $process.ExitCode + $script:CurrentProcess = $null + try { $process.Dispose() } catch {} + + if ($exitCode -eq 0) { + Write-Log "$($script:RepairSteps[$StepIndex].Name) completed successfully" -Level "SUCCESS" + Set-StepStatus -Index $StepIndex -Status "success" + return $true + } else { + Write-Log "$($script:RepairSteps[$StepIndex].Name) exited with code $exitCode" -Level "ERROR" + Set-StepStatus -Index $StepIndex -Status "error" + return $false + } + } + catch { + Write-Log "Failed: $($_.Exception.Message)" -Level "ERROR" + Set-StepStatus -Index $StepIndex -Status "error" + $script:CurrentProcess = $null + return $false + } +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# SPECIAL: CLEAR TEMP FILES +# ═══════════════════════════════════════════════════════════════════════════════ +function Invoke-TempCleanup { + param([int]$StepIndex) + + Write-Log "Starting temporary files cleanup..." -Level "CMD" + Set-StepStatus -Index $StepIndex -Status "running" + $script:lblStatus.Text = "Running - Clear Temporary Files" + [System.Windows.Forms.Application]::DoEvents() + + $paths = @( + $env:TEMP, + "C:\Windows\Temp", + "C:\Windows\Prefetch" + ) + + $totalRemoved = 0 + $totalFailed = 0 + + foreach ($path in $paths) { + if ($script:CancelRequested) { + Write-Log "Cleanup cancelled by user" -Level "WARN" + Set-StepStatus -Index $StepIndex -Status "error" + return $false + } + + if (Test-Path $path) { + Write-Log "Cleaning: $path" + try { + $items = Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue + foreach ($item in $items) { + if ($script:CancelRequested) { break } + try { + Remove-Item -Path $item.FullName -Force -Recurse -ErrorAction Stop + $totalRemoved++ + } catch { + $totalFailed++ + } + } + [System.Windows.Forms.Application]::DoEvents() + } catch { + Write-Log "Warning: Could not fully clean $path" -Level "WARN" + } + } + } + + Write-Log "Cleanup complete: $totalRemoved items removed, $totalFailed items skipped (in use)" -Level "SUCCESS" + Set-StepStatus -Index $StepIndex -Status "success" + return $true +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# SPECIAL: REBUILD ICON CACHE +# ═══════════════════════════════════════════════════════════════════════════════ +function Invoke-IconCacheRebuild { + param([int]$StepIndex) + + Write-Log "Rebuilding icon cache..." -Level "CMD" + Set-StepStatus -Index $StepIndex -Status "running" + $script:lblStatus.Text = "Running - Rebuild Icon Cache" + [System.Windows.Forms.Application]::DoEvents() + + try { + # Stop Explorer + Write-Log "Stopping Windows Explorer..." + Stop-Process -Name "explorer" -Force -ErrorAction SilentlyContinue + Start-Sleep -Seconds 2 + + # Delete icon cache files + $iconCachePath = Join-Path $env:LOCALAPPDATA "IconCache.db" + $thumbCachePath = Join-Path $env:LOCALAPPDATA "Microsoft\Windows\Explorer" + + if (Test-Path $iconCachePath) { + Remove-Item $iconCachePath -Force -ErrorAction SilentlyContinue + Write-Log "Deleted IconCache.db" + } + + if (Test-Path $thumbCachePath) { + Get-ChildItem (Join-Path $thumbCachePath "iconcache_*.db") -ErrorAction SilentlyContinue | ForEach-Object { + Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue + Write-Log "Deleted $($_.Name)" + } + Get-ChildItem (Join-Path $thumbCachePath "thumbcache_*.db") -ErrorAction SilentlyContinue | ForEach-Object { + Remove-Item $_.FullName -Force -ErrorAction SilentlyContinue + Write-Log "Deleted $($_.Name)" + } + } + + # Restart Explorer + Write-Log "Restarting Windows Explorer..." + Start-Process "explorer.exe" + Start-Sleep -Seconds 2 + + Write-Log "Icon cache rebuilt successfully" -Level "SUCCESS" + Set-StepStatus -Index $StepIndex -Status "success" + return $true + } + catch { + Write-Log "Icon cache rebuild failed: $($_.Exception.Message)" -Level "ERROR" + Start-Process "explorer.exe" -ErrorAction SilentlyContinue + Set-StepStatus -Index $StepIndex -Status "error" + return $false + } +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# ERROR DIALOG +# ═══════════════════════════════════════════════════════════════════════════════ +function Show-ErrorDialog { + param([string]$StepName, [string]$ErrorMessage) + + $msg = "An error occurred during:`n`n$StepName`n`nError: $ErrorMessage`n`n" + $msg += "Would you like to skip this step and continue?`n`n" + $msg += "Yes = Skip and continue`nNo = Retry this step`nCancel = Stop all repairs" + + $result = [System.Windows.Forms.MessageBox]::Show( + $msg, + "WinRepair - Error", + [System.Windows.Forms.MessageBoxButtons]::YesNoCancel, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) + return $result +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# NETWORK RESET WARNING DIALOG +# ═══════════════════════════════════════════════════════════════════════════════ +function Show-NetworkWarning { + $msg = "WARNING: NETWORK RESET`n`n" + $msg += "This will reset ALL network settings to factory defaults:`n`n" + $msg += "- Clear DNS resolver cache`n" + $msg += "- Reset Winsock catalog (may remove network add-ons)`n" + $msg += "- Reset TCP/IP stack to default configuration`n" + $msg += "- Reset Windows Firewall (ALL custom rules will be DELETED!)`n`n" + $msg += "Your network connection will be temporarily lost.`n" + $msg += "Custom firewall rules, VPN configurations, and network`n" + $msg += "adapter settings may need to be reconfigured.`n`n" + $msg += "Only proceed if you are experiencing network issues.`n`n" + $msg += "Do you want to continue?" + + $result = [System.Windows.Forms.MessageBox]::Show( + $msg, + "WinRepair - Network Reset Warning", + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) + return ($result -eq [System.Windows.Forms.DialogResult]::Yes) +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# RUN SYSTEM REPAIR (MAIN BUTTON) +# ═══════════════════════════════════════════════════════════════════════════════ +$script:btnRun.Add_Click({ + if ($script:IsRunning) { return } + + $script:IsRunning = $true + $script:CancelRequested = $false + $script:btnRun.Enabled = $false + $script:btnNetwork.Enabled = $false + $script:btnAdvanced.Enabled = $false + $script:btnCancel.Enabled = $true + $script:btnCancel.ForeColor = $script:Colors.Red + $script:btnCancel.FlatAppearance.BorderColor = $script:Colors.Red + $script:btnCancel.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::FromArgb(60, $script:Colors.Red.R, $script:Colors.Red.G, $script:Colors.Red.B) + $script:progressBar.Value = 0 + $script:txtLog.Clear() + $script:LogBuilder.Clear() + + # Reset all indicators + for ($i = 0; $i -lt $script:StepIndicators.Count; $i++) { + Set-StepStatus -Index $i -Status "pending" + } + + Write-Log "=======================================================" -Level "INFO" + Write-Log " WinRepair - System Repair Starting" -Level "STEP" + Write-Log " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -Level "INFO" + Write-Log "=======================================================" -Level "INFO" + Write-Log "" + + $completedSteps = 0 + $failedSteps = 0 + + for ($i = 0; $i -lt $script:RepairSteps.Count; $i++) { + # Skip unchecked steps in advanced mode + if ($script:StepEnabled.Count -gt $i -and -not $script:StepEnabled[$i]) { + Write-Log "" + Write-Log "---- Step $($i + 1) of $($script:RepairSteps.Count): $($script:RepairSteps[$i].Name) [SKIPPED] ----" -Level "WARN" + Set-StepStatus -Index $i -Status "pending" + $script:progressBar.Value = $i + 1 + [System.Windows.Forms.Application]::DoEvents() + continue + } + + if ($script:CancelRequested) { + Write-Log "" + Write-Log "Repair cancelled by user at step $($i + 1)" -Level "WARN" + break + } + + $step = $script:RepairSteps[$i] + Write-Log "" + Write-Log "---- Step $($i + 1) of $($script:RepairSteps.Count): $($step.Name) ----" -Level "STEP" + # Progress updated via progress bar + [System.Windows.Forms.Application]::DoEvents() + + $success = $false + + do { + if ($step.Command -eq "__SPECIAL_TEMP_CLEANUP__") { + $success = Invoke-TempCleanup -StepIndex $i + } + elseif ($step.Command -eq "__SPECIAL_ICON_CACHE__") { + $success = Invoke-IconCacheRebuild -StepIndex $i + } + else { + $success = Invoke-RepairCommand -Command $step.Command -StepIndex $i + } + + if (-not $success -and -not $script:CancelRequested) { + $dialogResult = Show-ErrorDialog -StepName $step.Name -ErrorMessage "Step did not complete successfully" + if ($dialogResult -eq [System.Windows.Forms.DialogResult]::Yes) { + Write-Log "User chose to skip $($step.Name)" -Level "WARN" + Set-StepStatus -Index $i -Status "error" + $failedSteps++ + $success = $true + } + elseif ($dialogResult -eq [System.Windows.Forms.DialogResult]::No) { + Write-Log "Retrying $($step.Name)..." -Level "WARN" + } + else { + $script:CancelRequested = $true + break + } + } else { + $completedSteps++ + } + } while (-not $success -and -not $script:CancelRequested) + + $script:progressBar.Value = $i + 1 + # Progress updated via progress bar + [System.Windows.Forms.Application]::DoEvents() + } + + # Final status + Write-Log "" + Write-Log "=======================================================" -Level "INFO" + if ($script:CancelRequested) { + $script:lblStatus.Text = "Cancelled - Repair was stopped by user" + Write-Log " Repair CANCELLED by user" -Level "WARN" + } + elseif ($failedSteps -gt 0) { + $script:lblStatus.Text = "Complete with warnings - $failedSteps step(s) had issues" + Write-Log " Repair COMPLETE with $failedSteps warning(s)" -Level "WARN" + } + else { + $script:lblStatus.Text = "Complete - All repairs finished successfully!" + Write-Log " Repair COMPLETE - All steps successful!" -Level "SUCCESS" + } + Write-Log " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -Level "INFO" + Write-Log "=======================================================" -Level "INFO" + + $script:IsRunning = $false + $script:btnRun.Enabled = $true + $script:btnNetwork.Enabled = $true + $script:btnAdvanced.Enabled = $true + $script:btnCancel.Enabled = $false + $script:btnCancel.ForeColor = $script:Colors.TextSecondary + $script:btnCancel.FlatAppearance.BorderColor = $script:Colors.Border +}) + +# ═══════════════════════════════════════════════════════════════════════════════ +# NETWORK RESET BUTTON +# ═══════════════════════════════════════════════════════════════════════════════ +$script:btnNetwork.Add_Click({ + if ($script:IsRunning) { return } + + if (-not (Show-NetworkWarning)) { + Write-Log "Network reset cancelled by user" -Level "INFO" + return + } + + $script:IsRunning = $true + $script:CancelRequested = $false + $script:btnRun.Enabled = $false + $script:btnNetwork.Enabled = $false + $script:btnAdvanced.Enabled = $false + $script:btnCancel.Enabled = $true + $script:btnCancel.ForeColor = $script:Colors.Red + $script:btnCancel.FlatAppearance.BorderColor = $script:Colors.Red + $script:btnCancel.FlatAppearance.MouseOverBackColor = [System.Drawing.Color]::FromArgb(60, $script:Colors.Red.R, $script:Colors.Red.G, $script:Colors.Red.B) + + Write-Log "" + Write-Log "=======================================================" -Level "INFO" + Write-Log " Network Reset Starting" -Level "STEP" + Write-Log "=======================================================" -Level "INFO" + + for ($ni = 0; $ni -lt $script:NetworkSteps.Count; $ni++) { + $netStep = $script:NetworkSteps[$ni] + if ($script:CancelRequested) { break } + + # Skip unchecked network steps in advanced mode + if ($script:NetworkEnabled.Count -gt $ni -and -not $script:NetworkEnabled[$ni]) { + Write-Log "" + Write-Log "$($netStep.Name) [SKIPPED]" -Level "WARN" + continue + } + + $script:lblStatus.Text = "Running - $($netStep.Name)" + Write-Log "" + Write-Log "Executing: $($netStep.Command)" -Level "CMD" + [System.Windows.Forms.Application]::DoEvents() + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = "cmd.exe" + $psi.Arguments = "/c $($netStep.Command) 2>&1" + $psi.UseShellExecute = $false + $psi.RedirectStandardOutput = $true + $psi.CreateNoWindow = $true + $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden + + try { + $process = [System.Diagnostics.Process]::Start($psi) + $output = $process.StandardOutput.ReadToEnd() + $process.WaitForExit() + + if ($output.Trim() -ne "") { + foreach ($line in ($output -split "`n")) { + if ($line.Trim() -ne "") { Write-Log $line.Trim() } + } + } + Write-Log "$($netStep.Name) completed" -Level "SUCCESS" + } + catch { + Write-Log "Failed: $($_.Exception.Message)" -Level "ERROR" + } + [System.Windows.Forms.Application]::DoEvents() + } + + Write-Log "" + Write-Log "=======================================================" -Level "INFO" + Write-Log " Network Reset Complete" -Level "SUCCESS" + Write-Log " You may need to reconnect to your network." -Level "WARN" + Write-Log "=======================================================" -Level "INFO" + + $script:lblStatus.Text = "Network reset complete - You may need to reconnect" + + $script:IsRunning = $false + $script:btnRun.Enabled = $true + $script:btnNetwork.Enabled = $true + $script:btnAdvanced.Enabled = $true + $script:btnCancel.Enabled = $false + $script:btnCancel.ForeColor = $script:Colors.TextSecondary + $script:btnCancel.FlatAppearance.BorderColor = $script:Colors.Border +}) + +# ═══════════════════════════════════════════════════════════════════════════════ +# CANCEL BUTTON +# ═══════════════════════════════════════════════════════════════════════════════ +$script:btnCancel.Add_Click({ + if (-not $script:IsRunning) { return } + + $result = [System.Windows.Forms.MessageBox]::Show( + "Are you sure you want to cancel the current operation?`n`nThe current step will be stopped.", + "WinRepair - Cancel", + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Question + ) + + if ($result -eq [System.Windows.Forms.DialogResult]::Yes) { + $script:CancelRequested = $true + if ($null -ne $script:CurrentProcess -and -not $script:CurrentProcess.HasExited) { + try { $script:CurrentProcess.Kill() } catch {} + } + } +}) + +# ═══════════════════════════════════════════════════════════════════════════════ +# EXPORT LOG BUTTON +# ═══════════════════════════════════════════════════════════════════════════════ +$script:btnExport.Add_Click({ + $saveDialog = New-Object System.Windows.Forms.SaveFileDialog + $saveDialog.Filter = "Text files (*.txt)|*.txt|Log files (*.log)|*.log|All files (*.*)|*.*" + $saveDialog.FileName = "WinRepair_Log_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt" + $saveDialog.Title = "Export Repair Log" + $saveDialog.InitialDirectory = [Environment]::GetFolderPath("Desktop") + + if ($saveDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { + try { + $script:LogBuilder.ToString() | Out-File -FilePath $saveDialog.FileName -Encoding UTF8 + [System.Windows.Forms.MessageBox]::Show( + "Log exported successfully to:`n$($saveDialog.FileName)", + "WinRepair - Export", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Information + ) | Out-Null + } + catch { + [System.Windows.Forms.MessageBox]::Show( + "Failed to export log:`n$($_.Exception.Message)", + "WinRepair - Error", + [System.Windows.Forms.MessageBoxButtons]::OK, + [System.Windows.Forms.MessageBoxIcon]::Error + ) | Out-Null + } + } +}) + +# ═══════════════════════════════════════════════════════════════════════════════ +# FORM CLOSING — CONFIRM IF RUNNING +# ═══════════════════════════════════════════════════════════════════════════════ +$form.Add_FormClosing({ + param($sender, $e) + if ($script:IsRunning) { + $result = [System.Windows.Forms.MessageBox]::Show( + "A repair operation is currently in progress.`n`nAre you sure you want to close WinRepair?", + "WinRepair - Close", + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) + if ($result -ne [System.Windows.Forms.DialogResult]::Yes) { + $e.Cancel = $true + } else { + $script:CancelRequested = $true + if ($null -ne $script:CurrentProcess -and -not $script:CurrentProcess.HasExited) { + try { $script:CurrentProcess.Kill() } catch {} + } + } + } +}) + +# ═══════════════════════════════════════════════════════════════════════════════ +# CHECK FOR PENDING WINDOWS UPDATES +# ═══════════════════════════════════════════════════════════════════════════════ +function Test-PendingUpdates { + try { + Write-Log "Checking for pending Windows Updates..." -Level "INFO" + [System.Windows.Forms.Application]::DoEvents() + + $session = New-Object -ComObject Microsoft.Update.Session + $searcher = $session.CreateUpdateSearcher() + $results = $searcher.Search("IsInstalled=0 AND IsHidden=0") + $pendingCount = $results.Updates.Count + + if ($pendingCount -gt 0) { + Write-Log "Found $pendingCount pending Windows Update(s)!" -Level "WARN" + + $updateList = "" + $maxShow = [Math]::Min($pendingCount, 5) + for ($u = 0; $u -lt $maxShow; $u++) { + $updateList += " - $($results.Updates.Item($u).Title)`n" + } + if ($pendingCount -gt 5) { + $updateList += " ... and $($pendingCount - 5) more`n" + } + + $msg = "WARNING: PENDING WINDOWS UPDATES DETECTED`n`n" + $msg += "$pendingCount update(s) are waiting to be installed:`n`n" + $msg += $updateList + $msg += "`nPending updates can cause repair tools (SFC, DISM) to fail or" + $msg += " report false errors.`n`n" + $msg += "It is strongly recommended to install all updates first.`n`n" + $msg += "Click 'Yes' to exit and open Windows Update.`n" + $msg += "Click 'No' to continue anyway (not recommended)." + + $result = [System.Windows.Forms.MessageBox]::Show( + $msg, + "WinRepair - Pending Updates", + [System.Windows.Forms.MessageBoxButtons]::YesNo, + [System.Windows.Forms.MessageBoxIcon]::Warning + ) + + if ($result -eq [System.Windows.Forms.DialogResult]::Yes) { + Write-Log "User chose to exit and install updates" -Level "INFO" + try { + Start-Process "ms-settings:windowsupdate" + } catch { + Start-Process "control" -ArgumentList "/name Microsoft.WindowsUpdate" + } + return $false # Signal to exit + } else { + Write-Log "User chose to continue despite pending updates" -Level "WARN" + return $true + } + } else { + Write-Log "No pending Windows Updates found - good to go!" -Level "SUCCESS" + return $true + } + } + catch { + Write-Log "Could not check for updates: $($_.Exception.Message)" -Level "WARN" + Write-Log "Continuing without update check..." -Level "INFO" + return $true + } +} + +# ═══════════════════════════════════════════════════════════════════════════════ +# LAUNCH +# ═══════════════════════════════════════════════════════════════════════════════ +Write-Log "WinRepair initialized - System theme: $($script:Theme) mode" -Level "INFO" +Write-Log "Running as Administrator: Yes" -Level "INFO" +Write-Log "Windows version: $([System.Environment]::OSVersion.VersionString)" -Level "INFO" +Write-Log "" + +# Check for pending updates before proceeding +$form.Add_Shown({ + $script:lblStatus.Text = "Checking for pending Windows Updates..." + $form.Cursor = [System.Windows.Forms.Cursors]::WaitCursor + [System.Windows.Forms.Application]::DoEvents() + + $canProceed = Test-PendingUpdates + + $form.Cursor = [System.Windows.Forms.Cursors]::Default + if (-not $canProceed) { + $form.Close() + return + } + $script:lblStatus.Text = "Ready - Click 'Run System Repair' to begin" + Write-Log "" + Write-Log "Ready. Click 'Run System Repair' to begin." -Level "INFO" +}) + +[System.Windows.Forms.Application]::Run($form)