[CmdletBinding()] param( [switch]$NoPause ) $ErrorActionPreference = "Continue" # Must run elevated (required for HKLM MSI uninstall + ProgramData + all-users cleanup) $current = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = [Security.Principal.WindowsPrincipal]::new($current) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "Please run this script as Administrator." -ForegroundColor Red if ([Environment]::UserInteractive -and -not $NoPause) { Read-Host "Press Enter to close" } return } function Write-Info($msg) { Write-Host $msg -ForegroundColor Cyan } function Write-Warn($msg) { Write-Host $msg -ForegroundColor Yellow } function Write-Err($msg) { Write-Host $msg -ForegroundColor Red } $displayNamePatterns = @( "*Level Screen Recorder*", "*Level Screen Recorder Deployment Tool*" ) Write-Info "Stopping running processes..." $procNames = @("Level Screen Recorder", "Update", "LevelAIDeploymentTool") foreach ($n in $procNames) { Get-Process -Name $n -ErrorAction SilentlyContinue | ForEach-Object { try { Stop-Process -Id $_.Id -Force -ErrorAction Stop Write-Info "Stopped process: $($_.ProcessName) [$($_.Id)]" } catch { Write-Warn "Could not stop process $($_.ProcessName): $($_.Exception.Message)" } } } Write-Info "Removing legacy/current scheduled tasks..." if (Get-Command Get-ScheduledTask -ErrorAction SilentlyContinue) { Get-ScheduledTask -ErrorAction SilentlyContinue | Where-Object { $_.TaskName -eq "LevelScreenRecorder_KeepAlive" -or $_.TaskName -like "LevelScreenRecorder_KeepAlive_*" -or $_.TaskName -like "LevelScreenRecorder_OnUnlock_*" } | ForEach-Object { try { Unregister-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath -Confirm:$false -ErrorAction Stop Write-Info "Removed task: $($_.TaskPath)$($_.TaskName)" } catch { Write-Warn "Failed to remove task $($_.TaskName): $($_.Exception.Message)" } } } else { $taskQuery = schtasks /Query /FO CSV /NH 2>$null if ($LASTEXITCODE -eq 0) { $tasks = $taskQuery | ConvertFrom-Csv -Header TaskName,NextRun,Status foreach ($t in $tasks) { if ($t.TaskName -eq "LevelScreenRecorder_KeepAlive" -or $t.TaskName -like "*LevelScreenRecorder_KeepAlive_*" -or $t.TaskName -like "*LevelScreenRecorder_OnUnlock_*") { schtasks /Delete /TN $t.TaskName /F | Out-Null Write-Info "Removed task: $($t.TaskName)" } } } } Write-Info "Uninstalling MSI entries (HKLM/HKCU)..." $uninstallRoots = @( "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKCU:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" ) $seen = @{} foreach ($root in $uninstallRoots) { Get-ItemProperty -Path $root -ErrorAction SilentlyContinue | Where-Object { $dn = $_.DisplayName $dn -and ($displayNamePatterns | ForEach-Object { $dn -like $_ } | Where-Object { $_ } | Measure-Object).Count -gt 0 } | ForEach-Object { $dn = $_.DisplayName $key = $_.PSChildName if ($seen.ContainsKey($key)) { return } $seen[$key] = $true # Prefer product code uninstall when key is GUID if ($key -match '^\{[0-9A-Fa-f-]+\}$') { Write-Info "Uninstalling MSI: $dn ($key)" $p = Start-Process msiexec.exe -ArgumentList "/x $key /qn /norestart" -Wait -PassThru if ($p.ExitCode -ne 0) { Write-Warn "MSI uninstall exit code $($p.ExitCode) for $dn" } return } # Fallback to QuietUninstallString / UninstallString $u = $_.QuietUninstallString if (-not $u) { $u = $_.UninstallString } if ($u) { Write-Info "Running uninstall command for: $dn" try { Start-Process -FilePath "cmd.exe" -ArgumentList "/c $u" -Wait | Out-Null } catch { Write-Warn "Failed uninstall command for ${dn}: $($_.Exception.Message)" } } } } Write-Info "Uninstalling per-user Squirrel installs..." $profiles = Get-ChildItem "C:\Users" -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notin @("All Users","Default","Default User","Public") } foreach ($p in $profiles) { $local = Join-Path $p.FullName "AppData\Local" $roots = @( (Join-Path $local "LevelAI"), (Join-Path $local "levelAI") ) foreach ($root in $roots) { $updateExe = Join-Path $root "Update.exe" if (Test-Path $updateExe) { Write-Info "Running Squirrel uninstall: $updateExe" try { Start-Process -FilePath $updateExe -ArgumentList "--uninstall" -Wait -ErrorAction Stop | Out-Null } catch { Write-Warn "Squirrel uninstall failed at ${updateExe}: $($_.Exception.Message)" } Start-Sleep -Seconds 2 try { if (Test-Path $root) { Remove-Item -Path $root -Recurse -Force -ErrorAction Stop Write-Info "Removed folder: $root" } } catch { Write-Warn "Failed to remove folder ${root}: $($_.Exception.Message)" } } } } Write-Info "Removing shortcuts..." foreach ($p in $profiles) { $startup = Join-Path $p.FullName "AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup" $shortcuts = @( (Join-Path $startup "Level Screen Recorder.lnk"), (Join-Path $startup "LevelScreenRecorder.lnk"), (Join-Path $p.FullName "Desktop\Level Screen Recorder.lnk"), (Join-Path $p.FullName "AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Level AI\Level Screen Recorder.lnk"), (Join-Path $p.FullName "AppData\Roaming\Microsoft\Windows\Start Menu\Programs\levelAI\Level Screen Recorder.lnk") ) foreach ($s in $shortcuts) { if (Test-Path $s) { try { Remove-Item -Path $s -Force -ErrorAction Stop Write-Info "Removed shortcut: $s" } catch { Write-Warn "Failed to remove shortcut ${s}: $($_.Exception.Message)" } } } } $programDataStartup = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\LevelAIDeploymentTool.lnk" if (Test-Path $programDataStartup) { try { Remove-Item -Path $programDataStartup -Force -ErrorAction Stop Write-Info "Removed ProgramData startup shortcut: $programDataStartup" } catch { Write-Warn "Failed to remove ProgramData startup shortcut: $($_.Exception.Message)" } } Write-Host "" Write-Host "Cleanup complete. Reboot/sign-out+sign-in before reinstall." -ForegroundColor Green if ([Environment]::UserInteractive -and -not $NoPause) { Read-Host "Press Enter to close" }