# OmniDx Tune, the free look's copy: it reads this PC and changes nothing. The parts that change Windows are not in it. <# OmniDx Tune - the Cut. One run, on one PC. It reads the machine, makes a restore point and an undo script, then cuts the process count and tunes the system for competitive play: every startup app off, the services nobody uses set to start only when asked, the preinstalled apps nobody opens removed, telemetry and background activity off, an "OmniDx" power plan built for performance, the network stack set for low latency, Discord / Spotify / the browsers told to behave, a profile per game, and a BIOS checklist written for the exact board it found. It is deliberately careful: - nothing runs until a restore point exists (or you said not to make one) - every registry value and every service start type it changes is recorded first, and undo.ps1 puts them all back in reverse order - Defender, the firewall, Secure Boot, the CPU mitigations and BitLocker are not touched. Windows Update stays; Extreme (or -ManualUpdates) only stops it acting on its own, so it updates when you press Check for updates. Memory integrity (VBS/HVCI) is off only when you pass -Aggressive and only after it has told you what that means - what it keeps is decided by what it found: a laptop keeps its battery, touch and hibernation; a paired Bluetooth headset keeps Bluetooth; a real printer keeps the spooler; Windows Hello keeps biometrics; Wi-Fi keeps the Wi-Fi service; a VPN keeps its adapter's services - it measures. The report says how many processes there were, how many there are, and what is still running - never a number it did not count Run it through the one-liner on omnidx.net, which handles the key and the elevation: irm omnidx.net/go.ps1 | iex Or by hand, in an administrator PowerShell: & ([scriptblock]::Create((irm https://omnidx.net/tune/omnidx.ps1))) -Key TUNE-XXXX-XXXX-XXXX-XXXX Windows 10 (1903+) and Windows 11. PowerShell 5.1, which every one of them has. #> [CmdletBinding()] param( # The key from the page after you paid. Locks to this PC on first run. [string]$Key = $env:OMNIDX_KEY, # The licence server. When not given, read from omnidx.net/tune/config.json. A first run needs it: nothing else can vouch for a key. [string]$Api = $env:OMNIDX_API, # Also switch memory integrity (VBS / HVCI) off. More frames, less security. Asked about first. [switch]$Aggressive, # Remove the Xbox apps and set their services to manual. Off by default: Game Pass and Minecraft need them. [switch]$CutXbox, # Extreme (caution): the standard tune plus every extra service, Game Bar entirely, animations and transparency, # the taskbar search box, notification toasts and badges, multi-plane overlay, memory compression, SysMain on SSDs, # Windows Search, the dynamic tick, and the Xbox pieces unless Game Pass or Minecraft is in use. A few more frames and # steadier lows for fewer conveniences. Asked about first; recorded and undone like everything else. [switch]$Extreme, # Startup entries to leave enabled, by the name Task Manager shows (e.g. -Keep 'Wallpaper Engine','RTSS'). [string[]]$Keep = @(), # Instant sign-in (comes with Extreme; -NoAutoSignIn leaves it out): Windows signs this account in by itself at every # start and asks for no PIN or password on waking. -SignInPassword is the account's password, for one that has one: # a SecureString from the app's box (never a command line), kept in Windows' own protected secret store. [switch]$AutoSignIn, [switch]$NoAutoSignIn, [System.Security.SecureString]$SignInPassword, # Windows Update on your say-so (comes with Extreme; -KeepAutoUpdates leaves it out, -ManualUpdates asks for it in the # standard tune too): nothing downloads, installs or restarts by itself, and Settings > Windows Update > Check for # updates still gets every update whenever you press it. Windows Update never swaps a driver; a new Windows version waits a year. [switch]$ManualUpdates, [switch]$KeepAutoUpdates, # Cuts you choose one at a time, each explained in the app and the report (what it does, the risk, what it saves): # Office Dolby Realtek Nahimic Waves Dts AmdEvents Discovery Diagnostics Printing FileSharing Storage NvidiaPanel. None unless named; recorded and undone like the rest. # -RestoreUncut (the app passes it: its boxes show what is cut now) puts back an earlier chosen cut not named here. [string[]]$Cut = @(), [switch]$RestoreUncut, # Point DNS at Cloudflare (1.1.1.1). Off by default; your router's DNS is usually fine. [switch]$Dns, # Skip the restore point. Only sensible if System Restore is broken on this machine. [switch]$NoRestorePoint, # Put everything back from the last run and stop. [switch]$Undo, # Put back only the newest run (the Extreme one, say) and leave the earlier runs in place. [switch]$UndoLast, # Read the machine and count the processes. Change nothing. [switch]$Report, # Keep the camera frame server and screen capture as if OBS were installed (it is detected on its own). [switch]$Streamer, # Check a key's format and checksum, then stop. Nothing is read or bound. [switch]$CheckKey, # Say what is still in place from earlier runs and what Windows has put back. Changes nothing. [switch]$Status, # Accepted for older commands. Since 1.117.0 nothing of the tune stays to run at every sign-in, so there is no keep task to leave out; # a run removes one an earlier version left either way. [switch]$NoKeep, # Leave the service hosts as Windows splits them (a process for every service): about twenty processes more after a # restart. A grouping an earlier run made goes back to Windows' own setting. [switch]$NoGroup, # Zip the logs, the machine as read, the numbers and the change records to the desktop for support. No key, no registry exports, none of your files. [switch]$SupportBundle, # Do not leave the one-shot task that writes the after-restart process count. [switch]$NoAfterCount, # Answer every question yes. [switch]$Yes, # Phases to leave out, by name: startup services tasks apps debloat telemetry # system power network programs games gamefiles nvidia extreme signin updates choice hosts cleanup keep. The app uses this. [string[]]$Skip = @(), # Never restart or offer to. The app has its own button for that. [switch]$NoRestart, # Open the window instead of the console flow. [switch]$Gui, # With -Gui: render the window to this PNG without showing it, then stop. [string]$Screenshot, # With -Screenshot: which tab to draw (Tune, the default, Edition or Info). [ValidateSet('Tune', 'Edition', 'Info')][string]$ScreenshotTab = 'Tune', # Read the PC and print one JSON line for the app: machine, startup entries, counts. [switch]$Probe, # Install these apps clean (ids from the list in the app's OmniDx OS tab, comma-separated), and nothing else. [string[]]$InstallApps = @(), # Print how much of itself the script can see (the app needs its own text). [switch]$SelfTest ) $ErrorActionPreference = 'Continue' $ProgressPreference = 'SilentlyContinue' # Smart App Control (or an organisation's app control policy) runs PowerShell in Constrained Language Mode, where # nothing here can read or change what it needs. Said plainly, before anything is read or changed. if ($ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage') { Write-Host '' Write-Host ' OmniDx Tune cannot run here: PowerShell is in Constrained Language Mode. Nothing was changed.' -ForegroundColor Yellow Write-Host ' On a home PC that is Smart App Control (Windows Security > App & browser control > Smart App Control settings);' -ForegroundColor Gray Write-Host " at work or school it is the organisation's app control policy." -ForegroundColor Gray return } # The 32-bit Windows PowerShell (x86) on a 64-bit Windows sees the 32-bit side of the registry and of System32: # bcdedit and DISM are not there and half the settings would land where Windows never reads them. The one command # hands over to the 64-bit one; this is for the script run by hand. if ([Environment]::Is64BitOperatingSystem -and -not [Environment]::Is64BitProcess) { Write-Host '' Write-Host ' OmniDx Tune needs the 64-bit Windows PowerShell; this window is the 32-bit one (x86). Nothing was changed.' -ForegroundColor Yellow Write-Host ' Open "Windows PowerShell" (not "Windows PowerShell (x86)") and run it again.' -ForegroundColor Gray return } # Windows 10's PowerShell can still offer only TLS 1.0 for its own web calls (the key check, the settings file) when the # window was started without the one command, which sets this too; the licence server wants TLS 1.2. try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } $script:Version = '1.117.3' $script:Root = 'C:\OmniDx' # The -Cut names, taken here where $Cut can only mean the parameter: PowerShell's names ignore case, so a function with a # list of its own called $cut would hide it from anything it calls (1.91.0's first Windows check: the third-party sweep). $script:CutNames = @($Cut) # To the second: two runs inside one minute (a refusal, then a retry) once shared a stamp, and the second's # record would have overwritten the first's, taking its undo with it. $script:Stamp = Get-Date -Format 'yyyy-MM-dd_HH-mm-ss' $script:Changes = New-Object System.Collections.ArrayList $script:Log = New-Object System.Collections.ArrayList $script:Warnings = New-Object System.Collections.ArrayList $script:Kept = New-Object System.Collections.ArrayList $script:RestorePointMade = $false $script:GamesFound = New-Object System.Collections.ArrayList $script:OtherGames = @() $script:OtherGameList = @() $script:ExtremeDeclined = $false $script:Timer = [System.Diagnostics.Stopwatch]::StartNew() # The steps outside the timed phases (the read, the key, the two counts, the safety net, keep.ps1, the report), # each as the seconds since the previous mark: 1.72.0's record showed a re-run's phases at 13 of its 29 seconds. $script:Steps = [ordered]@{}; $script:LastMark = 0.0 $script:Running = @() function Mark-Step([string]$name) { $now = $script:Timer.Elapsed.TotalSeconds; $script:Steps[$name] = [math]::Round($now - $script:LastMark, 1); $script:LastMark = $now } # The window runs the work in a second PowerShell instance, which needs this # script's text. From a file that is the file; from memory (the one command) # it is the scriptblock the bootstrapper made. $script:SelfText = '' try { $script:SelfText = [string]$MyInvocation.MyCommand.ScriptBlock } catch { } if ((-not $script:SelfText -or $script:SelfText.Length -lt 1000) -and $PSCommandPath -and (Test-Path $PSCommandPath)) { $script:SelfText = Get-Content $PSCommandPath -Raw } # The signed-in person's hive and folders. Normally the same as the account # running this; when a standard user gave an administrator's password at the # UAC prompt, HKCU would be the administrator's hive and every per-user tweak # would land on the wrong account. Resolve-User (in Main) fixes these up. $script:HKCU = 'HKCU:' $script:AppData = $env:APPDATA $script:LocalAppData = $env:LOCALAPPDATA $script:UserProfile = $env:USERPROFILE $script:UserName = $env:USERNAME # --------------------------------------------------------------------------- # saying things # --------------------------------------------------------------------------- function Say([string]$t, [string]$c = 'Gray') { Write-Host $t -ForegroundColor $c; [void]$script:Log.Add($t) } function Head([string]$t) { Write-Host ''; Write-Host ("== " + $t) -ForegroundColor Magenta; [void]$script:Log.Add(""); [void]$script:Log.Add("== $t") } function Did([string]$t) { Write-Host (" + " + $t) -ForegroundColor DarkGray; [void]$script:Log.Add(" + $t") } function Warn([string]$t) { Write-Host (" ! " + $t) -ForegroundColor Yellow; [void]$script:Log.Add(" ! $t"); [void]$script:Warnings.Add($t) } function Keep([string]$what, [string]$why) { [void]$script:Kept.Add(("{0}: {1}" -f $what, $why)) } function Plural([int]$n, [string]$one, [string]$many) { if ($n -eq 1) { return "$n $one" } else { return "$n $many" } } function Ask([string]$q) { if ($Yes) { return $true } $a = Read-Host (" " + $q + " [Y/n]") return ($a -eq '' -or $a -match '^[Yy]') } # --------------------------------------------------------------------------- # the key # --------------------------------------------------------------------------- $script:Alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' $script:KeySalt = 'omnidx-tune-2026' <# 32-bit FNV-1a, the same four lines as tunekey.js, worker.js and make-tune-key.py. PowerShell turns an overflowing product into a double and loses the low bits, so the multiply is done exactly with BigInteger and cut back to 32 bits. #> function Get-Fnv1a([string]$s) { [long]$h = 2166136261 foreach ($ch in $s.ToCharArray()) { $h = $h -bxor [long][int]$ch $h = [long](([bigint]$h * [bigint]16777619) % [bigint]4294967296) } return $h } function Get-KeyChecksum([string]$tag, [string]$payload) { [long]$h = Get-Fnv1a ("{0}:{1}:{2}" -f $script:KeySalt, $tag, $payload) $out = '' for ($i = 0; $i -lt 4; $i++) { $out += $script:Alphabet[[int]($h % 32)] $h = [long][math]::Floor($h / 32) + [long]($h % 7) } return $out } <# TUNE-XXXX-XXXX-XXXX-CCCC is one PC (a Squad order is three of them); the older SQUAD-XXXX-XXXX-XXXX-CCCC tag is three PCs on one key, still read. The last block is a checksum, so a mistyped key is caught here rather than on the server. The server is what makes a key real. #> function Read-Key([string]$raw) { $c = ($raw -replace '[^A-Za-z0-9]', '').ToUpperInvariant() if ($c -match '^(TUNE|SQUAD)([A-Z0-9]{12})([A-Z0-9]{4})$') { $tag = $Matches[1]; $payload = $Matches[2]; $sum = $Matches[3] foreach ($ch in $payload.ToCharArray()) { if ($script:Alphabet.IndexOf($ch) -lt 0) { return $null } } if ((Get-KeyChecksum $tag $payload) -ne $sum) { return $null } $seats = if ($tag -eq 'SQUAD') { 3 } else { 1 } $pretty = "{0}-{1}-{2}-{3}-{4}" -f $tag, $payload.Substring(0, 4), $payload.Substring(4, 4), $payload.Substring(8, 4), $sum return @{ tag = $tag; payload = $payload; key = $pretty; seats = $seats } } return $null } <# What makes this PC this PC: the board's UUID, its serial and the CPU id. Not the Windows install id, so a reinstall on the same machine still matches; not the disk, so a new SSD does not lose the key. #> function Get-Hwid { $csp = Get-CimInstance Win32_ComputerSystemProduct -ErrorAction SilentlyContinue $bb = Get-CimInstance Win32_BaseBoard -ErrorAction SilentlyContinue $cpu = Get-CimInstance Win32_Processor -ErrorAction SilentlyContinue | Select-Object -First 1 $raw = "{0}|{1}|{2}" -f $csp.UUID, $bb.SerialNumber, $cpu.ProcessorId $sha = [System.Security.Cryptography.SHA256]::Create() $bytes = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($raw)) return (($bytes | ForEach-Object { $_.ToString('x2') }) -join '').Substring(0, 16) } <# The server's view of a key, for status and the key check: issued or not, switched off by a refund or not, and on how many PCs. The server comes from -Api or from config.json; with neither, nothing is asked. #> function Get-ServerView([string]$key) { $api = $Api if (-not $api) { try { $web = New-Object System.Net.WebClient; $web.Headers['User-Agent'] = 'OmniDxTune/script' $cfg = $web.DownloadString('https://omnidx.net/tune/config.json?v=' + [DateTime]::UtcNow.Ticks) | ConvertFrom-Json if ($cfg.api) { $api = [string]$cfg.api } } catch { } } if (-not $api) { return $null } try { $r = Invoke-RestMethod -Method Post -Uri ("{0}/v1/tune/check" -f $api.TrimEnd('/')) -ContentType 'application/json' -Body (@{ key = $key } | ConvertTo-Json -Compress) -TimeoutSec 15 if ($r.ok) { return ("issued; on {0} of {1} PC{2}" -f $r.used, $r.seats, $(if ($r.seats -ne 1) { 's' } else { '' })) } return [string]$r.reason } catch { return $null } } function Test-Licence($parsed, [string]$hwid, $machine) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Test-Licence is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # recording every change, so undo.ps1 can put it back # --------------------------------------------------------------------------- function Record($entry) { [void]$script:Changes.Add($entry) } <# Settings an earlier version made and this one no longer does, each with why. A run puts each back the way undo would, from the record of the run that made it, and takes it out of that record so keep.ps1 stops putting it back. Undo of those runs then leaves it alone too. #> $script:Retired = @( @{ path = 'HKLM:\SOFTWARE\Microsoft\Windows\Dwm'; name = 'OverlayTestMode'; why = "the multi-plane overlay is back on (Extreme turned it off before 1.83.0): a borderless game keeps its own path to the screen while an overlay is drawn over it, instead of waiting a frame for Windows' compositor" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Control\Power\PowerThrottling'; name = 'PowerThrottlingOff'; why = "Windows' power throttling is back on (the tune turned it off before 1.83.0): it only slows programs in the background, never the game in front, and sends them to an Intel hybrid CPU's efficiency cores so the performance cores are the game's" } # 1.84.0 wrote a feature override for Resume's helper; take 36385891202 read the feature as Windows holds it (ntdll) # and found Windows' own default in force (priority 0, on) with the override present: it was never read. Taken out. @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Control\FeatureManagement\Overrides\8\56517033'; name = 'EnabledState'; why = "an unused feature override for Resume's helper is gone (Windows never read it)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Control\FeatureManagement\Overrides\8\56517033'; name = 'EnabledStateOptions'; why = "its second value too" } # 1.85.0 to 1.91.0 gave these eight a service host of their own (their type, own instead of shared): up to eight # processes at rest. They share their group's host again, as Windows runs them (1.92.0). # 1.98.0 had the sign-in keep task close Start's search at each sign-in; the restart film found Windows starting it again # within four seconds, so it saved nothing and flattered the count. Taken out (1.98.1). @{ path = 'HKLM:\SOFTWARE\OmniDx\Tune'; name = 'CloseSearchAtSignIn'; why = "Start's search is no longer closed at sign-in (Windows starts it again within seconds, so it saved nothing)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\Audiosrv'; name = 'Type'; why = "sound shares its service host again, as Windows has it (a process fewer after the restart)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\AudioEndpointBuilder'; name = 'Type'; why = "sound devices share their service host again (a process fewer)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\Dhcp'; name = 'Type'; why = "the network address shares its service host again (a process fewer)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache'; name = 'Type'; why = "name lookups (DNS) share their service host again (a process fewer)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\WlanSvc'; name = 'Type'; why = "Wi-Fi shares its service host again (a process fewer)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\bthserv'; name = 'Type'; why = "Bluetooth shares its service host again (a process fewer)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\Winmgmt'; name = 'Type'; why = "WMI shares its service host again (a process fewer)" } @{ path = 'HKLM:\SYSTEM\CurrentControlSet\Services\wuauserv'; name = 'Type'; why = "Windows Update shares its service host again (a process fewer while it runs)" } ) function Undo-Retired { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Undo-Retired is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } function Resolve-Hive([string]$p) { if ($p -like 'HKCU:*' -and $script:HKCU -ne 'HKCU:') { return $script:HKCU + $p.Substring(5) } return $p } function Set-Reg([string]$path, [string]$name, $value, [string]$kind = 'DWord') { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-Reg is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } function Remove-Reg([string]$path, [string]$name) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Remove-Reg is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # -Choice marks the record as a cut the person chose (Set-ChoiceCuts), so unticking it later puts it back; -Force lets # that putting-back move a disabled service to manual, which is otherwise left disabled on purpose. function Set-ServiceStart([string]$name, [string]$start, [string]$why = '', [switch]$NoStop, [string]$Choice = '', [switch]$Force) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-ServiceStart is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } function Save-Changes { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Save-Changes is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # undo # --------------------------------------------------------------------------- $script:UndoScript = @' <# OmniDx Tune - undo. Puts back every registry value and service start type the tune changed, in reverse order, from changes-latest.json next to this file. Restores the power plan that was active before and removes the OmniDx plan. Re-enables hibernation if it was on. Store apps that were removed are listed at the end with where to get them back. With no argument it walks back every run recorded here, newest first, so a PC tuned twice ends up as it was before the first run; each record is then moved to done\ so a later run starts clean. -File puts back one record only (the newest, for -UndoLast), moves it to done\ as well, and leaves the earlier runs standing. A keep task an earlier version left (nothing of the tune stays at sign-in since 1.117.0) goes either way. Run in an administrator PowerShell: powershell -ExecutionPolicy Bypass -File undo.ps1 #> param([string]$File) $ErrorActionPreference = 'Continue' $dir = $PSScriptRoot if ($File) { $files = @($File) } else { $files = @(Get-ChildItem $dir -Filter 'changes-*.json' -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne 'changes-latest.json' } | Sort-Object Name -Descending | ForEach-Object { $_.FullName }) } if (-not $files.Count) { Write-Host "No run recorded in $dir" -ForegroundColor Red; exit 1 } foreach ($f in $files) { if (-not (Test-Path $f)) { Write-Host "No changes file at $f" -ForegroundColor Red; exit 1 } } $removedApps = @() $done = 0; $failed = 0 # DISM work (a capability or an optional feature going back) can take minutes # each, and with Windows Update unreachable it can sit for an hour. Each gets # a few minutes in a background job; past that it is reported, and # Settings > Apps > Optional features finishes it later. function Invoke-Timed([scriptblock]$work, [object[]]$argList, [int]$seconds) { $job = Start-Job -ScriptBlock $work -ArgumentList $argList if (-not (Wait-Job $job -Timeout $seconds)) { Stop-Job $job -ErrorAction SilentlyContinue; Remove-Job $job -Force -ErrorAction SilentlyContinue; return 'timeout' } $state = $job.State; $errs = @($job.ChildJobs | ForEach-Object { $_.Error }).Count Receive-Job $job -ErrorAction SilentlyContinue | Out-Null; Remove-Job $job -Force -ErrorAction SilentlyContinue if ($state -eq 'Completed' -and $errs -eq 0) { return 'ok' } else { return 'failed' } } # NVIDIA driver profiles: the tune left the helper it used next to this file (omnidx-nv.cs); loaded once, when a # record needs it. $script:nvReady = $null function Open-Nv { if ($null -ne $script:nvReady) { return $script:nvReady } $script:nvReady = $false $src = Join-Path $dir 'omnidx-nv.cs' if (-not (Test-Path $src)) { Write-Host "NVIDIA profile records, but no omnidx-nv.cs next to this file; NVIDIA Control Panel > Manage 3D settings > Restore puts them back by hand" -ForegroundColor Yellow; return $false } try { if (-not ('OmniNv' -as [type])) { Add-Type -Path $src -ErrorAction Stop }; $e = [OmniNv]::Open(); if ($e) { Write-Host "NVIDIA driver: $e" -ForegroundColor Yellow } else { $script:nvReady = $true } } catch { Write-Host ("NVIDIA driver: {0}" -f $_.Exception.Message) -ForegroundColor Yellow } return $script:nvReady } # The legacy pieces go back in one DISM session (a session per piece cost # minutes); if DISM will not take them together they go one at a time, and # if the batch simply timed out (Windows Update out of reach) one at a time # would only time out again, so it says where to add them later instead. function Restore-Dism([string]$verb, [string]$flag, [string[]]$names, [string]$what) { if (-not $names -or -not $names.Count) { return 0 } if ($env:OMNIDX_SKIP_DISM -eq '1') { Write-Host ("{0} {1}(s) left for Settings > Apps > Optional features, as asked (OMNIDX_SKIP_DISM): {2}" -f $names.Count, $what, ($names -join ', ')) -ForegroundColor Yellow; return 0 } Write-Host ("{0} {1}(s) going back in one go (this can take a few minutes)..." -f $names.Count, $what) -ForegroundColor DarkGray $dargs = @('/online', $verb) + @($names | ForEach-Object { "$flag`:$_" }) + @('/NoRestart', '/Quiet') $r = Invoke-Timed { param($a) & dism.exe @a *>&1 | Out-Null; if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) { throw "dism exit $LASTEXITCODE" } } @(, $dargs) 600 if ($r -eq 'ok') { foreach ($n in $names) { Write-Host ("{0} back: {1}" -f $what, $n) -ForegroundColor DarkGray }; return $names.Count } if ($r -eq 'timeout') { Write-Host ("They did not come back in ten minutes; Windows Update is probably out of reach. Settings > Apps > Optional features adds them later: {0}" -f ($names -join ', ')) -ForegroundColor Yellow; return 0 } Write-Host "DISM would not take them together; one at a time..." -ForegroundColor Yellow $ok = 0 for ($i = 0; $i -lt $names.Count; $i++) { $n = $names[$i] $one = @('/online', $verb, "$flag`:$n", '/NoRestart', '/Quiet') $r = Invoke-Timed { param($a) & dism.exe @a *>&1 | Out-Null; if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 3010) { throw "dism exit $LASTEXITCODE" } } @(, $one) 300 if ($r -eq 'ok') { Write-Host ("{0} back: {1}" -f $what, $n) -ForegroundColor DarkGray; $ok++; continue } if ($r -eq 'timeout') { # One timeout means Windows Update is out of reach; the rest would only wait five minutes each for the same answer. $rest = @($names | Select-Object -Skip $i) Write-Host ("{0} did not come back in five minutes; Windows Update is probably out of reach. Settings > Apps > Optional features adds these later: {1}" -f $n, ($rest -join ', ')) -ForegroundColor Yellow break } Write-Host ("{0} did not come back here ({1}); Settings > Apps > Optional features adds it" -f $n, $r) -ForegroundColor Yellow } return $ok } $capsBack = New-Object System.Collections.ArrayList $featsBack = New-Object System.Collections.ArrayList foreach ($rec in $files) { $changes = @(Get-Content $rec -Raw | ConvertFrom-Json | ForEach-Object { $_ }) [array]::Reverse($changes) Write-Host ("Putting back {0} from {1}" -f $(if ($changes.Count -eq 1) { '1 change' } else { "$($changes.Count) changes" }), $rec) foreach ($c in $changes) { try { $deferred = $false switch ($c.type) { 'reg' { if ($c.removed) { # A binary or multi-string value comes back from the record's JSON as a list of numbers or strings (1.111.0: # the crash-shim values are binary). $v = $c.prev if ($c.kind -eq 'Binary') { $v = [byte[]]@($v | Where-Object { $null -ne $_ } | ForEach-Object { [byte]$_ }) } if ($c.kind -eq 'MultiString') { $v = [string[]]@($v | Where-Object { $null -ne $_ }) } if (-not (Test-Path $c.path)) { New-Item -Path $c.path -Force | Out-Null } New-ItemProperty -Path $c.path -Name $c.name -Value $v -PropertyType $c.kind -Force | Out-Null } elseif ($c.had) { $v = $c.prev if ($c.kind -eq 'Binary' -and $v) { $v = [byte[]]($v | ForEach-Object { [byte]$_ }) } if ($c.kind -eq 'MultiString') { $v = [string[]]@($v | Where-Object { $null -ne $_ }) } New-ItemProperty -Path $c.path -Name $c.name -Value $v -PropertyType $c.kind -Force | Out-Null } else { Remove-ItemProperty -Path $c.path -Name $c.name -Force -ErrorAction SilentlyContinue if (-not $c.keyExisted) { $left = Get-Item $c.path -ErrorAction SilentlyContinue if ($left -and $left.ValueCount -eq 0 -and $left.SubKeyCount -eq 0) { Remove-Item $c.path -Force -ErrorAction SilentlyContinue } } } Write-Host ("restored {0}\{1}" -f $c.path, $c.name) -ForegroundColor DarkGray } 'service' { $s = switch ($c.prev) { 'Disabled' { 'disabled' } 'Manual' { 'demand' } 'Automatic' { 'auto' } 'Boot' { 'boot' } 'System' { 'system' } default { 'auto' } } & sc.exe config $c.name start= $s | Out-Null Write-Host ("service {0} -> {1}" -f $c.name, $c.prev) -ForegroundColor DarkGray } 'power' { if ($c.prev) { & powercfg /setactive $c.prev | Out-Null } if ($c.created) { & powercfg /delete $c.created | Out-Null } Write-Host "power plan restored" -ForegroundColor DarkGray } 'hibernate' { if ($c.prev -eq 'on') { & powercfg /h on | Out-Null; Write-Host "hibernation on" -ForegroundColor DarkGray } } 'netsh' { foreach ($cmd in $c.restore) { & cmd.exe /c $cmd | Out-Null }; Write-Host "tcp settings restored" -ForegroundColor DarkGray } 'nic' { try { Set-NetAdapterAdvancedProperty -Name $c.adapter -DisplayName $c.property -DisplayValue $c.prev -NoRestart -ErrorAction Stop } catch { } Write-Host ("{0}: {1} -> {2}" -f $c.adapter, $c.property, $c.prev) -ForegroundColor DarkGray } 'nickw' { try { Get-NetAdapterAdvancedProperty -Name $c.adapter -ErrorAction Stop | Where-Object { $_.RegistryKeyword -eq $c.keyword } | Set-NetAdapterAdvancedProperty -RegistryValue $c.prev -NoRestart -ErrorAction Stop } catch { } Write-Host ("{0}: {1} -> {2}" -f $c.adapter, $c.keyword, $c.prev) -ForegroundColor DarkGray } 'nicpower' { try { Enable-NetAdapterPowerManagement -Name $c.adapter -ErrorAction Stop } catch { } } 'dns' { try { Set-DnsClientServerAddress -InterfaceIndex $c.index -ResetServerAddresses -ErrorAction Stop } catch { } } 'task' { try { Enable-ScheduledTask -TaskPath $c.path -TaskName $c.name -ErrorAction Stop | Out-Null } catch { } } 'signin' { $wl = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' foreach ($v in 'AutoAdminLogon', 'DefaultUserName', 'DefaultDomainName', 'AutoLogonCount') { $pv = $c.prev.$v if ($null -ne $pv -and "$pv" -ne '') { New-ItemProperty -Path $wl -Name $v -Value $pv -PropertyType $(if ($v -eq 'AutoLogonCount') { 'DWord' } else { 'String' }) -Force | Out-Null } elseif ($v -eq 'AutoAdminLogon') { New-ItemProperty -Path $wl -Name $v -Value '0' -PropertyType String -Force | Out-Null } else { Remove-ItemProperty -Path $wl -Name $v -Force -ErrorAction SilentlyContinue } } if (-not ('OmniDxSignIn' -as [type])) { Add-Type -TypeDefinition 'using System; using System.Runtime.InteropServices; public static class OmniDxSignIn { [StructLayout(LayoutKind.Sequential)] struct U { public ushort L; public ushort M; public IntPtr B; } [StructLayout(LayoutKind.Sequential)] struct OA { public int Len; public IntPtr R; public IntPtr N; public uint A; public IntPtr S; public IntPtr Q; } [DllImport("advapi32.dll")] static extern uint LsaOpenPolicy(IntPtr s, ref OA o, uint a, out IntPtr h); [DllImport("advapi32.dll", EntryPoint = "LsaStorePrivateData")] static extern uint StoreData(IntPtr h, ref U k, ref U d); [DllImport("advapi32.dll", EntryPoint = "LsaStorePrivateData")] static extern uint ClearData(IntPtr h, ref U k, IntPtr d); [DllImport("advapi32.dll")] static extern uint LsaClose(IntPtr h); [DllImport("advapi32.dll")] static extern int LsaNtStatusToWinError(uint s); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool LogonUser(string u, string d, string p, int t, int v, out IntPtr h); [DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h); static U Str(string s) { U u = new U(); u.B = Marshal.StringToHGlobalUni(s); u.L = (ushort)(s.Length * 2); u.M = (ushort)(s.Length * 2 + 2); return u; } public static int Secret(string key, string value) { OA o = new OA(); o.Len = Marshal.SizeOf(typeof(OA)); IntPtr h; uint st = LsaOpenPolicy(IntPtr.Zero, ref o, 0xF0FFF, out h); if (st != 0) return LsaNtStatusToWinError(st); U k = Str(key); try { if (value == null) { st = ClearData(h, ref k, IntPtr.Zero); } else { U d = Str(value); try { st = StoreData(h, ref k, ref d); } finally { Marshal.FreeHGlobal(d.B); } } } finally { Marshal.FreeHGlobal(k.B); LsaClose(h); } return LsaNtStatusToWinError(st); } public static int TryLogon(string user, string domain, string password) { IntPtr t; if (LogonUser(user, domain, password, 2, 0, out t)) { CloseHandle(t); return 0; } return Marshal.GetLastWin32Error(); } }' } [void][OmniDxSignIn]::Secret('DefaultPassword', $null) if ($null -ne $c.prevPasswordLess) { New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device' -Name DevicePasswordLessBuildVersion -Value ([int]$c.prevPasswordLess) -PropertyType DWord -Force | Out-Null } if ($c.scheme) { & powercfg /setacvalueindex $c.scheme SUB_NONE CONSOLELOCK ([int]$c.ac) 2>$null | Out-Null; & powercfg /setdcvalueindex $c.scheme SUB_NONE CONSOLELOCK ([int]$c.dc) 2>$null | Out-Null } Write-Host "sign-in back to how it was: a password or PIN at start and on waking" -ForegroundColor DarkGray } 'gamefile' { if (Test-Path -LiteralPath $c.path) { $gb = [IO.File]::ReadAllBytes($c.path) $genc = New-Object System.Text.UTF8Encoding($false) if ($gb.Length -ge 2 -and $gb[0] -eq 0xFF -and $gb[1] -eq 0xFE) { $genc = New-Object System.Text.UnicodeEncoding($false, $true) } elseif ($gb.Length -ge 3 -and $gb[0] -eq 0xEF -and $gb[1] -eq 0xBB -and $gb[2] -eq 0xBF) { $genc = New-Object System.Text.UTF8Encoding($true) } $gk = [regex]::Escape([string]$c.key) $gpat = switch ($c.format) { 'ini' { "^(\s*$gk\s*=\s*)(.*?)(\s*)$" } 'kv' { "^(\s*`"$gk`"\s+`")(.*?)(`"\s*)$" } default { "^($gk\:)(.*?)(\s*)$" } } $gparts = [regex]::Split([IO.File]::ReadAllText($c.path, $genc), '(\r?\n)'); $ghit = $false for ($gi = 0; $gi -lt $gparts.Count; $gi += 2) { $gm = [regex]::Match($gparts[$gi], $gpat); if ($gm.Success) { if ($gm.Groups[2].Value -eq [string]$c.value) { $gparts[$gi] = $gm.Groups[1].Value + [string]$c.prev + $gm.Groups[3].Value; $ghit = $true }; break } } if ($ghit) { [IO.File]::WriteAllText($c.path, ($gparts -join ''), $genc); Write-Host ("{0}: {1} back to {2}" -f $c.game, $c.key, $c.prev) -ForegroundColor DarkGray } else { Write-Host ("{0}: {1} was changed after the tune set it; left as it is" -f $c.game, $c.key) -ForegroundColor DarkGray } } } 'file' { if (Test-Path $c.backup) { Copy-Item $c.backup $c.path -Force; Write-Host ("file restored {0}" -f $c.path) -ForegroundColor DarkGray } } 'appx' { $removedApps += $c.name } 'fsutil' { $setting = $(if ($c.setting) { $c.setting } else { 'disablelastaccess' }); & fsutil behavior set $setting $c.prev | Out-Null; Write-Host ("fsutil {0} -> {1}" -f $setting, $c.prev) -ForegroundColor DarkGray } 'mmagent' { try { if ($c.feature -eq 'MemoryCompression') { Enable-MMAgent -MemoryCompression -ErrorAction Stop } else { Enable-MMAgent -ApplicationPreLaunch -ErrorAction Stop } } catch { } } 'bcdedit' { & bcdedit /deletevalue $c.name 2>$null | Out-Null; Write-Host ("boot setting {0} back to default" -f $c.name) -ForegroundColor DarkGray } 'bcdset' { & bcdedit /set $c.name $c.prev 2>$null | Out-Null; Write-Host ("boot setting {0} back to {1}" -f $c.name, $c.prev) -ForegroundColor DarkGray } 'bcdtimeout' { & bcdedit /timeout ([int]$c.prev) 2>$null | Out-Null; Write-Host ("boot menu wait back to {0} s" -f $c.prev) -ForegroundColor DarkGray } 'task-created' { try { Unregister-ScheduledTask -TaskName $c.name -Confirm:$false -ErrorAction Stop } catch { } } 'mppref' { $p = @{}; $p[$c.name] = $c.prev; try { Set-MpPreference @p -ErrorAction Stop; Write-Host ("Defender {0} -> {1}" -f $c.name, $c.prev) -ForegroundColor DarkGray } catch { } } 'capability' { if (-not $capsBack.Contains($c.name)) { [void]$capsBack.Add($c.name) }; $deferred = $true } 'feature' { if (-not $featsBack.Contains($c.name)) { [void]$featsBack.Add($c.name) }; $deferred = $true } 'onedrive' { if (Test-Path $c.setup) { $p = Start-Process $c.setup -ArgumentList '/silent' -WindowStyle Hidden -PassThru -ErrorAction SilentlyContinue if ($p) { if ($p.WaitForExit(180000)) { Write-Host "OneDrive reinstalled" -ForegroundColor DarkGray } else { Write-Host "OneDrive's installer is still running in the background; it finishes on its own" -ForegroundColor Yellow } } } } 'nvdrs' { # A game's NVIDIA driver setting: the value it had, or off the profile so the driver's own applies again. if (-not (Open-Nv)) { throw 'NVIDIA driver interface not available' } $np = [IntPtr]::Zero; $nn = '' $ns = $(if ($c.global) { [OmniNv]::GlobalProfile([ref]$np) } elseif ($c.own) { [OmniNv]::FindProfile([string]$c.own, [ref]$np) } else { [OmniNv]::FindApp([string]$c.exe, [ref]$np, [ref]$nn) }) if ($ns -eq 0) { $nr = $(if ($null -eq $c.prev) { [OmniNv]::DeleteSetting($np, [uint32]$c.id) } else { [OmniNv]::SetDword($np, [uint32]$c.id, [uint32]$c.prev) }) [void][OmniNv]::Save() Write-Host ("NVIDIA {0}: setting 0x{1:X8} {2}" -f $(if ($c.global) { 'every game' } else { $c.exe }), [uint32]$c.id, $(if ($null -eq $c.prev) { 'back to the driver default' } else { "back to $($c.prev)" })) -ForegroundColor DarkGray } } 'nvprofile' { if (Open-Nv) { $np = [IntPtr]::Zero; if ([OmniNv]::FindProfile([string]$c.name, [ref]$np) -eq 0) { [void][OmniNv]::DeleteProfile($np); [void][OmniNv]::Save(); Write-Host ("NVIDIA profile removed: {0}" -f $c.name) -ForegroundColor DarkGray } } } } if (-not $deferred) { $done++ } } catch { Write-Host ("could not undo {0}: {1}" -f ($c | ConvertTo-Json -Compress), $_.Exception.Message) -ForegroundColor Yellow; $failed++ } } try { $doneDir = Join-Path $dir 'done'; New-Item -ItemType Directory -Path $doneDir -Force | Out-Null Move-Item $rec (Join-Path $doneDir ([IO.Path]::GetFileName($rec))) -Force } catch { } } # What is left: changes-latest.json follows the newest remaining run. A keep task an earlier version (before 1.117.0) # left goes whatever its record says: nothing of the tune stays at sign-in any more. $left = @(Get-ChildItem $dir -Filter 'changes-20*.json' -ErrorAction SilentlyContinue | Sort-Object Name -Descending) if ($left.Count) { Copy-Item $left[0].FullName (Join-Path $dir 'changes-latest.json') -Force; Write-Host ("{0} earlier run(s) still in place" -f $left.Count) -ForegroundColor DarkGray } else { Remove-Item (Join-Path $dir 'changes-latest.json') -Force -ErrorAction SilentlyContinue } try { Unregister-ScheduledTask -TaskName 'OmniDx keep' -Confirm:$false -ErrorAction Stop; Write-Host "keep task from an earlier version removed" -ForegroundColor DarkGray } catch { } # A legacy piece that did not come back is not a failure of the undo: Settings > Apps > Optional features adds it later, and the line says so. $left = 0 $n = Restore-Dism '/Add-Capability' '/CapabilityName' @($capsBack) 'capability'; $done += $n; $left += (@($capsBack).Count - $n) $n = Restore-Dism '/Enable-Feature' '/FeatureName' @($featsBack) 'feature'; $done += $n; $left += (@($featsBack).Count - $n) if ($removedApps.Count) { Write-Host "" Write-Host "These Store apps were removed. Reinstall any you want from the Microsoft Store (search the name):" -ForegroundColor Yellow $removedApps | Sort-Object -Unique | ForEach-Object { Write-Host (" " + $_) } } Write-Host "" Write-Host ("Done: {0} put back{1}{2}. Restart to finish." -f $done, $(if ($failed) { ", $failed could not be" } else { '' }), $(if ($left) { ", $left left for Settings > Apps > Optional features" } else { '' })) -ForegroundColor Green '@ # --------------------------------------------------------------------------- # the machine # --------------------------------------------------------------------------- <# An external program with a time limit: its output, or $null when it did not finish in time or did not start. #> function Invoke-External([string]$exe, [string[]]$argv, [int]$ms) { try { $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = $exe; $psi.Arguments = (($argv | ForEach-Object { if ($_ -match '\s') { '"' + $_ + '"' } else { $_ } }) -join ' ') $psi.UseShellExecute = $false; $psi.RedirectStandardOutput = $true; $psi.RedirectStandardError = $true; $psi.CreateNoWindow = $true $p = [System.Diagnostics.Process]::Start($psi) $reading = $p.StandardOutput.ReadToEndAsync(); $errors = $p.StandardError.ReadToEndAsync() if (-not $p.WaitForExit($ms)) { try { $p.Kill() } catch { }; return $null } [void]$errors.Wait(1000) return $reading.Result } catch { return $null } } <# One line of the NVIDIA tool's CSV, as numbers and the throttle reasons that are active. Every driver ships the tool; laptops and older cards answer [N/A] for what they cannot report, and that stays $null. #> function Read-GpuHealth([string]$csv) { if (-not $csv) { return $null } $line = @($csv -split "`r?`n" | Where-Object { $_.Trim() }) | Select-Object -First 1 if (-not $line) { return $null } $f = @($line -split ',\s*' | ForEach-Object { $_.Trim() }) if ($f.Count -lt 13) { return $null } $num = { param($v) if ($v -match '^-?[0-9]+(\.[0-9]+)?$') { return [double]$v }; return $null } $throttle = @() if ($f[8] -eq 'Active' -or $f[9] -eq 'Active') { $throttle += 'thermal' } if ($f[10] -eq 'Active') { $throttle += 'power cap' } if ($f[11] -eq 'Active') { $throttle += 'power brake' } if ($f[12] -eq 'Active') { $throttle += 'hardware slowdown' } return @{ tempC = (& $num $f[0]); powerW = (& $num $f[1]); limitW = (& $num $f[2]); maxW = (& $num $f[3]); clockMhz = (& $num $f[4]); clockMaxMhz = (& $num $f[5]); fanPct = (& $num $f[6]); pstate = $f[7]; throttle = $throttle } } <# Resizable BAR from nvidia-smi -q -d MEMORY: the "Total" under "FB Memory Usage" (the card's memory) and under "BAR1 Memory Usage" (the window the CPU sees it through). 256 MiB is the old fixed window, Resizable BAR off; a window the size of the card's memory is Resizable BAR on. 'on', 'off', or $null when the tool did not say. #> function Read-Rebar([string]$text) { if (-not $text) { return $null } $fb = $null; $bar = $null; $in = '' foreach ($l in ($text -split "`r?`n")) { if ($l -match '^\s*FB Memory Usage') { $in = 'fb'; continue } if ($l -match '^\s*BAR1 Memory Usage') { $in = 'bar'; continue } if ($in -and $l -match '^\s*Total\s*:\s*(\d+)\s*MiB') { if ($in -eq 'fb' -and $null -eq $fb) { $fb = [int]$Matches[1] } elseif ($in -eq 'bar' -and $null -eq $bar) { $bar = [int]$Matches[1] }; $in = ''; continue } if ($l -match '^\s*\S.*Usage\s*$') { $in = '' } } if (-not $bar -or -not $fb) { return $null } if ($bar -le 256 -and $fb -ge 2048) { return 'off' } if ($bar -ge [int]($fb * 0.9)) { return 'on' } return $null } <# The last start, in Windows' own figures. The motherboard's part before Windows began (FwPOSTTime, Task Manager's "Last BIOS time"); Windows' part to the desktop (event 100 of the Diagnostics-Performance log, the figure the after-restart line already uses: BootTime in all, MainPathBootTime to the desktop, BootPostBootTime for what kept starting after it); and what Windows itself logged as slowing a start in the last 30 days, the slowest five (events 101 an app, 102 a driver, 103 a service, 109 a device; each event's own delay, or its total time). Reads only; Windows Server and a PC with the log switched off have none of it. #> function Read-Boot { $r = @{ fwMs = $null; bootMs = $null; mainMs = $null; postMs = $null; slow = @() } try { $fw = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power' -Name FwPOSTTime -ErrorAction Stop).FwPOSTTime; if ([int64]$fw -gt 0 -and [int64]$fw -lt 600000) { $r.fwMs = [int]$fw } } catch { } $data = { param($e) $d = @{}; foreach ($x in @(([xml]$e.ToXml()).Event.EventData.Data)) { if ($x.Name) { $d[$x.Name] = "$($x.'#text')" } }; return $d } $log = 'Microsoft-Windows-Diagnostics-Performance/Operational' try { $e = Get-WinEvent -FilterHashtable @{ LogName = $log; Id = 100 } -MaxEvents 1 -ErrorAction Stop $d = & $data $e foreach ($k in @(@('BootTime', 'bootMs'), @('MainPathBootTime', 'mainMs'), @('BootPostBootTime', 'postMs'))) { $v = 0; if ([int]::TryParse("$($d[$k[0]])", [ref]$v) -and $v -gt 0 -and $v -lt 3600000) { $r[$k[1]] = $v } } } catch { } try { $kinds = @{ 101 = 'app'; 102 = 'driver'; 103 = 'service'; 109 = 'device' } $best = @{} foreach ($e in @(Get-WinEvent -FilterHashtable @{ LogName = $log; Id = 101, 102, 103, 109; StartTime = (Get-Date).AddDays(-30) } -MaxEvents 300 -ErrorAction Stop)) { $d = & $data $e $name = "$($d['FriendlyName'])".Trim(); if (-not $name) { $name = "$($d['Name'])".Trim() } if (-not $name) { continue } if ($name -match '[\\/]') { $name = Split-Path $name -Leaf } $ms = 0; if (-not ([int]::TryParse("$($d['DegradationTime'])", [ref]$ms) -and $ms -gt 0)) { [void][int]::TryParse("$($d['TotalTime'])", [ref]$ms) } if ($ms -le 0) { continue } $k = "$($kinds[[int]$e.Id])|$name" if (-not $best.ContainsKey($k) -or $best[$k].ms -lt $ms) { $best[$k] = @{ kind = $kinds[[int]$e.Id]; name = $name; ms = $ms; when = $e.TimeCreated } } } $r.slow = @($best.Values | Sort-Object { $_.ms } -Descending | Select-Object -First 5) } catch { } return $r } # One line for the report: the last start, both parts, in seconds. function Get-BootLine($b) { if (-not $b -or (-not $b.fwMs -and -not $b.mainMs -and -not $b.bootMs)) { return $null } $parts = @() if ($b.fwMs) { $parts += ('motherboard {0:0.0} s before Windows' -f ($b.fwMs / 1000)) } if ($b.mainMs) { $parts += ('Windows {0:0.0} s to the desktop' -f ($b.mainMs / 1000)) } elseif ($b.bootMs) { $parts += ('Windows {0:0.0} s' -f ($b.bootMs / 1000)) } return ('Last start: ' + ($parts -join ', ') + $(if ($b.postMs) { ('; {0:0.0} s more of startup after it' -f ($b.postMs / 1000)) } else { '' })) } <# The link to the router and to the internet's edge as numbers: the median round trip, the jitter (mean deviation) and the loss over five pings each. On a network that drops pings (some routers, firewalls and providers do), the time to open a TCP connection is taken instead, the handshake being one round trip: the router's DNS port, else its web page; the edge's HTTPS port. A host that answers neither reads as no reply, not as a fault. #> function Read-Network { $r = @{ gatewayMs = $null; gatewayJitter = $null; gatewayLoss = $null; edgeMs = $null; edgeJitter = $null; edgeLoss = $null; edge = $null; gateway = $null; gatewayBy = 'ping'; edgeBy = 'ping' } $gw = $null try { $gw = @(Get-CimInstance Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = TRUE' -ErrorAction Stop | ForEach-Object { $_.DefaultIPGateway } | Where-Object { $_ -and $_ -match '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' } | Select-Object -First 1)[0] } catch { } $probe = { param($target, [int[]]$ports) $ping = New-Object System.Net.NetworkInformation.Ping; $rtts = @(); $lost = 0; $by = 'ping' for ($i = 0; $i -lt 5; $i++) { try { $rep = $ping.Send($target, 300); if ($rep.Status -eq 'Success') { $rtts += [double]$rep.RoundtripTime } else { $lost++ } } catch { $lost++ } if ($i -eq 1 -and -not $rtts.Count) { break } Start-Sleep -Milliseconds 40 } $ping.Dispose() # Only a completed connection counts (Windows retries a refused one, which would time the retries). foreach ($port in $ports) { if ($rtts.Count) { break } $lost = 0; $by = "TCP $port" for ($i = 0; $i -lt 5; $i++) { $c = New-Object System.Net.Sockets.TcpClient try { $sw = [Diagnostics.Stopwatch]::StartNew(); $ar = $c.BeginConnect($target, $port, $null, $null) if ($ar.AsyncWaitHandle.WaitOne(1000)) { $ms = $sw.Elapsed.TotalMilliseconds; $c.EndConnect($ar); $rtts += [math]::Round($ms, 1) } else { $lost++ } } catch { $lost++ } finally { $c.Close() } if ($i -eq 1 -and -not $rtts.Count) { break } Start-Sleep -Milliseconds 40 } } if (-not $rtts.Count) { return @{ ms = $null; jitter = $null; loss = 100; by = 'ping' } } $sorted = @($rtts | Sort-Object); $med = $sorted[[math]::Floor(($sorted.Count - 1) / 2)] $mean = ($rtts | Measure-Object -Average).Average $jit = [math]::Round((($rtts | ForEach-Object { [math]::Abs($_ - $mean) }) | Measure-Object -Average).Average, 1) return @{ ms = [math]::Round($med, 1); jitter = $jit; loss = [int](100 * $lost / ($rtts.Count + $lost)); by = $by } } if ($gw) { $r.gateway = $gw; $g = & $probe $gw @(53, 80); $r.gatewayMs = $g.ms; $r.gatewayJitter = $g.jitter; $r.gatewayLoss = $g.loss; $r.gatewayBy = $g.by } foreach ($edge in '1.1.1.1', '8.8.8.8') { $e = & $probe $edge @(443); if ($null -ne $e.ms) { $r.edgeMs = $e.ms; $r.edgeJitter = $e.jitter; $r.edgeLoss = $e.loss; $r.edge = $edge; $r.edgeBy = $e.by; break } } return $r } <# Every screen on the desktop and the refresh rates its monitor offers, from Windows' own display settings (1.105.0). List: each screen's resolution and rate now, and the highest rate at that resolution and colour depth (interlaced modes left out). Raise: each screen that offers more switched to its highest rate without saving it, Windows' own way to try a mode; Keep saves it; Revert goes back to the saved one. The app asks between the two, the way Windows' display settings do, so a mode the cable cannot carry comes back by itself after 15 seconds. #> $script:DisplaySource = @' using System; using System.Collections.Generic; using System.Runtime.InteropServices; public static class OmniDisplay { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct DEVMODE { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; public short dmSpecVersion, dmDriverVersion, dmSize, dmDriverExtra; public int dmFields, dmPositionX, dmPositionY, dmDisplayOrientation, dmDisplayFixedOutput; public short dmColor, dmDuplex, dmYResolution, dmTTOption, dmCollate; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmFormName; public short dmLogPixels; public int dmBitsPerPel, dmPelsWidth, dmPelsHeight, dmDisplayFlags, dmDisplayFrequency, dmICMMethod, dmICMIntent, dmMediaType, dmDitherType, dmReserved1, dmReserved2, dmPanningWidth, dmPanningHeight; } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct DISPLAY_DEVICE { public int cb; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string DeviceName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceString; public int StateFlags; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceID; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string DeviceKey; } [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern bool EnumDisplaySettings(string dev, int mode, ref DEVMODE dm); [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern bool EnumDisplayDevices(string dev, int i, ref DISPLAY_DEVICE dd, int flags); [DllImport("user32.dll", CharSet = CharSet.Unicode)] static extern int ChangeDisplaySettingsEx(string dev, ref DEVMODE dm, IntPtr hwnd, int flags, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "ChangeDisplaySettingsExW")] static extern int ChangeDisplaySettingsBack(string dev, IntPtr dm, IntPtr hwnd, int flags, IntPtr lParam); const int FieldsMode = 0x40000 | 0x80000 | 0x100000 | 0x400000; public class Screen { public string Device = "", Monitor = ""; public int Width, Height, Bits, Hz, MaxHz; public bool Primary; public bool CanRise { get { return Hz >= 24 && MaxHz > Hz + 1; } } } static DEVMODE New() { DEVMODE dm = new DEVMODE(); dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE)); return dm; } public static List List() { List l = new List(); for (int i = 0; i < 16; i++) { DISPLAY_DEVICE d = new DISPLAY_DEVICE(); d.cb = Marshal.SizeOf(typeof(DISPLAY_DEVICE)); if (!EnumDisplayDevices(null, i, ref d, 0)) break; if ((d.StateFlags & 1) == 0 || (d.StateFlags & 8) != 0) continue; DEVMODE cur = New(); if (!EnumDisplaySettings(d.DeviceName, -1, ref cur)) continue; Screen s = new Screen(); s.Device = d.DeviceName; s.Width = cur.dmPelsWidth; s.Height = cur.dmPelsHeight; s.Bits = cur.dmBitsPerPel; s.Hz = cur.dmDisplayFrequency; s.MaxHz = cur.dmDisplayFrequency; s.Primary = (d.StateFlags & 4) != 0; for (int m = 0; m < 4000; m++) { DEVMODE dm = New(); if (!EnumDisplaySettings(d.DeviceName, m, ref dm)) break; if (dm.dmPelsWidth == s.Width && dm.dmPelsHeight == s.Height && dm.dmBitsPerPel == s.Bits && (dm.dmDisplayFlags & 2) == 0 && dm.dmDisplayFrequency > s.MaxHz) s.MaxHz = dm.dmDisplayFrequency; } DISPLAY_DEVICE mon = new DISPLAY_DEVICE(); mon.cb = Marshal.SizeOf(typeof(DISPLAY_DEVICE)); if (EnumDisplayDevices(d.DeviceName, 0, ref mon, 0)) s.Monitor = (mon.DeviceString ?? "").Trim(); if (s.Primary) l.Insert(0, s); else l.Add(s); } return l; } static bool Mode(Screen s, out DEVMODE dm) { dm = New(); if (!EnumDisplaySettings(s.Device, -1, ref dm)) return false; dm.dmDisplayFrequency = s.MaxHz; dm.dmFields = FieldsMode; return true; } public static int Test(Screen s) { DEVMODE dm; return Mode(s, out dm) ? ChangeDisplaySettingsEx(s.Device, ref dm, IntPtr.Zero, 2, IntPtr.Zero) : -1; } public static List Raise(List screens) { List done = new List(); foreach (Screen s in screens) { if (!s.CanRise) continue; DEVMODE dm; if (!Mode(s, out dm)) continue; if (ChangeDisplaySettingsEx(s.Device, ref dm, IntPtr.Zero, 2, IntPtr.Zero) != 0) continue; if (ChangeDisplaySettingsEx(s.Device, ref dm, IntPtr.Zero, 0, IntPtr.Zero) == 0) done.Add(s); } return done; } public static void Keep(List screens) { foreach (Screen s in screens) { DEVMODE dm; if (Mode(s, out dm)) ChangeDisplaySettingsEx(s.Device, ref dm, IntPtr.Zero, 1, IntPtr.Zero); } } public static void Revert(List screens) { foreach (Screen s in screens) ChangeDisplaySettingsBack(s.Device, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero); } } '@ function Get-Machine { # How long each slow read takes, kept in the machine record and the probe, so a # slow PC (or a slow build machine) says where the seconds went. $sw = [System.Diagnostics.Stopwatch]::StartNew(); $tm = [ordered]@{} $lap = { param($name) $tm[$name] = [int]$sw.ElapsedMilliseconds; $sw.Restart() } $os = Get-CimInstance Win32_OperatingSystem $cs = Get-CimInstance Win32_ComputerSystem $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 $bb = Get-CimInstance Win32_BaseBoard $bios = Get-CimInstance Win32_BIOS $gpus = @(Get-CimInstance Win32_VideoController | Where-Object { $_.Name -and $_.Name -notmatch 'Basic Display|Remote|Virtual' }) $gpu = $gpus | Sort-Object { if ($_.Name -match 'NVIDIA|Radeon RX|Radeon Pro|Arc') { 0 } else { 1 } } | Select-Object -First 1 $vendor = if ($gpu.Name -match 'NVIDIA|GeForce|RTX|GTX') { 'NVIDIA' } elseif ($gpu.Name -match 'AMD|Radeon') { 'AMD' } elseif ($gpu.Name -match 'Intel|Arc') { 'Intel' } else { 'Unknown' } $battery = Get-CimInstance Win32_Battery -ErrorAction SilentlyContinue $laptop = ($battery -ne $null) -or ($cs.PCSystemType -eq 2) -or ($cs.PCSystemTypeEx -eq 2) # The disks straight from the storage class (Get-PhysicalDisk loads the whole Storage module for three fields). # MediaType 3 is a hard disk, 4 an SSD; BusType 17 is NVMe. The cmdlet is the fallback when the class is missing. $disks = @() try { $disks = @(Get-CimInstance -Namespace root/Microsoft/Windows/Storage -ClassName MSFT_PhysicalDisk -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ DeviceId = $_.DeviceId; MediaType = $(if ($_.MediaType -eq 3) { 'HDD' } elseif ($_.MediaType -eq 4) { 'SSD' } else { 'Unspecified' }); BusType = $(if ($_.BusType -eq 17) { 'NVMe' } else { "$($_.BusType)" }) } }) } catch { $disks = @(Get-PhysicalDisk -ErrorAction SilentlyContinue) } $allSsd = $disks.Count -gt 0 -and -not ($disks | Where-Object { $_.MediaType -eq 'HDD' }) $nvme = ($disks | Where-Object { $_.BusType -eq 'NVMe' }).Count -gt 0 $ramGb = [math]::Round($cs.TotalPhysicalMemory / 1GB) $build = [int]$os.BuildNumber $win = if ($build -ge 22000) { 11 } else { 10 } # A virtual display reports 1 Hz; anything under 24 is not a refresh rate a person set, so it reads as unknown. $refresh = ($gpus | ForEach-Object { $_.CurrentRefreshRate } | Where-Object { $_ -ge 24 } | Measure-Object -Maximum).Maximum & $lap 'cim' # The printer list from its class (Get-Printer loads a module for one field: 2.3 s cold on 1.63.0's read line); the cmdlet is the fallback. $printers = @() try { $printers = @(Get-CimInstance -Namespace root/StandardCimv2 -ClassName MSFT_Printer -ErrorAction Stop | Where-Object { $_.Name -notmatch 'Microsoft|OneNote|Fax|XPS|PDF' }) } catch { $printers = @(Get-Printer -ErrorAction SilentlyContinue | Where-Object { $_.Name -notmatch 'Microsoft|OneNote|Fax|XPS|PDF' }) } & $lap 'printers' # One device listing for the five classes that matter, filtered in memory: 1.58.0's read line showed the # five class-by-class queries costing 2.2 s on the build machine, each one walking the whole bus. # The same class the cmdlet wraps, asked directly (no module load); the cmdlet is the fallback. $devs = @() try { $devs = @(Get-CimInstance -ClassName Win32_PnPEntity -Filter "Status = 'OK' AND (PNPClass = 'HIDClass' OR PNPClass = 'XboxComposite' OR PNPClass = 'XnaComposite' OR PNPClass = 'Bluetooth' OR PNPClass = 'Biometric')" -ErrorAction Stop | ForEach-Object { [pscustomobject]@{ Class = $_.PNPClass; FriendlyName = $_.Name; Ids = (@($_.HardwareID) + @($_.CompatibleID)) -join ' ' } }) } catch { $devs = @(Get-PnpDevice -Class HIDClass, XboxComposite, XnaComposite, Bluetooth, Biometric -Status OK -ErrorAction SilentlyContinue | ForEach-Object { [pscustomobject]@{ Class = $_.Class; FriendlyName = $_.FriendlyName; Ids = (@($_.HardwareID) + @($_.CompatibleID)) -join ' ' } }) } $btAll = @($devs | Where-Object { $_.Class -eq 'Bluetooth' }) $bt = @($btAll | Where-Object { $_.FriendlyName -notmatch 'Adapter|Enumerator|Radio|Microsoft|Generic|RFCOMM|LE Generic|Service' }) $btRadio = $btAll.Count -gt 0 # A touch screen or pen by what it is to HID (digitizer page 0x0D: usage 4 a touch screen, 2 a pen), not by its # name, which comes translated ("Pantalla tactil compatible con HID"): a Spanish touch laptop read as having none, # and Extreme would have cut its touch keyboard and pen service. The English name stays as a second way in. $touch = @($devs | Where-Object { $_.Class -eq 'HIDClass' -and ($_.Ids -match 'HID_DEVICE_UP:000D_U:000[24]' -or $_.FriendlyName -match 'touch screen|touchscreen') }).Count -gt 0 $bio = @($devs | Where-Object { $_.Class -eq 'Biometric' }).Count -gt 0 # Only the device classes a controller can be in, not every device on the bus. $xboxPad = @($devs | Where-Object { $_.FriendlyName -match 'Xbox' }).Count -gt 0 # The adapters once each: the physical ones (Wi-Fi present, which one is live) and all of them (a VPN adapter). $physical = @(Get-NetAdapter -Physical -ErrorAction SilentlyContinue) $wifi = @($physical | Where-Object { $_.PhysicalMediaType -match '802.11|Native' -or $_.Name -match 'Wi-?Fi|Wireless' }).Count -gt 0 $vpn = @(Get-NetAdapter -ErrorAction SilentlyContinue | Where-Object { $_.InterfaceDescription -match 'TAP|Wintun|WireGuard|VPN|NordLynx|Proton|Mullvad|Cloudflare WARP' }).Count -gt 0 # Game Pass, the Xbox app or Minecraft: the package repository in the registry names every staged Store package by its # full name, and reading it costs nothing where loading the Appx module for two lookups was part of the 1.9 s device # lap on 1.61.0's read line. The cmdlets are the fallback when the key is missing. $xboxUsed = Test-Path (Join-Path $script:AppData '.minecraft') if (-not $xboxUsed) { $repo = 'HKLM:\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages' if (Test-Path $repo) { $xboxUsed = @(Get-ChildItem -Path $repo -Name -ErrorAction SilentlyContinue | Where-Object { $_ -match '^Microsoft\.(GamingApp|MinecraftUWP)_' }).Count -gt 0 } else { $xboxUsed = ((Get-AppxPackage -Name Microsoft.GamingApp -ErrorAction SilentlyContinue) -ne $null) -or ((Get-AppxPackage -Name Microsoft.MinecraftUWP -ErrorAction SilentlyContinue) -ne $null) } } & $lap 'pnp' $live = @($physical | Where-Object { $_.Status -eq 'Up' }) $wifiLive = @($live | Where-Object { $_.PhysicalMediaType -match '802.11|Native' -or $_.Name -match 'Wi-?Fi|Wireless' }).Count -gt 0 $vm = ($cs.Model -match 'Virtual|VMware|VirtualBox|KVM|QEMU|HVM') -or ($cs.Manufacturer -match 'QEMU|Xen|innotek|VMware') $domain = [bool]$cs.PartOfDomain $driverVer = $gpu.DriverVersion $driverDate = $null; try { if ($gpu.DriverDate) { $driverDate = [datetime]$gpu.DriverDate } } catch { } $mem = @(Get-CimInstance Win32_PhysicalMemory -ErrorAction SilentlyContinue) $sticks = $mem.Count $ramRated = ($mem | ForEach-Object { $_.Speed } | Where-Object { $_ } | Measure-Object -Maximum).Maximum $ramNow = ($mem | ForEach-Object { $_.ConfiguredClockSpeed } | Where-Object { $_ } | Measure-Object -Maximum).Maximum $ramSlow = [bool]($ramRated -and $ramNow -and ($ramNow -lt ($ramRated - 100))) # DDR5 (SMBIOS memory type 34): with an AMD desktop that is an AM5 board, whose memory training at every start is # the slow part of its boot until Memory Context Restore is on (the BIOS checklist, item 8). $ddr5 = @($mem | Where-Object { [int]$_.SMBIOSMemoryType -eq 34 }).Count -gt 0 $pf = @(Get-CimInstance Win32_PageFileUsage -ErrorAction SilentlyContinue) $noPageFile = ($pf.Count -eq 0) -and (-not $cs.AutomaticManagedPagefile) $onBattery = $false; if ($battery) { $onBattery = (($battery | Select-Object -First 1).BatteryStatus -eq 1) } # Which disk Windows lives on, from the partition class (Get-Partition and Get-Disk load the Storage module, # which 1.60.0's read line showed moving 1.6 s from one lap to another rather than away); the cmdlets are the fallback. $sysHdd = $false # And every drive letter on a hard disk (1.113.0), read from the same partition list: a game installed there loads # and streams its world slowly whatever is tuned, and the game profiles name it. Nothing to read on an all-SSD PC. $hddLetters = @() try { $letter = $env:SystemDrive.Substring(0, 1) $dn = $null; $parts = @() try { $parts = @(Get-CimInstance -Namespace root/Microsoft/Windows/Storage -ClassName MSFT_Partition -ErrorAction Stop) $dn = ($parts | Where-Object { "$($_.DriveLetter)" -eq $letter } | Select-Object -First 1).DiskNumber } catch { $dn = (Get-Partition -DriveLetter $letter -ErrorAction Stop | Get-Disk -ErrorAction Stop).Number } if ($null -ne $dn) { $pd = $disks | Where-Object { [int]$_.DeviceId -eq [int]$dn } | Select-Object -First 1 if ($pd -and $pd.MediaType -eq 'HDD') { $sysHdd = $true } } if (-not $allSsd -and $parts.Count) { $hddIds = @($disks | Where-Object { $_.MediaType -eq 'HDD' } | ForEach-Object { [int]$_.DeviceId }) $hddLetters = @($parts | Where-Object { "$($_.DriveLetter)" -match '^[A-Za-z]$' -and $hddIds -contains [int]$_.DiskNumber } | ForEach-Object { "$($_.DriveLetter)".ToUpper() } | Select-Object -Unique) } } catch { } & $lap 'memory' # Every screen on the desktop, its refresh rate and the highest its monitor offers at the same resolution (1.105.0), # from Windows' own display settings (OmniDisplay, below). Until 1.104.0 the highest rate came from the adapter's # whole mode list, which can name a rate the monitor offers only at another resolution. That list is the fallback. $maxRefresh = 0; $screens = @() try { if (-not ('OmniDisplay' -as [type])) { Add-Type -TypeDefinition $script:DisplaySource -ErrorAction Stop } $screens = @([OmniDisplay]::List()) $main = $screens | Select-Object -First 1 if ($main) { if ($main.Hz -ge 24) { $refresh = $main.Hz }; $maxRefresh = $main.MaxHz } } catch { } if (-not $screens.Count) { # Every display mode the driver knows, which some drivers take a long time to list: ten seconds, then it is left out. try { $ps = [PowerShell]::Create() [void]$ps.AddScript('(Get-CimInstance CIM_VideoControllerResolution -ErrorAction Stop | ForEach-Object { $_.RefreshRate } | Where-Object { $_ } | Measure-Object -Maximum).Maximum') $h = $ps.BeginInvoke() if ($h.AsyncWaitHandle.WaitOne(10000)) { $r = $ps.EndInvoke($h); if ($r -and $r.Count) { $maxRefresh = [int]$r[$r.Count - 1] } } else { try { $ps.Stop() } catch { } } $ps.Dispose() } catch { } } & $lap 'modes' $otherAv = @(); try { $otherAv = @(Get-CimInstance -Namespace root\SecurityCenter2 -ClassName AntiVirusProduct -ErrorAction Stop | ForEach-Object { $_.displayName } | Where-Object { $_ -and $_ -notmatch 'Defender' } | Sort-Object -Unique) } catch { } $installed = @() foreach ($uk in 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*', 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') { $installed += @(Get-ItemProperty $uk -ErrorAction SilentlyContinue | ForEach-Object { $_.DisplayName } | Where-Object { $_ }) } $optimizers = @($installed | Where-Object { $_ -match 'Advanced SystemCare|Razer Cortex|CCleaner|Driver Booster|Wise Care|Glary|PC Optimizer|Smart Game Booster|Outbyte|Restoro|Reimage|Booster' } | Sort-Object -Unique) $oem = @($installed | Where-Object { $_ -match 'SupportAssist|HP Support Assistant|HP Analytics|HP Wolf|Lenovo Vantage|Lenovo System Update|Armoury Crate|MyASUS|Dragon Center|MSI Center|Acer Care|Predator Sense|Alienware Command|Omen Gaming Hub|Aura Sync|LiveDash|McAfee|Norton' } | Sort-Object -Unique) $uefi = $env:firmware_type -eq 'UEFI' # Secure Boot from the flag Windows records at boot (the cmdlet loads a module to read the same state); the cmdlet is the fallback. $secureBoot = $false try { $sbv = (Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\SecureBoot\State' -ErrorAction Stop).GetValue('UEFISecureBootEnabled'); if ($null -eq $sbv) { throw 'no flag' }; $secureBoot = ([int]$sbv -eq 1) } catch { try { $secureBoot = Confirm-SecureBootUEFI -ErrorAction Stop } catch { } } # The TPM from its own class (Get-Tpm loads a module to read one field); the cmdlet is the fallback when the class is missing. $tpm = $false try { $tpm = @(Get-CimInstance -Namespace root/CIMV2/Security/MicrosoftTpm -ClassName Win32_Tpm -ErrorAction Stop).Count -gt 0 } catch { try { $tpm = (Get-Tpm -ErrorAction Stop).TpmPresent } catch { } } & $lap 'software' # A desktop with a graphics card whose monitor is plugged into the motherboard: the integrated chip reports a # display mode and the card reports none. Laptops route the screen through the integrated chip by design. $displayOnIgpu = $false; $igpuName = ''; $discrete = @(); $integrated = @() try { $discrete = @($gpus | Where-Object { $_.Name -match 'NVIDIA|GeForce|RTX|GTX|Radeon RX|Radeon Pro|Arc' }) $integrated = @($gpus | Where-Object { $_.Name -match 'Intel\(R\) (UHD|HD|Iris)|AMD Radeon\(TM\) Graphics|Radeon Graphics$|Radeon\(TM\) Vega' -and $_.Name -notmatch 'RX' }) if (-not $laptop -and $discrete.Count -and $integrated.Count) { $cardDrives = @($discrete | Where-Object { $_.CurrentHorizontalResolution -gt 0 }).Count -gt 0 $chipDrives = @($integrated | Where-Object { $_.CurrentHorizontalResolution -gt 0 }).Count -gt 0 if ($chipDrives -and -not $cardDrives) { $displayOnIgpu = $true; $igpuName = ($integrated | Select-Object -First 1).Name } } } catch { } # The graphics card's PCI Express link (1.110.0), from Windows' own device properties for any maker's card: lanes in # use against lanes it has. A card in a lower slot, on a riser, or sharing lanes with an M.2 drive runs on fewer. $gpuLink = $null try { foreach ($d in @(Get-PnpDevice -Class Display -PresentOnly -ErrorAction Stop | Where-Object { $_.InstanceId -like 'PCI\*' -and $_.FriendlyName -match 'NVIDIA|GeForce|RTX|GTX|Radeon RX|Radeon Pro|Arc' })) { $lp = @{} foreach ($kn in 'DEVPKEY_PciDevice_CurrentLinkWidth', 'DEVPKEY_PciDevice_MaxLinkWidth', 'DEVPKEY_PciDevice_MaxLinkSpeed') { try { $lp[$kn] = [int](Get-PnpDeviceProperty -InstanceId $d.InstanceId -KeyName $kn -ErrorAction Stop).Data } catch { } } if ($lp['DEVPKEY_PciDevice_CurrentLinkWidth'] -gt 0 -and $lp['DEVPKEY_PciDevice_MaxLinkWidth'] -gt 0) { $gpuLink = @{ name = $d.FriendlyName; width = $lp['DEVPKEY_PciDevice_CurrentLinkWidth']; maxWidth = $lp['DEVPKEY_PciDevice_MaxLinkWidth']; maxGen = [int]$lp['DEVPKEY_PciDevice_MaxLinkSpeed'] } break } } } catch { } # A laptop with a graphics card next to the integrated chip: its screen goes through the integrated chip (NVIDIA # Optimus, AMD's switchable graphics) unless the laptop's own MUX switch hands it to the card. $muxHint = [bool]($laptop -and $discrete.Count -gt 0 -and $integrated.Count -gt 0) # Room on the system drive: games stutter and shader caches fail when it is nearly full. $sysFreeGb = -1; $sysPct = -1 try { $sd = Get-PSDrive -Name $env:SystemDrive.Substring(0, 1) -ErrorAction Stop; if ($sd.Used -ne $null) { $sysFreeGb = [math]::Round($sd.Free / 1GB); $sysPct = [math]::Round(100 * $sd.Free / ($sd.Free + $sd.Used)) } } catch { } $vbsOn = $false; $dg = $null; try { $dg = Get-CimInstance -Namespace root\Microsoft\Windows\DeviceGuard -ClassName Win32_DeviceGuard -ErrorAction Stop; $vbsOn = ($dg.VirtualizationBasedSecurityStatus -eq 2) -or ($dg.SecurityServicesRunning -contains 2) } catch { } # IOMMU (VT-d / AMD-Vi): Windows lists "DMA protection" among the security properties available only when the IOMMU is on. # VALORANT's Vanguard and FACEIT require it, with memory integrity, since 2026; the tune reports it and never turns it off. $iommu = $false; $hvci = $false try { if ($dg) { $iommu = @($dg.AvailableSecurityProperties) -contains 3; $hvci = @($dg.SecurityServicesRunning) -contains 2 } } catch { } $riot = Test-Path (Join-Path $env:ProgramData 'Riot Games') $faceit = (Test-Path (Join-Path $env:ProgramFiles 'FACEIT AC')) -or ($null -ne (Get-Service -Name 'FACEIT' -ErrorAction SilentlyContinue)) # A dual-CCD X3D (7900X3D, 7950X3D, 9900X3D, 9950X3D): AMD's driver steers games onto the cache half, and spots them through Game Mode and Game Bar. $x3dDual = [bool]($cpu.Name -match '(7900|7950|9900|9950)X3D') # Intel's performance-and-efficiency-core CPUs (12th to 14th generation with E-cores, every Core Ultra): Windows 11 # reads Intel's Thread Director and keeps a game on the performance cores; Windows 10 does not. The E-cores have no # second thread, so such a CPU has fewer than two threads per core (an i5-12400, all P-cores, has two and is not one). $hybrid = [bool](($cpu.Manufacturer -notmatch 'AMD') -and ($cpu.Name -match '(?i)\bi[3579]-1[234]\d{2,3}|Core\(TM\) Ultra|Core Ultra|Core\(TM\) [3579] \d{3}[A-Z]') -and ([int]$cpu.NumberOfLogicalProcessors -lt 2 * [int]$cpu.NumberOfCores)) & $lap 'security' # GPU health from the driver's own tool: temperature, the power limit against the card's maximum, the throttle # reasons, as a labelled measurement. NVIDIA ships the tool with every driver; AMD and Intel ship no equivalent. $gpuHealth = $null; $rebar = $null if ($vendor -eq 'NVIDIA') { $smi = @("$env:SystemRoot\System32\nvidia-smi.exe", "$env:ProgramFiles\NVIDIA Corporation\NVSMI\nvidia-smi.exe") | Where-Object { Test-Path $_ } | Select-Object -First 1 if ($smi) { $q = 'temperature.gpu,power.draw,power.limit,power.max_limit,clocks.sm,clocks.max.sm,fan.speed,pstate,clocks_throttle_reasons.hw_thermal_slowdown,clocks_throttle_reasons.sw_thermal_slowdown,clocks_throttle_reasons.sw_power_cap,clocks_throttle_reasons.hw_power_brake_slowdown,clocks_throttle_reasons.hw_slowdown' $gpuHealth = Read-GpuHealth (Invoke-External $smi @("--query-gpu=$q", '--format=csv,noheader,nounits') 8000) # Resizable BAR, from the same tool: the window the CPU sees into the card's memory (BAR1) is 256 MB with it # off and the size of the card's memory with it on. nvidia-smi's labels are English in every Windows language. $rebar = Read-Rebar (Invoke-External $smi @('-q', '-d', 'MEMORY') 8000) } } & $lap 'gpu' # The ACPI thermal zones, where the board exposes them (many desktops do not, and some report a fixed figure): # the hottest one, and the passive limit that says the CPU is being held back by heat right now. $cpuTempC = $null; $passivePct = $null try { $zones = @(Get-CimInstance -Namespace root/wmi -ClassName MSAcpi_ThermalZoneTemperature -ErrorAction Stop | ForEach-Object { [math]::Round($_.CurrentTemperature / 10 - 273.15, 1) } | Where-Object { $_ -gt 5 -and $_ -lt 125 }) if ($zones.Count) { $cpuTempC = ($zones | Measure-Object -Maximum).Maximum } } catch { } if ($null -ne $cpuTempC) { try { $pl = Get-Counter -Counter '\Thermal Zone Information(*)\% Passive Limit' -MaxSamples 1 -ErrorAction Stop; $passivePct = [int](($pl.CounterSamples | Measure-Object CookedValue -Maximum).Maximum) } catch { } } & $lap 'thermal' # The last start in Windows' own figures: the motherboard's part, Windows' part, and what Windows logged as slow. $boot = Read-Boot & $lap 'boot' # The network as numbers: five pings to the router and five to the internet's edge. $net = Read-Network & $lap 'net' # Streaming software. What it needs from Windows (the camera frame server, Windows Graphics Capture) stays. $streamApps = @() if ((Test-Path (Join-Path $env:ProgramFiles 'obs-studio\bin\64bit\obs64.exe')) -or (Test-Path 'HKLM:\SOFTWARE\OBS Studio') -or ($null -ne (Get-Process obs64 -ErrorAction SilentlyContinue))) { $streamApps += 'OBS Studio' } if ((Test-Path (Join-Path $env:ProgramFiles 'Streamlabs Desktop')) -or (Test-Path (Join-Path $env:ProgramFiles 'Streamlabs OBS')) -or (Test-Path (Join-Path $script:LocalAppData 'Programs\Streamlabs Desktop'))) { $streamApps += 'Streamlabs Desktop' } if (Test-Path (Join-Path ${env:ProgramFiles(x86)} 'SplitmediaLabs')) { $streamApps += 'XSplit' } if ($Streamer -and -not $streamApps.Count) { $streamApps += 'asked for with -Streamer' } @{ os = "$($os.Caption) $($os.Version) (build $build)"; win = $win; build = $build cpu = $cpu.Name.Trim(); cpuVendor = $(if ($cpu.Manufacturer -match 'AMD') { 'AMD' } else { 'Intel' }); cores = $cpu.NumberOfCores; threads = $cpu.NumberOfLogicalProcessors gpu = $gpu.Name; gpuVendor = $vendor; ramGb = $ramGb board = ("{0} {1}" -f $bb.Manufacturer, $bb.Product).Trim(); boardVendor = $bb.Manufacturer; bios = ("{0} {1}" -f $bios.Manufacturer, $bios.SMBIOSBIOSVersion).Trim(); biosDate = $bios.ReleaseDate laptop = $laptop; allSsd = $allSsd; nvme = $nvme; refresh = $refresh printers = $printers.Count; btDevices = $bt.Count; btRadio = $btRadio; wifi = $wifi; touch = $touch; biometric = $bio; vpn = $vpn; xboxUsed = $xboxUsed uefi = $uefi; secureBoot = $secureBoot; tpm = $tpm; vbs = $vbsOn; iommu = $iommu; hvci = $hvci riot = $riot; faceit = $faceit; x3dDual = $x3dDual; hybrid = $hybrid; ddr5 = $ddr5; rebar = $rebar; boot = $boot displayOnIgpu = $displayOnIgpu; igpuName = $igpuName; gpuLink = $gpuLink; muxHint = $muxHint; sysFreeGb = $sysFreeGb; sysPct = $sysPct name = $cs.Name xboxPad = $xboxPad; wifiLive = $wifiLive; vm = $vm; domain = $domain driverVer = $driverVer; driverDate = $driverDate sticks = $sticks; ramRated = $ramRated; ramNow = $ramNow; ramSlow = $ramSlow noPageFile = $noPageFile; onBattery = $onBattery; sysHdd = $sysHdd; hddLetters = $hddLetters; maxRefresh = $maxRefresh; screens = $screens otherAv = $otherAv; optimizers = $optimizers; oem = $oem gpuHealth = $gpuHealth; cpuTempC = $cpuTempC; passivePct = $passivePct; net = $net; streamer = $streamApps timings = $tm } } function Show-Machine($m) { Say (" {0}" -f $m.os) Say (" CPU {0} ({1} cores / {2} threads)" -f $m.cpu, $m.cores, $m.threads) Say (" GPU {0}" -f $m.gpu) if ($m.driverVer) { Say (" Driver {0}{1}" -f $m.driverVer, $(if ($m.driverDate) { " ({0})" -f $m.driverDate.ToString('MMM yyyy') } else { '' })) } if ($m.gpuHealth) { $g = $m.gpuHealth Say (" GPU now {0}{1}{2}{3} (NVIDIA's own tool, at idle)" -f $(if ($null -ne $g.tempC) { "$($g.tempC) C" } else { 'temperature not reported' }), $(if ($g.limitW -and $g.maxW) { ", power limit $($g.limitW) W of $($g.maxW) W" } elseif ($g.limitW) { ", power limit $($g.limitW) W" } else { '' }), $(if ($null -ne $g.fanPct) { ", fan $($g.fanPct)%" } else { '' }), $(if ($g.throttle.Count) { ", throttling: " + ($g.throttle -join ', ') } else { ', no throttling' })) } if ($null -ne $m.cpuTempC) { Say (" Thermal zone {0} C (ACPI: a board sensor as often as the CPU){1}" -f $m.cpuTempC, $(if ($m.passivePct -gt 0) { ", passive limit $($m.passivePct)%" } else { '' })) } if ($m.net) { $n = $m.net $rt = $(if ($null -ne $n.gatewayMs) { "router $($n.gatewayMs) ms (jitter $($n.gatewayJitter), loss $($n.gatewayLoss)%$(if ($n.gatewayBy -ne 'ping') { ", by $($n.gatewayBy)" }))" } elseif ($n.gateway) { 'router answers neither pings nor TCP' } else { 'no router found' }) $ed = $(if ($null -ne $n.edgeMs) { "internet edge $($n.edgeMs) ms (jitter $($n.edgeJitter), loss $($n.edgeLoss)%, via $($n.edge)$(if ($n.edgeBy -ne 'ping') { " by $($n.edgeBy)" }))" } else { 'internet edge did not answer' }) Say (" Network: {0}; {1}. Five round trips each{2}." -f $rt, $ed, $(if ($n.gatewayBy -ne 'ping' -or $n.edgeBy -ne 'ping') { ': this network drops pings, so a TCP connection was timed where they went unanswered' } else { ', by ping' })) } Say (" RAM {0} GB{1}{2}" -f $m.ramGb, $(if ($m.sticks) { ", $($m.sticks) stick$(if ($m.sticks -ne 1) { 's' })" } else { '' }), $(if ($m.ramNow) { ", $($m.ramNow) MT/s$(if ($m.ramRated -and $m.ramRated -ne $m.ramNow) { " of $($m.ramRated) rated" })" } else { '' })) Say (" Board {0} | BIOS {1}" -f $m.board, $m.bios) Say (" {0}{1}{2}, {3}" -f $(if ($m.laptop) { 'Laptop' } else { 'Desktop' }), $(if ($m.allSsd) { ', all SSD' } else { ', has a hard disk' }), $(if ($m.nvme) { ', NVMe' } else { '' }), $(if ($m.refresh) { "$($m.refresh) Hz" } else { 'refresh unknown' })) # One add per reason, into a list: the long comma-separated array of subexpressions this replaced came back empty # under Windows PowerShell 5.1 on the build machine even with a reason set (1.75.0's -Streamer check). $keeps = New-Object System.Collections.ArrayList if ($m.printers) { [void]$keeps.Add('printer') } if ($m.btDevices) { [void]$keeps.Add("Bluetooth ($($m.btDevices) paired)") } if ($m.wifi) { [void]$keeps.Add('Wi-Fi') } if ($m.touch) { [void]$keeps.Add('touch') } if ($m.biometric) { [void]$keeps.Add('Windows Hello') } if ($m.vpn) { [void]$keeps.Add('VPN') } if ($m.xboxUsed -and -not $CutXbox) { [void]$keeps.Add('Xbox / Game Pass') } if ($m.xboxPad -and -not $CutXbox) { [void]$keeps.Add('Xbox controller') } if (@($m.streamer).Count) { [void]$keeps.Add('streaming (' + (@($m.streamer) -join ', ') + ')') } if ($m.laptop) { [void]$keeps.Add('battery, hibernate') } Say (" Keeps: {0}" -f $(if ($keeps.Count) { $keeps -join ', ' } else { 'nothing extra' })) Say (" UEFI {0}, Secure Boot {1}, TPM {2}, memory integrity {3}, IOMMU {4}{5}" -f $m.uefi, $m.secureBoot, $m.tpm, $(if ($m.vbs) { 'on' } else { 'off' }), $(if ($m.iommu) { 'on' } else { 'off' }), $(if ($m.rebar) { ", Resizable BAR $($m.rebar)" } else { '' })) $bl = Get-BootLine $m.boot if ($bl) { Say (" {0}" -f $bl) } foreach ($sl in @($m.boot.slow)) { if ($sl) { Say (" slowed a start: {0} {1}, {2:0.0} s ({3:d MMM})" -f $sl.kind, $sl.name, ($sl.ms / 1000), $sl.when) } } # Where the read spent its time, in seconds, largest first: a slow PC (or a slow build machine) says so on its own line. if ($m.timings -and $m.timings.Count) { $total = 0; foreach ($v in $m.timings.Values) { $total += [int]$v } $parts = @($m.timings.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 4 | ForEach-Object { "{0} {1:0.0}" -f $_.Key, ([int]$_.Value / 1000) }) Say (" Read in {0:0.0} s ({1})" -f ($total / 1000), ($parts -join ', ')) } } <# Drivers. The devices Windows reports a problem with (Device Manager's yellow marks), whether the graphics card has its maker's driver, and how old the drivers are that games lean on (graphics, network, audio, Bluetooth, storage), each with where its maker's newest is. Old network and audio drivers are the usual cause of the wake-up delay's worst moments, a stale graphics driver misses its maker's game fixes, and Microsoft's basic display driver means no graphics driver at all. Reads only: installing a driver is for its maker's installer. #> $script:DriverCodes = @{ 1 = 'not set up'; 3 = 'its driver may be damaged'; 10 = 'cannot start'; 12 = 'not enough free resources'; 14 = 'waiting for a restart'; 18 = 'its driver needs reinstalling'; 19 = 'its registry entry is damaged'; 24 = 'not present or not working'; 28 = 'no driver installed'; 31 = 'its driver did not load'; 32 = 'its driver is switched off'; 37 = 'its driver failed to start'; 39 = 'its driver is missing or damaged'; 43 = 'stopped after reporting a problem'; 52 = "its driver's signature cannot be verified" } function Get-DriverSource([string]$what) { if ($what -match 'NVIDIA') { return 'nvidia.com/drivers (or the NVIDIA app)' } if ($what -match 'AMD|Advanced Micro|Radeon') { return 'amd.com/support (AMD Software: Adrenalin, or the chipset driver)' } if ($what -match 'Killer') { return 'intel.com/support: Killer is Intel now (Intel Driver & Support Assistant finds it)' } if ($what -match 'Intel') { return 'intel.com/support: Intel Driver & Support Assistant finds every Intel driver on the PC' } if ($what -match 'Realtek') { return "your board's or laptop's support page (Realtek's own drivers are generic; the board maker's are matched to it)" } return "your board's or laptop's support page" } function Get-DriverCheck($m) { $r = @{ problems = @(); drivers = @(); notes = @() } $sw = [Diagnostics.Stopwatch]::StartNew() # One pass over the devices that are present, with each one's driver read from the registry key Windows keeps for # it (Enum\\Driver names Control\Class\\: maker, version, month-day-year date). The list of # every signed driver (Win32_PnPSignedDriver) this replaces took five to ten seconds on its own. $ents = @(); try { $ents = @(Get-CimInstance Win32_PnPEntity -Property Name, PNPClass, PNPDeviceID, ClassGuid, ConfigManagerErrorCode -ErrorAction Stop) } catch { } $guids = @{ '{4d36e968-e325-11ce-bfc1-08002be10318}' = 'Graphics'; '{4d36e972-e325-11ce-bfc1-08002be10318}' = 'Network'; '{4d36e96c-e325-11ce-bfc1-08002be10318}' = 'Audio' '{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}' = 'Bluetooth'; '{4d36e97b-e325-11ce-bfc1-08002be10318}' = 'Storage'; '{4d36e96a-e325-11ce-bfc1-08002be10318}' = 'Storage' } $now = Get-Date foreach ($d in $ents) { $c = [int]$d.ConfigManagerErrorCode if ($c -ne 0 -and $c -ne 22 -and $c -ne 45 -and $c -ne 47) { # 22 switched off on purpose, 45 unplugged, 47 safely removed $what = $script:DriverCodes[$c]; if (-not $what) { $what = "problem code $c" } $r.problems += ("{0} ({1}): {2}" -f $(if ($d.Name) { $d.Name } else { $d.PNPDeviceID }), $(if ($d.PNPClass) { $d.PNPClass } else { 'unknown kind' }), $what) } $cls = $guids["$($d.ClassGuid)".ToLower()]; $name = "$($d.Name)"; $id = "$($d.PNPDeviceID)" if (-not $cls -or -not $name) { continue } # The hardware itself, on a bus: not the software devices (VPN and virtual adapters, virtual sound cards, Bluetooth's # enumerators and paired headsets, Storage Spaces). if ($id -notmatch '^(PCI|USB|HDAUDIO|INTELAUDIO|SD|ACPI)\\') { continue } if ($name -match 'Virtual|Remote|Hyper-V|Parsec|Steam Streaming|Voicemeeter|VB-Audio|Oculus|IddCx|Indirect|VPN|TAP-') { continue } $provider = ''; $version = ''; $date = $null try { $drv = (Get-ItemProperty -LiteralPath ('HKLM:\SYSTEM\CurrentControlSet\Enum\' + $id) -Name Driver -ErrorAction Stop).Driver $k = Get-ItemProperty -LiteralPath ('HKLM:\SYSTEM\CurrentControlSet\Control\Class\' + $drv) -ErrorAction Stop $provider = "$($k.ProviderName)"; $version = "$($k.DriverVersion)" $dt = [datetime]::MinValue if ([datetime]::TryParseExact("$($k.DriverDate)", [string[]]@('M-d-yyyy', 'MM-dd-yyyy'), [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::None, [ref]$dt)) { $date = $dt } } catch { } $days = if ($date) { [int]($now - $date).TotalDays } else { -1 } # How a card on the PCI bus interrupts: MSI when its driver asked for it, else a (shareable) line. $irq = '' if ($id -match '^PCI\\') { $msi = $null; try { $msi = (Get-ItemProperty -LiteralPath ('HKLM:\SYSTEM\CurrentControlSet\Enum\' + $id + '\Device Parameters\Interrupt Management\MessageSignaledInterruptProperties') -Name MSISupported -ErrorAction Stop).MSISupported } catch { } $irq = if ($msi -eq 1) { 'MSI' } else { 'line-based' } } $r.drivers += [pscustomobject]@{ cls = $cls; name = $name; provider = $provider; version = $version; date = $(if ($date) { $date.ToString('yyyy-MM-dd') } else { '?' }); days = $days; irq = $irq } } $r.ms = [int]$sw.ElapsedMilliseconds $age = { param([int]$d) if ($d -ge 730) { "{0} years old" -f [math]::Floor($d / 365) } elseif ($d -ge 60) { "{0} months old" -f [math]::Floor($d / 30) } else { "{0} days old" -f $d } } $gpus = @($r.drivers | Where-Object { $_.cls -eq 'Graphics' }) $real = @($gpus | Where-Object { $_.name -notmatch 'Microsoft Basic' }) if ($gpus.Count -and -not $real.Count) { $r.notes += "No graphics driver: Windows is drawing with its Microsoft Basic Display Adapter, and games run at a fraction of their speed or not at all. Install your card's driver: nvidia.com/drivers, amd.com/support or intel.com/support." } # On a virtual machine the drivers are the host's to keep current: their age is not this PC's to fix. $hostOwned = $(if ($m.vm) { 1000000 } else { 0 }) foreach ($g in $real) { if ($g.days -ge (180 + $hostOwned) -and $g.provider -notmatch '^Microsoft') { $r.notes += ("Graphics: {0}, driver {1} from {2} ({3}). Newer drivers carry fixes for new games and their latency work (Reflex, Anti-Lag): {4}." -f $g.name, $g.version, $g.date, (& $age $g.days), (Get-DriverSource "$($g.name) $($g.provider)")) } } foreach ($n in @($r.drivers | Where-Object { $_.cls -eq 'Network' -and $_.days -ge (730 + $hostOwned) -and $_.provider -notmatch '^Microsoft' })) { $r.notes += ("Network: {0}, driver from {1} ({2}). Old network drivers are a common cause of lag spikes and of the worst wake-up delays; the newest: {3}." -f $n.name, $n.date, (& $age $n.days), (Get-DriverSource "$($n.name) $($n.provider)")) } foreach ($a in @($r.drivers | Where-Object { $_.cls -eq 'Audio' -and $_.days -ge (1095 + $hostOwned) -and $_.provider -notmatch '^Microsoft' -and $_.name -notmatch 'NVIDIA|AMD High Definition|Intel\(R\) Display' })) { $r.notes += ("Audio: {0}, driver from {1} ({2}). Audio drivers are the other usual cause of wake-up delay spikes; the newest: {3}." -f $a.name, $a.date, (& $age $a.days), (Get-DriverSource "$($a.name) $($a.provider)")) } # Interrupts: a card on line-based interrupts can share its line with other devices and waits on them; message-signalled # (MSI) is answered on its own. Current drivers switch MSI on for most cards; this names the ones left on a line. foreach ($x in @($r.drivers | Where-Object { ($_.cls -eq 'Graphics' -or $_.cls -eq 'Network') -and $_.irq -eq 'line-based' -and $_.name -notmatch 'Microsoft Basic' })) { if (-not $m.vm) { $r.notes += ("{0}: {1} answers on a line-based interrupt, which it can share with other devices and wait on; message-signalled (MSI) mode answers on its own. Its maker's newest driver switches MSI on for most cards; for an older one, MSI mode is the value MSISupported = 1 under the device's Interrupt Management > MessageSignaledInterruptProperties key (0 undoes it), then a restart." -f $x.cls, $x.name) } } # Where the sound goes: Bluetooth plays it late. $r.sound = Get-SoundOutput if ($r.sound -and $r.sound.bt) { $r.notes += ("Sound plays through Bluetooth ({0}): Bluetooth audio reaches your ears 100 to 250 ms after the game makes it, so footsteps and shots come late{1}. A wired headset, or a wireless one with its own USB dongle, has none of that delay." -f $r.sound.name, $(if ($r.sound.handsFree) { ", and in hands-free mode (its microphone in use) the sound also drops to phone quality" } else { '' })) } # Ryzen leans on AMD's chipset driver for how Windows parks and schedules its cores (dual-CCD X3D most of all). if ($m.cpuVendor -eq 'AMD' -and -not $m.vm) { $ppm = @($ents | Where-Object { "$($_.Name)" -like 'AMD PPM Provisioning*' })[0] if (-not $ppm) { $r.notes += "AMD CPU without AMD's chipset driver (its PPM provisioning part is missing): Windows then schedules and parks the cores without AMD's rules for this chip. The chipset driver for your board's chipset: amd.com/support > Chipsets." } } $wuDrivers = $true; try { if ((Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -Name ExcludeWUDriversInQualityUpdate -ErrorAction Stop).ExcludeWUDriversInQualityUpdate -eq 1) { $wuDrivers = $false } } catch { } $where = if ($wuDrivers) { "Windows Update > Advanced options > Optional updates often has the missing driver, or the device maker's support page." } else { "the device maker's support page has the driver (Windows Update is set to leave drivers to you)." } if ($r.problems.Count) { $r.notes = @("Device Manager shows a problem with " + $(if ($r.problems.Count -eq 1) { '1 device' } else { "$($r.problems.Count) devices" }) + ": " + $where) + $r.notes } return $r } function Show-Drivers($m) { $script:Drivers = Get-DriverCheck $m $d = $script:Drivers $pick = { param($cls) $x = @($d.drivers | Where-Object { $_.cls -eq $cls -and $_.name -notmatch 'Microsoft Basic' } | Sort-Object days | Select-Object -First 1)[0]; if ($x) { "{0} {1} ({2})" -f $x.name, $x.version, $x.date } } $bits = @(); foreach ($c in 'Graphics', 'Network', 'Audio') { $v = & $pick $c; if ($v) { $bits += ("{0}: {1}" -f $c.ToLower(), $v) } } if ($d.sound) { Say (" Sound: {0}{1}" -f $d.sound.name, $(if ($d.sound.bt) { ' (Bluetooth)' } else { '' })) } Say (" Drivers: {0}; {1}" -f $(if ($bits.Count) { $bits -join '; ' } else { 'none read' }), $(if ($d.problems.Count) { "{0} device(s) with a problem" -f $d.problems.Count } else { 'no device with a problem' })) foreach ($p in $d.problems) { Say (" - {0}" -f $p) 'Yellow' } foreach ($n in $d.notes) { Warn $n } } # The drivers for the text reports: one line each, the problems, the notes. function Get-DriverLines { $d = $script:Drivers; if (-not $d) { return @(' not read') } $out = @((" (read in {0} ms)" -f $d.ms)); if ($d.sound) { $out += (" Sound out {0}{1}" -f $d.sound.name, $(if ($d.sound.bt) { ' (Bluetooth: 100 to 250 ms late)' } else { '' })) } $out += @($d.drivers | Sort-Object cls, name | ForEach-Object { " {0,-9} {1} ({2} {3}, {4}{5})" -f $_.cls, $_.name, $_.provider, $_.version, $_.date, $(if ($_.irq) { ", $($_.irq) interrupts" } else { '' }) }) if ($d.problems.Count) { $out += ' With a problem:'; $out += @($d.problems | ForEach-Object { " - $_" }) } else { $out += ' No device with a problem.' } $out += @($d.notes | ForEach-Object { " ! $_" }) return $out } <# Things worth more than any tweak, said once, up front. Nothing here changes anything; it is the advice a friend who builds PCs would give after one look at yours. #> # Crashes and hardware errors from Windows' own logs over the last 30 days (1.114.0): the errors the hardware corrected by # itself (WHEA-Logger 19 the processor, 47 the memory, 17 PCI Express, 18 fatal), blue screens by their code, power-offs # with no shutdown and no blue screen (Kernel-Power 41 with no bugcheck code), the graphics driver reset by Windows # (Display 4101), and the games that crashed (Application Error 1000) with the module they crashed in most. An # overclock, undervolt or memory profile that is not stable shows here first, and no tweak fixes it. Reads only. function Get-Stability { $since = (Get-Date).AddDays(-30) $st = @{ cpu = 0; memory = 0; pcie = 0; fatal = 0; power = 0; gpu = 0; bsod = @{}; games = @() } $q = { param($h) try { @(Get-WinEvent -FilterHashtable $h -MaxEvents 2000 -ErrorAction Stop) } catch { @() } } foreach ($e in (& $q @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-WHEA-Logger'; Id = 17, 18, 19, 47; StartTime = $since })) { if ($e.Id -eq 19) { $st.cpu += 1 } elseif ($e.Id -eq 47) { $st.memory += 1 } elseif ($e.Id -eq 17) { $st.pcie += 1 } elseif ($e.Id -eq 18) { $st.fatal += 1 } } foreach ($e in (& $q @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-WER-SystemErrorReporting'; Id = 1001; StartTime = $since })) { if ("$($e.Properties[0].Value)" -match '^\s*0x([0-9a-fA-F]+)') { $c = [Convert]::ToInt64($Matches[1], 16); $st.bsod[$c] = 1 + [int]$st.bsod[$c] } } foreach ($e in (& $q @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-Kernel-Power'; Id = 41; StartTime = $since })) { if ("$($e.Properties[0].Value)" -match '^(0|0x0+)?$') { $st.power += 1 } } $st.gpu = @(& $q @{ LogName = 'System'; ProviderName = 'Display'; Id = 4101; StartTime = $since }).Count $listed = Get-GameListed $byGame = @{} foreach ($e in (& $q @{ LogName = 'Application'; ProviderName = 'Application Error'; Id = 1000; StartTime = $since })) { $path = "$($e.Properties[10].Value)" if (-not $path) { $path = "$($e.Properties[0].Value)" } $name = Resolve-GameName $path $listed if (-not $name) { continue } if (-not $byGame.ContainsKey($name)) { $byGame[$name] = @{ name = $name; n = 0; mods = @{} } } $byGame[$name].n += 1 $mod = "$($e.Properties[3].Value)" $byGame[$name].mods[$mod] = 1 + [int]$byGame[$name].mods[$mod] } $st.games = @($byGame.Values | Sort-Object -Property @{ Expression = { $_.n }; Descending = $true }) return $st } # Who a crash's module belongs to, and what to do: { what, what to do }. function Get-CrashModuleOwner([string]$m) { $l = $m.ToLower() if ($l -match '^(nvwgf2um|nvoglv|nvd3dum|nvldumd|nvcuda|nvgpucomp|nvlddmkm|nvapi|nvumdshim|nvrtum|nvwgf2)') { return @("NVIDIA's graphics driver ($m)", 'a clean install of the current NVIDIA driver (its installer: Custom, then Perform a clean installation) and no graphics card overclock while you test; if it started with a new driver, the one before') } if ($l -match '^(amdxx|atidxx|aticfx|amdvlk|atiumd|amdxc|amdihk|atio6axx|amdenc|amfrt)') { return @("AMD's graphics driver ($m)", "a clean install of the current AMD driver (AMD Software's Factory Reset option) and no graphics card overclock while you test; if it started with a new driver, the one before") } if ($l -match '^(igd|igxel|igc64|igdgmm|igvk|ig\d+icd|intelocl|igdumdim|igdrcl)') { return @("Intel's graphics driver ($m)", "the current Intel graphics driver from Intel's site, installed clean") } if ($l -match '^(d3d11|d3d12|d3d12core|dxgi|d3d9|dxcore|d3d10|d3dcompiler_\d+|vulkan-1)\.dll$') { return @("DirectX or Vulkan ($m), usually the graphics driver under it", 'a clean install of the current graphics driver') } if ($l -match '^(gameoverlayrenderer|discordhook|rtsshooks|owclient|overwolf|nvspcap|graphics-hook|obs-vulkan|medal|igo(32|64)|eosovh|reshade)') { return @("an overlay ($m)", "turn that overlay off for this game (Steam's, Discord's, RivaTuner's, Overwolf's, NVIDIA's or OBS's, whichever it is) and see if the crashes stop") } if ($l -match '^(easyanticheat|beclient|eac|vgk|faceit|eossdk)') { return @("its anti-cheat ($m)", "verify (repair) the game's files in its launcher, which reinstalls it") } if (-not $l -or $l -eq 'unknown' -or $l -match '^(ntdll|kernelbase|ucrtbase|msvcr\d*|msvcp\d*|vcruntime\d*(_\d)?|combase|kernel32)\.dll$') { return @("the game's own code", "verify (repair) the game's files in its launcher; if every game crashes, test with the memory profile (XMP/EXPO) and any overclock off") } return @($m, "look up $m, whose name says which program put it there") } # Each drive's own health (1.115.0): Windows' verdict for the disk (MSFT_PhysicalDisk), and the drive's own counters, # which Windows gives administrators (the tune is one): the share of its rated life used, its temperature against its # rated limit, and reads it could not correct. OMNIDX_TEST_DRIVE stands in one drive for the check: # name|health|wear|temperature|limit|uncorrected reads. function Get-DriveHealth { $out = @() if ($env:OMNIDX_TEST_DRIVE) { $f = $env:OMNIDX_TEST_DRIVE -split '\|' $out += @{ name = $f[0]; health = [int]$f[1]; ssd = $true; wear = [int]$f[2]; temp = [int]$f[3]; max = [int]$f[4]; errors = [long]$f[5] } } try { foreach ($pd in @(Get-CimInstance -Namespace 'root\Microsoft\Windows\Storage' -ClassName MSFT_PhysicalDisk -ErrorAction Stop)) { if ($pd.BusType -eq 7 -or $pd.BusType -eq 12) { continue } $h = @{ name = "$($pd.FriendlyName)".Trim(); health = [int]$pd.HealthStatus; ssd = ($pd.MediaType -eq 4 -or $pd.BusType -eq 17); wear = -1; temp = -1; max = -1; errors = -1 } try { $c = Get-CimAssociatedInstance -InputObject $pd -ResultClassName MSFT_StorageReliabilityCounter -ErrorAction Stop | Select-Object -First 1 if ($c) { if ($null -ne $c.Wear -and ($h.ssd -or [int]$c.Wear -gt 0)) { $h.wear = [int]$c.Wear } if ($c.Temperature) { $h.temp = [int]$c.Temperature } if ($c.TemperatureMax) { $h.max = [int]$c.TemperatureMax } if ($null -ne $c.ReadErrorsUncorrected) { $h.errors = [long]$c.ReadErrorsUncorrected } } } catch { } $out += $h } } catch { } return $out } # A blue screen's code in plain words: what usually causes it. function Get-BugcheckText([long]$c) { switch ($c) { 0x124 { return 'WHEA_UNCORRECTABLE_ERROR, a hardware error the processor could not correct: an overclock, undervolt or memory profile that is not stable is the usual cause, so load the BIOS defaults and see if it comes back' } 0x101 { return 'CLOCK_WATCHDOG_TIMEOUT, a processor core that stopped answering: usually a processor overclock or undervolt too far' } 0x1A { return 'MEMORY_MANAGEMENT: try the memory profile (XMP/EXPO) one step slower or off, and run Windows Memory Diagnostic' } 0x116 { return 'VIDEO_TDR_FAILURE, the graphics driver could not be reset: a clean install of the current driver, no graphics card overclock, and its power cables' } 0x133 { return "DPC_WATCHDOG_VIOLATION, a driver that held the processor too long: the SSD's firmware and the chipset driver first" } 0xD1 { return 'DRIVER_IRQL_NOT_LESS_OR_EQUAL, a driver (network, audio or USB most often): update them from the maker' } 0xA { return 'IRQL_NOT_LESS_OR_EQUAL, a driver or the memory: update the network, audio and chipset drivers, then test the memory with its profile off' } 0x3B { return 'SYSTEM_SERVICE_EXCEPTION, a driver or the memory: update the graphics and chipset drivers, then test the memory with its profile off' } 0x50 { return 'PAGE_FAULT_IN_NONPAGED_AREA, a driver or the memory: update drivers, then test the memory with its profile off' } 0x7E { return 'SYSTEM_THREAD_EXCEPTION_NOT_HANDLED, a driver: update the graphics and chipset drivers' } 0xEF { return "CRITICAL_PROCESS_DIED: run sfc /scannow in an administrator terminal, and check the drive's health" } 0x7A { return "KERNEL_DATA_INPAGE_ERROR, a read from the drive that failed: check the drive's health and its cable or slot" } default { return 'Reliability Monitor (perfmon /rel) lists each one with what the PC was doing' } } } function Show-Advice($m) { # Each drive's health and its own counters (1.115.0). foreach ($d in @(Get-DriveHealth)) { $nm = if ($d.name) { $d.name } else { 'A drive' } if ($d.health -eq 1 -or $d.health -eq 2) { Warn ("{0} reports itself as failing (Windows' health check: {1}). Copy what you want to keep off it now, and replace it: no tweak helps a failing drive." -f $nm, $(if ($d.health -eq 2) { 'Unhealthy' } else { 'Warning' })) } if ($d.errors -gt 0) { Warn ("{0} has had {1} it could not correct: a failing drive's sign. Keep a backup, and plan to replace it." -f $nm, (Plural ([int][math]::Min($d.errors, [int]::MaxValue)) 'read' 'reads')) } if ($d.wear -ge 90) { Warn ("{0} has used {1}% of its rated life. Plan to replace it, and keep a backup until then." -f $nm, $d.wear) } elseif ($d.wear -ge 70) { Say (" {0} has used {1}% of its rated life: fine for now, worth a backup." -f $nm, $d.wear) 'White' } if ($d.temp -gt 0 -and $d.max -gt 0 -and $d.temp -ge ($d.max - 5)) { Warn ("{0} is at {1} C, close to its {2} C limit, where drives slow themselves down: a heatsink or the board's M.2 cover, or air over it, helps." -f $nm, $d.temp, $d.max) } } # What Windows' own logs say crashed, or which part corrected errors by itself, over 30 days (1.114.0). $st = Get-Stability if ($st.cpu) { Warn ("Your processor corrected {0} by itself in the last 30 days (WHEA-Logger 19 in Event Viewer). That is the classic sign of an overclock or undervolt that is not stable (PBO, Curve Optimizer, a negative offset): back it off a step or load the BIOS defaults. No tweak fixes it." -f (Plural $st.cpu 'error' 'errors')) } if ($st.memory) { Warn ("Your memory corrected {0} by itself in the last 30 days (WHEA-Logger 47): often a memory profile (XMP/EXPO) too fast for this processor or board, or a failing stick. Try the profile one step slower or off, and run Windows Memory Diagnostic." -f (Plural $st.memory 'error' 'errors')) } if ($st.pcie) { Warn ("A PCI Express slot corrected {0} in the last 30 days (WHEA-Logger 17): the graphics card's or an NVMe drive's link. Reseat the card, leave out a riser cable, or set that slot one PCIe generation lower in the BIOS." -f (Plural $st.pcie 'error' 'errors')) } if ($st.fatal) { Warn ("A fatal hardware error stopped this PC {0} in the last 30 days (WHEA-Logger 18). Load the BIOS defaults (no overclock, no memory profile) and see if it comes back." -f (Plural $st.fatal 'time' 'times')) } foreach ($c in @($st.bsod.Keys | Sort-Object)) { Warn ("Blue screen 0x{0:X} ({1} in the last 30 days): {2}." -f $c, (Plural $st.bsod[$c] 'time' 'times'), (Get-BugcheckText $c)) } if ($st.power) { Warn ("The PC went off without shutting down {0} in the last 30 days, with no blue screen (Kernel-Power 41): a freeze you reset, a power cut, or a power supply or overclock that cannot hold under a game's load." -f (Plural $st.power 'time' 'times')) } if ($st.gpu) { Warn ("The graphics driver stopped answering and Windows reset it {0} in the last 30 days (Display 4101): a black screen for a moment, often with a game crash. A graphics card overclock or undervolt too far, an old or broken driver (a clean install of the current one), or the card's power cables." -f (Plural $st.gpu 'time' 'times')) } foreach ($g in @($st.games | Select-Object -First 3)) { $top = $g.mods.GetEnumerator() | Sort-Object -Property Value -Descending | Select-Object -First 1 $o = Get-CrashModuleOwner ([string]$top.Key) Warn ("{0} crashed {1} in the last 30 days, {2} {3}: {4}." -f $g.name, (Plural $g.n 'time' 'times'), $(if ($g.n -eq 1) { 'in' } elseif ($top.Value -eq $g.n) { 'every time in' } else { 'most often in' }), $o[0], $o[1]) } # Xbox mode (1.113.0), Windows' own: Windows 11 24H2 and 25H2 from update 7019 on (the two share their update numbers), # rolled out to every kind of PC since April 2026 and gradually, so the page may not be there yet. It runs through the # Xbox app, which the tune keeps whenever it is installed. if ($m.win -eq 11 -and $m.build -ge 26100) { $ubr = 0; try { $ubr = [int](Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name UBR -ErrorAction Stop).UBR } catch { } if ($m.build -gt 26200 -or $ubr -ge 7019) { Say (" Xbox mode, Windows' own: Settings > Gaming > Full screen experience, home app Xbox, then Win+F11. Games play full screen with Windows' background work held back while you play (Microsoft rolls it out gradually, so the page may not show here yet). It runs through the Xbox app{0}." -f $(if ($m.xboxUsed) { '' } else { ', which is not installed here: get it from the Microsoft Store first (with Extreme, the tune removes the Xbox app only when it is not installed)' })) 'White' } } # Advanced Shader Delivery (1.115.0): games from the Xbox PC app that support it download their shaders already compiled, # so the first minutes do not stutter and loading is far shorter (Forza Horizon 6 first). In preview for AMD's RDNA # graphics (Radeon RX 5000 to 9000, and the 680M to 890M built into Ryzen laptops) on Windows 11 24H2 and later, with AMD # Software 26.6.1 and Gaming Services 37.113.11003.0 or newer; it switches itself on, and only for the Xbox app's games. # Gaming Services' version is read from the same package list the Xbox check reads. OMNIDX_TEST_ASD stands in a card # (and the Xbox app) for the check. $asdGpu = if ($env:OMNIDX_TEST_ASD) { $env:OMNIDX_TEST_ASD } else { "$($m.gpu)" } if ($m.win -eq 11 -and $m.build -ge 26100 -and ($m.xboxUsed -or $env:OMNIDX_TEST_ASD) -and $asdGpu -match 'Radeon.*(\bRX\s*(5|6|7|9)\d{3}|\b(68|78|88|89)0M\b)') { $gsv = $null try { $gsv = Get-ChildItem -Path 'HKLM:\SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages' -Name -ErrorAction Stop | Where-Object { $_ -match '^Microsoft\.GamingServices_\d+(\.\d+){3}_' } | ForEach-Object { [version](($_ -split '_')[1]) } | Sort-Object -Descending | Select-Object -First 1 } catch { } $gsNote = if (-not $gsv) { ' (Gaming Services is not installed here: the Xbox app installs it with its first game)' } elseif ($gsv -lt [version]'37.113.11003.0') { " (Gaming Services here is $gsv`: Microsoft Store, Library, Get updates)" } else { '' } Say (" Advanced Shader Delivery, on this {0}: games from the Xbox app that support it download their shaders already compiled, so the first minutes do not stutter and loading is far shorter (Forza Horizon 6 first). It switches itself on with AMD Software 26.6.1 or newer and Gaming Services 37.113 or newer{1}. Games from Steam and other launchers compile their own, as before." -f $asdGpu, $gsNote) 'White' } if ($m.ramSlow) { Warn ("RAM is running at {0} MT/s but is rated for {1}: the memory profile (XMP / EXPO) is off in the BIOS. That is item 1 on your checklist and the biggest free gain on this PC." -f $m.ramNow, $m.ramRated) } if ($m.sticks -eq 1) { Warn "One stick of RAM: single channel. A matching second stick is the biggest upgrade this PC can get." } $lowScreens = @($m.screens | Where-Object { $_ -and $_.CanRise }) if ($lowScreens.Count) { foreach ($ls in $lowScreens) { Warn ("{0} runs at {1} Hz, and its monitor offers {2} Hz at {3} x {4}. The app's 'Use {2} Hz' button switches it and asks you to keep it (nothing answered in 15 seconds goes back), or Settings > System > Display > Advanced display." -f $(if ($ls.Primary) { 'Your main screen' } else { "Screen $($ls.Device -replace '^\\\\\.\\DISPLAY', '')" }), $ls.Hz, $ls.MaxHz, $ls.Width, $ls.Height) } } elseif (-not @($m.screens).Count -and $m.maxRefresh -and $m.refresh -and $m.refresh -ge 24 -and ($m.maxRefresh -gt ($m.refresh + 1))) { Warn ("Your display can do {0} Hz but Windows is set to {1} Hz. Settings > System > Display > Advanced display." -f $m.maxRefresh, $m.refresh) } # How old the graphics driver is, Show-Drivers says, with the other drivers. if ($m.noPageFile) { Warn "No page file. Games that need more memory than is free crash with an out-of-memory error instead of slowing down. The tune puts Windows' own setting (System managed) back." } if ($m.displayOnIgpu) { Warn ("The display seems to be driven by the integrated graphics ({0}) while a {1} is installed. Check the monitor cable is in the graphics card's ports, not the motherboard's; no tweak comes close to that." -f $m.igpuName, $m.gpu) } # Lanes are read at rest; a card's width does not drop when it idles (only its speed does), so fewer lanes at rest is # fewer lanes in a game. A laptop's card is soldered to its lanes, so there is nothing to move there. if ($m.gpuLink -and -not $m.laptop -and $m.gpuLink.width -lt $m.gpuLink.maxWidth) { $gl = $m.gpuLink $line = "{0} runs on {1} of its {2} PCI Express lanes." -f $gl.name, $gl.width, $gl.maxWidth if ($gl.width -le 4 -or ($gl.maxGen -gt 0 -and $gl.maxGen -le 3)) { Warn ($line + " At that width it loses real frames. It belongs in the top full-length slot, not on a riser cable, and the board's manual says which M.2 slots or second card take lanes from it.") } else { Say (" " + $line + " On PCI Express 4.0 and up that costs little; the top full-length slot gives it all of them.") } } if ($m.muxHint) { Say " This laptop has a graphics card next to its integrated chip, so its screen goes through the integrated chip. If the laptop's own control app has a GPU mode (a MUX switch, called Ultimate, dGPU, Discrete or Direct mode), switching it on lets the card drive the screen directly: more frames and less delay in games, at the cost of battery life while it is on. Windows' settings cannot do that part." } if ($m.sysFreeGb -ge 0 -and ($m.sysFreeGb -lt 20 -or $m.sysPct -lt 10)) { Warn ("{0} has {1} GB free ({2}%). Games stutter and shader caches fail on a full drive: Settings > System > Storage > Temporary files, or move a game to another drive." -f $env:SystemDrive, $m.sysFreeGb, $m.sysPct) } if ($m.sysHdd) { Warn "Windows is on a hard disk. An SSD is the biggest upgrade this PC can get; no tweak comes close." } if ($m.ramGb -lt 16) { Warn ("{0} GB of RAM. 16 GB is the floor for current games; the tune helps, but it cannot make memory." -f $m.ramGb) } if ($m.win -eq 10 -and $m.build -lt 19045) { Warn "Windows 10 is not on 22H2. Update it: the last builds fixed things no tweak can." } if ($m.win -eq 10 -and $m.hybrid) { Warn ("{0} has performance and efficiency cores, and this is Windows 10. Windows 11 reads the CPU's Thread Director and keeps a game on the performance cores; Windows 10 does not, and a game can land on the slow ones. For games on this CPU, Windows 11 is the better Windows." -f $m.cpu) } if ($m.rebar -eq 'off') { Warn ("Resizable BAR is off: the {0} sees the CPU through the old 256 MB window (NVIDIA's own tool says so). BIOS checklist, item 2: Above 4G Decoding and Re-Size BAR on, CSM off." -f $m.gpu) } if ($m.boot -and $m.boot.fwMs -ge 15000) { Warn ("The motherboard takes {0:0.0} s before Windows even starts (Task Manager's Last BIOS time). That is the slow part of this PC's boot; the BIOS checklist's item 8 is the fix{1}." -f ($m.boot.fwMs / 1000), $(if ($m.cpuVendor -eq 'AMD' -and $m.ddr5 -and -not $m.laptop) { ', and on this AM5 board Memory Context Restore is most of it' } else { '' })) } if ($m.onBattery) { Warn "Running on battery. Plug in: the restore point and the cut are best done on mains, and the plan is tuned for mains." } if ($m.optimizers.Count) { Warn ("Another optimizer is installed ({0}). Two of these fighting over the same settings is worse than one; consider removing it." -f ($m.optimizers -join ', ')) } if ($m.oem.Count) { Say (" OEM extras found: {0}. Not touched; remove any you do not use from Settings > Apps." -f ($m.oem -join ', ')) } if ($m.otherAv.Count) { Say (" Antivirus: {0}. Not touched." -f ($m.otherAv -join ', ')) } if ($m.vm) { Warn "This looks like a virtual machine. The tune will run, but the numbers mean little here." } if ($m.gpuHealth) { $g = $m.gpuHealth if ($g.throttle -contains 'thermal') { Warn ("The GPU is thermally throttling at idle ({0} C): dust, a stopped fan or a dead thermal pad. No tweak matters until that is fixed." -f $g.tempC) } elseif ($null -ne $g.tempC -and $g.tempC -ge 65) { Warn ("GPU at {0} C at idle. Under load that becomes a throttle: clean the fans and the case filters, and check the fan curve." -f $g.tempC) } if ($g.throttle -contains 'power brake') { Warn "The GPU reports a hardware power brake: the power supply or the power connector is pulling it back. Check the cable seating and the supply's rating." } if ($g.limitW -and $g.maxW -and ($g.limitW -lt ($g.maxW * 0.9))) { Say (" GPU power limit is {0} W of a possible {1} W: a laptop mode, an app's slider or the card's own default. The undervolt notes in the report are the safe way to more clock at the same limit." -f $g.limitW, $g.maxW) } } if ($m.passivePct -gt 0) { Warn ("A thermal zone reports a passive limit of {0}%: the CPU is being held back by heat right now, at idle. Cooling first; no tweak beats that." -f $m.passivePct) } elseif ($null -ne $m.cpuTempC -and $m.cpuTempC -ge 90) { Warn ("A thermal zone reads {0} C at idle. If that is the CPU, cooling is the whole problem." -f $m.cpuTempC) } if ($m.net) { $n = $m.net # A router timed by TCP answers in its own software: a few ms more than a ping, so the bar is set higher there. $gwTcp = $n.gatewayBy -ne 'ping' if ($null -ne $n.gatewayMs -and ($n.gatewayMs -ge $(if ($gwTcp) { 10 } else { 5 }) -or $n.gatewayJitter -ge $(if ($gwTcp) { 5 } else { 3 }) -or $n.gatewayLoss -gt 0)) { Warn ("The link to your router is {0} ms with {1} ms of jitter and {2}% loss over five {4}. {3}" -f $n.gatewayMs, $n.gatewayJitter, $n.gatewayLoss, $(if ($m.wifiLive) { 'That is the Wi-Fi: a cable to the router beats every network tweak there is.' } else { 'On a cable that points at the cable itself, the port or the router.' }), $(if ($gwTcp) { 'TCP connections (it drops pings)' } else { 'pings' })) } if ($null -ne $n.edgeMs -and $n.edgeLoss -gt 0) { Warn ("Packet loss to the internet edge ({0}% of five {1}). {2}" -f $n.edgeLoss, $(if ($n.edgeBy -ne 'ping') { 'TCP connections' } else { 'pings' }), $(if ($null -ne $n.gatewayMs -and $n.gatewayLoss -eq 0) { 'The router answered every time, so it is past it: the line or the provider. No tweak fixes loss.' } elseif ($null -ne $n.gatewayMs) { 'The router lost some too, so it starts in the home: the Wi-Fi, the cable or the router. No tweak fixes loss.' } else { 'That is the line or the router, and no tweak fixes loss.' })) } elseif ($null -ne $n.edgeMs -and $n.edgeJitter -ge 10) { Say (" Internet edge jitter is {0} ms: variable, which games feel as rubber-banding. Worth a word with the ISP if it stays." -f $n.edgeJitter) } } # The link itself: a wired adapter at 100 Mbps or less points at the cable; Wi-Fi on 2.4 GHz or a weak signal resends # packets. Wi-Fi is judged only when no cable is up (a game takes the cable when both are). if (-not $m.vm) { $wired = @(Get-NetAdapter -Physical -ErrorAction SilentlyContinue | Where-Object { $_.Status -eq 'Up' -and "$($_.PhysicalMediaType)" -notmatch '802\.11|Native' -and $_.Name -notmatch 'Wi-?Fi|Wireless|WLAN' -and $_.InterfaceDescription -notmatch 'Wi-?Fi|Wireless|WLAN|802\.11|Bluetooth' }) foreach ($a in $wired) { $bps = [int64]0; try { $bps = [int64]$a.ReceiveLinkSpeed } catch { } if ($bps -gt 0 -and $bps -le 100000000) { Warn ("The cable connection ({0}) is linked at {1}, not gigabit. On a gigabit adapter that is almost always the cable (damaged, or an old Cat5) or a router port that only does 100: a Cat5e or Cat6 cable in another port usually brings it to 1 Gbps, and a failing cable also drops packets." -f $a.InterfaceDescription, $a.LinkSpeed) } } if ($m.wifiLive -and -not $wired.Count) { $sig = 0; $ch = 0 # netsh prints its labels in the language of Windows (Senal, Sinal, Canal, Kanal): the signal is the one line that ends # in a percentage, the band its value in GHz, and the channel's label is matched in the languages that spell it alike. $band24 = $false foreach ($l in @(& netsh.exe wlan show interfaces 2>$null)) { if ($l -match '^[^:]+:\s*(\d+)\s*%\s*$') { $sig = [int]$matches[1] } elseif ($l -match '^[^:]+:\s*2[.,]4\s*GHz\s*$') { $band24 = $true } elseif ($l -match '^\s*(Channel|Canal|Canale|Kanal|Kanaal)\s*:\s*(\d+)\s*$') { $ch = [int]$matches[2] } } if ($band24 -or ($ch -ge 1 -and $ch -le 14)) { Warn ("Wi-Fi on 2.4 GHz{0}: the crowded band every neighbour's router and microwave share. The same router's 5 GHz (or 6 GHz) network is quicker and steadier within a room or two; a cable beats both." -f $(if ($ch -ge 1 -and $ch -le 14) { " (channel $ch)" } else { '' })) } if ($sig -gt 0 -and $sig -lt 60) { Warn ("Wi-Fi signal at {0}%: weak packets get sent again, and every resend is lag. Closer to the router, a mesh point or powerline adapter near the PC, or a cable." -f $sig) } } } if ($m.streamer.Count) { Say (" Streaming software found ({0}): the camera frame server and Windows Graphics Capture stay, in Extreme too. The report has the encoder and capture notes." -f ($m.streamer -join ', ')) } # The 2026 anti-cheat rules: FACEIT (all players by mid-2026) and Vanguard On-Demand (Windows 11 25H2) want Secure Boot, # TPM 2.0, IOMMU and memory integrity on. Said here, once, because turning any of them off is the one "tweak" that ends a game. if ($m.riot -or $m.faceit) { $who = @(@($(if ($m.riot) { 'VALORANT (Vanguard)' }), $(if ($m.faceit) { 'FACEIT' })) | Where-Object { $_ }) $missing = @(@($(if (-not $m.secureBoot) { 'Secure Boot' }), $(if (-not $m.tpm) { 'TPM 2.0' }), $(if (-not $m.iommu) { 'IOMMU' }), $(if (-not $m.vbs) { 'memory integrity' })) | Where-Object { $_ }) if ($missing.Count) { Warn ("{0} found. Since 2026 it requires Secure Boot, TPM 2.0, IOMMU and memory integrity on, and {1} {2} off on this PC. The BIOS checklist (items 4, 5 and 12) says where; the tune never turns any of them off." -f ($who -join ' and '), ($missing -join ', '), $(if ($missing.Count -eq 1) { 'is' } else { 'are' })) } else { Say (" {0} found: Secure Boot, TPM, IOMMU and memory integrity are on, which is what its anti-cheat wants. The tune leaves all four alone." -f ($who -join ' and ')) } # FACEIT's own timetable: Windows 11 for every player from October 2026 (its security FAQ). Said in the console, not only in the report. if ($m.faceit -and $m.win -eq 10) { Warn "FACEIT requires Windows 11 from October 2026, and this PC runs Windows 10. The tune changes nothing about that; plan the upgrade before then." } } if ($m.x3dDual) { $vc = Get-Service -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -match '3D V-Cache' } | Select-Object -First 1 if (-not $vc) { Warn "Dual-CCD X3D CPU without the AMD 3D V-Cache Performance Optimizer service: games can land on the half without the big cache. Install the AMD chipset driver from amd.com." } elseif ("$($vc.StartType)" -eq 'Disabled' -or "$($vc.Status)" -ne 'Running') { Warn "The AMD 3D V-Cache Performance Optimizer service is not running: games can land on the half of the CPU without the big cache. Services > set it to Automatic and start it." } else { Say " Dual-CCD X3D CPU: AMD's optimizer is running, and Game Mode and Game Bar stay on because it spots games through them." } } # What another tool left switched off: said here as well as in the report, since the console is where most people look. foreach ($n in @(Get-LeftoverNotes $m)) { if ($n -notmatch '^Nothing found') { Say (" ! Left by another tool: " + $n) 'Yellow' } } } <# An estimate of what this PC needs after the tune and a restart: what Windows itself runs on this build, plus the services and driver helpers this hardware keeps. Printed as "about", because it is one. #> function Get-SafeBar($m, [switch]$AsExtreme) { # Measured in the restart film, three minutes after the sign-in on a clean Windows 11 in a virtual machine, the # standard tune with the service hosts grouped: 78 with the film's own helper (take 36392477956), so about 75 of # Windows' own; 95 before the grouping (take 36388095575). With Extreme and OmniDx Edition: about 51. $t = 69 if ($m.win -eq 11) { $t += 6 } switch ($m.gpuVendor) { 'NVIDIA' { $t += 6 } 'AMD' { $t += 4 } 'Intel' { $t += 2 } } if ($m.laptop) { $t += 8 } if ($m.printers) { $t += 2 } if ($m.btRadio) { $t += 3 } if ($m.wifi) { $t += 2 } if ($m.biometric) { $t += 2 } if ($m.vpn) { $t += 4 } if ($m.vbs) { $t += 2 } if ($m.xboxUsed -and -not (Test-CutXbox $m)) { $t += 5 } if ($m.otherAv.Count) { $t += 6 } # -NoGroup: every service in a host of its own again, about twenty more (the restart film: 95, against 78 grouped). if ($NoGroup) { $t += 18 } # Extreme cuts the extra services, the search indexer and the shell's helpers: about half a dozen fewer. if (($Extreme -and -not $script:ExtremeDeclined) -or $AsExtreme) { $t -= 6 } return $t } <# Which services this PC keeps, and why. Built from what Get-Machine found, used by the cut and by the free report, so the two never disagree. #> <# Whether the Xbox pieces go: when asked (-CutXbox), or under -Extreme when nothing on this PC uses them. #> function Test-CutXbox($m) { return [bool]($CutXbox -or ($Extreme -and -not $m.xboxUsed -and -not $m.xboxPad -and -not $m.x3dDual)) } function Get-KeepList($m) { $k = @{} if ($m.printers) { $k['Spooler'] = 'a printer is installed'; $k['WiaRpc'] = 'a printer (or scanner) is installed'; $k['stisvc'] = 'a printer (or scanner) is installed'; $k['PrintWorkflowUserSvc'] = 'a printer is installed' } if ($m.vm) { foreach ($n in 'vmicguestinterface', 'vmicheartbeat', 'vmickvpexchange', 'vmicrdv', 'vmicshutdown', 'vmictimesync', 'vmicvmsession', 'vmicvss') { $k[$n] = 'this is a virtual machine' } } if ($m.domain) { $k['cloudidsvc'] = 'joined to a domain' } if ($m.touch) { $k['PenService'] = 'touch screen' } if ($m.btDevices -or $m.btRadio) { foreach ($n in 'bthserv', 'BTAGService', 'BthAvctpSvc') { $k[$n] = 'Bluetooth is in use' } } if ($m.wifi) { $k['WlanSvc'] = $(if ($m.wifiLive) { 'Wi-Fi is your connection' } else { 'a Wi-Fi adapter is present' }); $k['RmSvc'] = 'a Wi-Fi adapter is present' } if ($m.touch -or $m.laptop) { $k['TabletInputService'] = $(if ($m.touch) { 'touch screen' } else { 'laptop' }) } if ($m.biometric) { $k['WbioSrvc'] = 'a Windows Hello reader is present' } if ($m.laptop) { foreach ($n in 'SensorService', 'SensrSvc', 'SensorDataService', 'WwanSvc') { $k[$n] = 'laptop' } } if ($m.laptop) { $k['NahimicService'] = "laptop: its speakers are tuned through Nahimic" } if ($m.laptop) { $k['RtkAudioUniversalService'] = 'laptop: some tune their speakers through it' } elseif (Test-Path (Join-Path $env:ProgramFiles 'OmniDx\Edition\OmniHub.exe')) { $k['RtkAudioUniversalService'] = 'OmniDx Edition has its own switch for it (Hub > Running)' } if ($m.laptop) { foreach ($n in 'HPAppHelperCap', 'HPDiagsCap', 'HPNetworkCap', 'HPSysInfoCap', 'LenovoVantageService', 'ImControllerService', 'AsusAppService', 'ArmouryCrateService', 'ASUSOptimization') { $k[$n] = 'laptop: the PC maker runs the fans, the battery limit or the hotkeys through it' } } # A Lenovo whose fans and hotkeys have services of their own does not need Vantage running (Test-LenovoOwnControls). if ($m.laptop -and (Test-LenovoOwnControls)) { foreach ($n in 'LenovoVantageService', 'ImControllerService') { $k.Remove($n) } } if ($m.vpn) { foreach ($n in 'iphlpsvc', 'SSDPSRV', 'upnphost') { $k[$n] = 'a VPN adapter is present' } } if (-not $m.allSsd) { $k['SysMain'] = 'a hard disk benefits from prefetch' } if ($m.xboxUsed -and -not (Test-CutXbox $m)) { foreach ($n in 'XblAuthManager', 'XblGameSave', 'XboxNetApiSvc', 'XboxGipSvc') { $k[$n] = 'Game Pass, the Xbox app or Minecraft is installed' } } if ($m.xboxPad -and -not (Test-CutXbox $m)) { $k['XboxGipSvc'] = 'an Xbox controller is connected' } if ($m.streamer.Count) { $k['FrameServer'] = 'streaming software uses the camera frame server'; $k['CaptureService'] = 'streaming software uses Windows Graphics Capture' } $k['Themes'] = 'Windows 10 falls back to the classic look without it' return $k } function Test-PendingReboot { if (Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending') { return $true } if (Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') { return $true } return $false } <# When a standard account gave an administrator's password at the UAC prompt, HKCU and %APPDATA% belong to the administrator, not to the person at the keyboard. Point every per-user change at the signed-in account instead. #> function Resolve-User { try { $console = (Get-CimInstance Win32_ComputerSystem -ErrorAction Stop).UserName if (-not $console) { return } $me = "$env:USERDOMAIN\$env:USERNAME" if ($console -ieq $me) { return } $sid = (New-Object System.Security.Principal.NTAccount($console)).Translate([System.Security.Principal.SecurityIdentifier]).Value if (-not (Test-Path "Registry::HKEY_USERS\$sid")) { return } $prof = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid" -ErrorAction Stop).ProfileImagePath if (-not $prof -or -not (Test-Path $prof)) { return } $script:HKCU = "Registry::HKEY_USERS\$sid" $script:UserProfile = $prof $script:AppData = Join-Path $prof 'AppData\Roaming' $script:LocalAppData = Join-Path $prof 'AppData\Local' $script:UserName = ($console -split '\\')[-1] Warn ("Tuning the signed-in account {0}; administrator rights came from {1}. Per-user settings go to {0}." -f $console, $me) } catch { } } $script:ProcNames = @{} function Save-ProcessList([string]$tag) { try { $procs = @(Get-Process -ErrorAction SilentlyContinue) $script:ProcNames[$tag] = @($procs | ForEach-Object { $_.ProcessName } | Sort-Object -Unique) $rows = $procs | Group-Object ProcessName | Sort-Object -Property @{ Expression = 'Count'; Descending = $true }, @{ Expression = 'Name'; Descending = $false } | ForEach-Object { "{0,-40} x{1,-3} {2,8:N0} MB" -f $_.Name, $_.Count, (($_.Group | Measure-Object WorkingSet64 -Sum).Sum / 1MB) } Set-Content -Path (Join-Path $script:Root ("processes-{0}-{1}.txt" -f $tag, $script:Stamp)) -Value $rows -Encoding UTF8 } catch { } } <# Process names that were running before and are not now: the cut, by name. #> function Get-GoneProcesses { if (-not $script:ProcNames.before -or -not $script:ProcNames.after) { return @() } return @($script:ProcNames.before | Where-Object { $script:ProcNames.after -notcontains $_ }) } <# The number that counts is the one after a restart, and nobody is at the keyboard to read it then. One task, run once at the next sign-in, writes it to a text file and removes itself. Disclosed in the report; -NoAfterCount skips it; undo removes it. #> <# A sign-in task's PowerShell with no window at all. -WindowStyle Hidden hides the console only once PowerShell has started, so for a moment (seconds, on a busy sign-in) an empty blue window sits on the desktop and takes the focus. Windows 11's console host starts it headless instead; Windows 10 keeps the hidden style. Every program by its full path, in a folder that exists: a task that starts the moment you sign in with conhost.exe by name alone failed with "path not found" (0x80070003) after an Extreme restart (take 36463676533), while the same action a minute or three after the sign-in ran. #> function New-QuietAction([string]$file) { $sys = Join-Path $env:WINDIR 'System32' $pwsh = Join-Path $sys 'WindowsPowerShell\v1.0\powershell.exe' $ps = '-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "' + $file + '"' $dir = Split-Path $file if ([Environment]::OSVersion.Version.Build -ge 22000 -and (Test-Path (Join-Path $sys 'conhost.exe'))) { return New-ScheduledTaskAction -Execute (Join-Path $sys 'conhost.exe') -Argument ('--headless ' + $pwsh + ' ' + $ps) -WorkingDirectory $dir } return New-ScheduledTaskAction -Execute $pwsh -Argument $ps -WorkingDirectory $dir } <# A sign-in task's settings: on battery as well (Windows' default is not to start on battery at all), stopped if it hangs, and started again up to three times, a minute apart, when Windows could not start it. #> function New-SignInSettings([int]$minutes) { $s = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes $minutes) -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 1) $s.Priority = 8 # below normal: behind whatever the person has open return $s } function Register-AfterCount { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Register-AfterCount is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # keeping it cut # --------------------------------------------------------------------------- <# Windows updates turn pieces of the tune back on: a feature update resets service start types, re-enables tasks and puts a few values back. This is the comparison, shared by -Status (read only) and by keep.ps1 (which puts back what drifted when it is run): every registry value, service start type and scheduled task the recorded runs changed, against the machine now. Where two runs touched the same thing the newer one wins. Deliberately not checked: startup apps and personal preferences (mouse acceleration, the taskbar, Game Bar, visual effects, GPU preferences). If those change after the tune it was you, in Settings or Task Manager, and that must win. Records made by versions before 1.6 carry no target value and are skipped. #> function Get-Drift { param([string[]]$Files, [switch]$Fix) $yours = @('\Control Panel\', '\StartupApproved\', '\CurrentVersion\Run', '\Explorer\Advanced', '\GameBar', 'GameConfigStore', '\GameDVR', '\Accessibility\', '\UserGpuPreferences', '\VisualEffects', '\Explorer\Serialize', '\Personalization', '\Classes\CLSID\', '\Themes\Personalize', '\PushNotifications', '\Notifications\', '\CurrentVersion\Search', '\WindowMetrics', '\Dwm') $entries = @() # Each entry knows the record it came from, so a newer copy of a recorded service (below) is written into that record. foreach ($f in @($Files | Sort-Object)) { try { foreach ($e in @(Get-Content $f -Raw | ConvertFrom-Json | ForEach-Object { $_ })) { $e | Add-Member -NotePropertyName 'from' -NotePropertyValue $f -Force; $entries += $e } } catch { } } [array]::Reverse($entries) $seen = @{}; $checked = 0; $fixed = 0; $failed = 0 $drift = New-Object System.Collections.ArrayList # Services and tasks listed once, up front: one query each for sixty services and eighty tasks kept the old sign-in # keep task (and WMI) busy for most of a minute. $taskSvc = $null # Tasks Windows switches back on by itself whenever a program uses what they look after: WinINet turns its cache task on # again the next time Edge or an updater opens the cache (the Windows check, 1.84.0). Cut again quietly, with its task host # stopped, instead of reported at every -Status as if an update had undone the tune. $comesBack = @('\microsoft\windows\wininet\cachetask') foreach ($c in $entries) { $id = '' if ($c.type -eq 'reg') { $id = 'reg|' + $c.path + '|' + $c.name } elseif ($c.type -eq 'service') { $id = 'svc|' + $c.name } elseif ($c.type -eq 'task') { $id = 'task|' + $c.path + $c.name } if (-not $id -or $seen.ContainsKey($id)) { continue } $seen[$id] = $true try { if ($c.type -eq 'reg') { if (-not $c.PSObject.Properties['value']) { continue } $skip = $false; foreach ($y in $yours) { if ($c.path -like ('*' + $y + '*')) { $skip = $true } } if ($skip) { continue } # A value this account may not read is left out, not reported missing: Status and keep.ps1 -Check run without # administrator rights (the Hub's check when it opens, 1.117.0), and some keys the tune sets (an event trace # session's, a service's own) read only for administrators. Missing means the read said so, nothing else. $cur = $null; $has = $false; $denied = $false try { $cur = (Get-ItemProperty -Path $c.path -Name $c.name -ErrorAction Stop).($c.name); $has = $true } catch { $denied = $_.Exception -is [System.Security.SecurityException] -or $_.Exception -is [System.UnauthorizedAccessException] } if ($denied) { continue } $checked++ if ($c.removed) { if (-not $has) { continue } [void]$drift.Add(("{0}\{1} is back" -f $c.path, $c.name)) if ($Fix) { Remove-ItemProperty -Path $c.path -Name $c.name -Force -ErrorAction Stop; $fixed++ } continue } $want = $c.value # A DWord reads back as a signed 32-bit value (0xFFFFFFFF is -1), and PowerShell # types the literal 0xFFFFFFFF itself as -1; both sides go through the same mask. $norm = { param($v, $kind) if ($kind -eq 'DWord') { try { return [string]([int64]$v -band [int64]4294967295) } catch { return "$v" } } else { return (@($v) -join ',') } } $same = $false if ($has) { $same = ((& $norm $cur $c.kind) -eq (& $norm $want $c.kind)) } if ($same) { continue } [void]$drift.Add(("{0}\{1} = {2} (wanted {3})" -f $c.path, $c.name, $(if ($has) { (& $norm $cur $c.kind) } else { 'missing' }), (& $norm $want $c.kind))) if ($Fix) { if ($c.kind -eq 'Binary') { $want = [byte[]]@($want | ForEach-Object { [byte]$_ }) } if (-not (Test-Path $c.path)) { New-Item -Path $c.path -Force | Out-Null } New-ItemProperty -Path $c.path -Name $c.name -Value $want -PropertyType $c.kind -Force -ErrorAction Stop | Out-Null $fixed++ } } elseif ($c.type -eq 'service') { $svc = Get-Service -Name $c.name -ErrorAction SilentlyContinue if (-not $svc) { continue } $checked++ # The start mode from the service's own key, as sc.exe reads it (the template for per-user services such as # WpnUserService, which is what the tune configured). Not through WMI: a WMI question leaves a provider host # (WmiPrvSE) running for minutes after it (the clean Windows 11 film, 1.93.0). $mode = $null try { $sv = (Get-ItemProperty -LiteralPath ('HKLM:\SYSTEM\CurrentControlSet\Services\' + $c.name) -Name Start -ErrorAction Stop).Start; $mode = switch ([int]$sv) { 2 { 'Automatic' } 3 { 'Manual' } 4 { 'Disabled' } default { '' } } } catch { } if (-not $mode) { $mode = $svc.StartType.ToString() } if ($mode -eq 'Auto' -or $mode -eq 'AutomaticDelayedStart') { $mode = 'Automatic' } if ($mode -eq $c.now) { continue } [void]$drift.Add(("service {0} is {1} (wanted {2})" -f $c.name, "$mode".ToLower(), "$($c.now)".ToLower())) if ($Fix) { $s = switch ("$($c.now)") { 'Disabled' { 'disabled' } 'Manual' { 'demand' } default { 'auto' } } & sc.exe config $c.name start= $s | Out-Null if ($LASTEXITCODE -ne 0) { throw "sc.exe returned $LASTEXITCODE" } if ($c.now -ne 'Automatic' -and $svc.Status -eq 'Running') { try { Stop-Service -Name $c.name -Force -ErrorAction Stop -WarningAction SilentlyContinue } catch { } } $fixed++ } } elseif ($c.type -eq 'task') { # Through Task Scheduler's own COM interface, not the ScheduledTask cmdlets: those ask WMI, and a WMI question # leaves provider hosts (WmiPrvSE) running for minutes (the restart film, 1.98.0, found two). if ($null -eq $taskSvc) { $taskSvc = New-Object -ComObject Schedule.Service; $taskSvc.Connect() } $fp = "$($c.path)".TrimEnd('\'); if (-not $fp) { $fp = '\' } $t = $null; try { $t = $taskSvc.GetFolder($fp).GetTask("$($c.name)") } catch { } if (-not $t) { continue } $checked++ if (-not $t.Enabled) { continue } if ($comesBack -contains ("$($c.path)$($c.name)").ToLower()) { if ($Fix) { $t.Enabled = $false; try { $t.Stop(0) } catch { } } continue } [void]$drift.Add(("task {0} is on again" -f $c.name)) if ($Fix) { $t.Enabled = $false; $fixed++ } } } catch { $failed++ } } # A newer copy of a service the tune set (1.107.0). Some updaters carry their version in the service's name # (GoogleUpdaterService156.0.8067.0), so every update of the program installs a fresh pair set to start with # Windows, which a check by name never sees. Same name before the version: set the way the recorded one was, and # written into the same record, so undo puts it back with the rest. Read from the services' own keys, not WMI. $family = @{} foreach ($c in $entries) { if ($c.type -ne 'service' -or "$($c.now)" -eq 'Automatic' -or $c.choice) { continue } if ("$($c.name)" -match '^(.*[A-Za-z])(\d+(\.\d+){1,3})$' -and -not $family.ContainsKey($Matches[1])) { $family[$Matches[1]] = $c } } if ($family.Count) { foreach ($k in @(Get-ChildItem -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Services' -ErrorAction SilentlyContinue)) { $n = $k.PSChildName if ($seen.ContainsKey('svc|' + $n) -or $n -notmatch '^(.*[A-Za-z])(\d+(\.\d+){1,3})$' -or -not $family.ContainsKey($Matches[1])) { continue } $rc = $family[$Matches[1]] $start = $null; try { $start = [int](Get-ItemProperty -LiteralPath $k.PSPath -Name Start -ErrorAction Stop).Start } catch { } if ($start -ne 2) { continue } $checked++ [void]$drift.Add(("service {0} is automatic: a newer copy of {1} (wanted {2})" -f $n, $rc.name, "$($rc.now)".ToLower())) if (-not $Fix) { continue } try { & sc.exe config $n start= $(if ("$($rc.now)" -eq 'Disabled') { 'disabled' } else { 'demand' }) | Out-Null if ($LASTEXITCODE -ne 0) { throw "sc.exe returned $LASTEXITCODE" } try { Stop-Service -Name $n -Force -ErrorAction Stop -WarningAction SilentlyContinue } catch { } $list = @(Get-Content $rc.from -Raw | ConvertFrom-Json | ForEach-Object { $_ }) + @([pscustomobject]@{ type = 'service'; name = $n; prev = 'Automatic'; now = "$($rc.now)"; copyOf = "$($rc.name)" }) ConvertTo-Json -InputObject @($list) -Depth 6 | Set-Content -Path $rc.from -Encoding UTF8 $dir = Split-Path $rc.from $newest = Get-ChildItem $dir -Filter 'changes-*.json' | Where-Object { $_.Name -ne 'changes-latest.json' } | Sort-Object Name | Select-Object -Last 1 if ($newest -and $newest.FullName -eq (Get-Item $rc.from).FullName) { Copy-Item $rc.from (Join-Path $dir 'changes-latest.json') -Force } $seen['svc|' + $n] = $true $fixed++ } catch { $failed++ } } } return @{ checked = $checked; drift = @($drift); fixed = $fixed; failed = $failed } } # Services that stopped unexpectedly (the Service Control Manager's events 7031 and 7034, the same in every language) # since a given time: with the service hosts grouped a crash takes its host's other services down too, and this is # where that shows. Status reads the last seven days. function Get-ServiceStops { param([datetime]$Since) $ev = @(); try { $ev = @(Get-WinEvent -FilterHashtable @{ LogName = 'System'; ProviderName = 'Service Control Manager'; Id = 7031, 7034; StartTime = $Since } -ErrorAction Stop) } catch { } return @($ev | Group-Object { "$($_.Properties[0].Value)" } | Sort-Object Count -Descending | ForEach-Object { if ($_.Count -gt 1) { '{0} x{1}' -f $_.Name, $_.Count } else { $_.Name } }) } <# keep.ps1: written next to undo.ps1 with Get-Drift's text pasted in, so it runs on its own. Nothing starts it (1.117.0): no task, nothing at sign-in. It runs when a person runs it, or when the OmniDx Hub's "Put them back" does; -Check says what it would do without doing it. #> $script:KeepScript = @' <# OmniDx Tune - keep. Windows updates turn pieces of the tune back on: a service set to start again, a task re-enabled, a value reset. This compares every recorded change with the machine and puts back what drifted. Nothing starts it: no task, nothing at sign-in. It runs when you run it, or when the OmniDx Hub's "Put them back" does. It touches only what the tune recorded in changes-*.json next to it, never adds anything, leaves startup apps and personal preferences alone, and does nothing at all once undo.ps1 has run. Administrator PowerShell: powershell -ExecutionPolicy Bypass -File C:\OmniDx\undo\keep.ps1 To only look (no administrator rights needed): add -Check Each put-back is logged in C:\OmniDx\keep-log.txt #> param([switch]$Check) $ErrorActionPreference = 'Continue' $dir = $PSScriptRoot $files = @(Get-ChildItem $dir -Filter 'changes-*.json' -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne 'changes-latest.json' } | ForEach-Object { $_.FullName }) $logFile = Join-Path (Split-Path $dir) 'keep-log.txt' if (-not $files.Count) { Write-Host "Nothing recorded here; nothing to keep."; exit 0 } if (-not $Check -and -not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "Putting things back needs an administrator PowerShell. -Check looks without one." -ForegroundColor Yellow exit 2 } $took = [Diagnostics.Stopwatch]::StartNew() __DRIFT__ $r = Get-Drift -Files $files -Fix:(-not $Check) $line = "{0} checked {1}, {2} had drifted{3}{4}; {5} s" -f (Get-Date -Format s), $r.checked, $r.drift.Count, $(if ($Check) { ' (check only)' } elseif ($r.fixed) { ", $($r.fixed) put back" } else { '' }), $(if ($r.failed) { ", $($r.failed) could not be" } else { '' }), [math]::Round($took.Elapsed.TotalSeconds, 1) Write-Host $line -ForegroundColor $(if ($r.drift.Count) { 'Yellow' } else { 'Green' }) foreach ($d in $r.drift) { Write-Host (" " + $d) -ForegroundColor DarkGray } # A look changes nothing, the log included; a put-back is written down with what it found. if (-not $Check) { try { $old = @(); if (Test-Path $logFile) { $old = @(Get-Content $logFile -ErrorAction SilentlyContinue | Select-Object -Last 199) } Set-Content -Path $logFile -Value ($old + @($line) + @($r.drift | ForEach-Object { " " + $_ })) -Encoding UTF8 } catch { } } '@ <# Since 1.117.0 there is no keep task: nothing of the tune stays to run at sign-in or in the background (the after-restart count runs once and removes itself). A run removes the one an earlier version left, writes keep.ps1 for whoever wants to run it, and says how things come back after an update: status says what an update turned on, the OmniDx Hub checks when it is opened and puts it back in one click, and running the command again does the same (same key, same PC, free). #> function Remove-KeepTask([string]$why) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Remove-KeepTask is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } function Register-Keep { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Register-Keep is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Ten runs of everything stay in C:\OmniDx; older logs, reports and snapshots go. Undo records stay until undo moves them, and a run's backup folder stays as long as its record does. #> function Limit-History { $keep = 10 foreach ($pat in 'log-*.txt', 'machine-*.json', 'processes-before-*.txt', 'processes-after-*.txt', 'report-20*.txt', 'report-20*.html', 'report-preview-*.txt', 'summary-*.json', 'card-*.png') { try { Get-ChildItem $script:Root -Filter $pat -File -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -Skip $keep | Remove-Item -Force -ErrorAction SilentlyContinue } catch { } } try { $live = @(Get-ChildItem (Join-Path $script:Root 'undo') -Filter 'changes-*.json' -ErrorAction SilentlyContinue | ForEach-Object { $_.BaseName -replace '^changes-', '' }) Get-ChildItem (Join-Path $script:Root 'backup') -Directory -ErrorAction SilentlyContinue | Where-Object { $live -notcontains $_.Name } | Sort-Object Name -Descending | Select-Object -Skip $keep | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } catch { } } <# -Status: the runs recorded here, whether their settings are still in place, what Windows put back, the last put-back, the after-restart count, and the key. Reads only, and needs no administrator rights. #> function Show-Status { Head "Status" $dir = Join-Path $script:Root 'undo' $files = @(Get-ChildItem $dir -Filter 'changes-*.json' -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne 'changes-latest.json' } | Sort-Object Name) $undone = @(Get-ChildItem (Join-Path $dir 'done') -Filter 'changes-*.json' -ErrorAction SilentlyContinue) $lic = Get-ItemProperty 'HKLM:\SOFTWARE\OmniDx\Tune' -ErrorAction SilentlyContinue if ($lic -and $lic.Key) { Say (" Key: {0}-****-****-{1}, bound to this PC {2}" -f $lic.Key.Split('-')[0], $lic.Key.Split('-')[-1], "$($lic.Bound)".Substring(0, [math]::Min(10, "$($lic.Bound)".Length))) $view = Get-ServerView $lic.Key if ($view) { Say (" The licence server says: {0}." -f $view) $(if ($view -match 'issued;') { 'Gray' } else { 'Yellow' }) } } else { Say " Key: none bound on this PC yet." } if (-not $files.Count) { Say (" Runs in place: none{0}." -f $(if ($undone.Count) { " ({0} undone)" -f $undone.Count } else { '' })) if (Get-ScheduledTask -TaskName 'OmniDx keep' -ErrorAction SilentlyContinue) { Say " A keep task from an earlier version is still registered with nothing to keep; undo, or any run, removes it." 'Yellow' } Say (" Processes running now: {0}" -f (Get-ProcessCount)) 'White' return } foreach ($f in $files) { $n = 0; try { $n = @(Get-Content $f.FullName -Raw | ConvertFrom-Json | ForEach-Object { $_ }).Count } catch { } Say (" Run {0}: {1} recorded" -f ($f.BaseName -replace '^changes-', ''), (Plural $n 'change' 'changes')) } if ($undone.Count) { Say (" Runs in place: {0} ({1} undone earlier, in undo\done)." -f $files.Count, $undone.Count) } $r = Get-Drift -Files @($files | ForEach-Object { $_.FullName }) if ($r.drift.Count) { Say (" Settings checked: {0}. Windows has put back {1}:" -f $r.checked, $r.drift.Count) 'Yellow' $r.drift | Select-Object -First 20 | ForEach-Object { Say (" - " + $_) } if ($r.drift.Count -gt 20) { Say (" ... and {0} more" -f ($r.drift.Count - 20)) } Say " To put them back: run the command again (same key, same PC, free); with the OmniDx Hub, open it and press Put them back; or in an administrator PowerShell: powershell -ExecutionPolicy Bypass -File C:\OmniDx\undo\keep.ps1" 'White' } else { Say (" Settings checked: {0}. All still in place." -f $r.checked) 'Green' } Say " (Startup apps and personal preferences are not checked: if those changed, it was you, and that wins.)" # Nothing of the tune stays at sign-in (1.117.0): the last put-back, from its log, and a keep task an earlier version left. $keepLog = Join-Path $script:Root 'keep-log.txt' $last = $null; if (Test-Path $keepLog) { $last = @(Get-Content $keepLog -ErrorAction SilentlyContinue | Where-Object { $_ -match '^\d{4}-' }) | Select-Object -Last 1 } if ($last) { Say (" Last put-back: {0}" -f $last) } if (Get-ScheduledTask -TaskName 'OmniDx keep' -ErrorAction SilentlyContinue) { Say " A keep task from an earlier version is still registered: it runs at sign-in. Run the command again and it goes (nothing of the tune stays at sign-in since 1.117.0)." 'Yellow' } # Whether Windows could start the one-shot after-restart count at all, in words: a task Windows failed to start leaves # no log of its own, only this code (restart takes found "path not found" at the moment of a sign-in). foreach ($tn in 'OmniDx after-restart count') { $t = Get-ScheduledTask -TaskName $tn -ErrorAction SilentlyContinue if (-not $t) { continue } $ti = $null; try { $ti = Get-ScheduledTaskInfo -InputObject $t -ErrorAction Stop } catch { } if (-not $ti -or $ti.LastRunTime -lt [datetime]'2000-01-01') { continue } # As eight hex digits: Windows PowerShell reads 0x80070003 as a negative number, never equal to the task's code. $code = '{0:X8}' -f ([int64]$ti.LastTaskResult -band 0xFFFFFFFFL) $words = switch ($code) { '00000000' { 'ran to the end' } '00041301' { 'running now' } '00041303' { 'has not run yet' } '80070002' { 'Windows could not start it: file not found' } '80070003' { 'Windows could not start it: path not found' } '800704DD' { 'Windows could not start it: nobody was signed in' } '800710E0' { 'Windows refused to start it (the operator or administrator refused the request)' } default { "ended with 0x$code" } } Say (" {0}: last start {1:g}, {2}" -f $tn, $ti.LastRunTime, $words) $(if ($code -in '00000000', '00041301', '00041303') { 'Gray' } else { 'Yellow' }) if ($code -in '80070002', '80070003', '800710E0') { Say " Run the command again: it registers the task afresh. If this line comes back, email us (the address is on omnidx.net/studio/known-issues/)." 'White' } } $stops = @(Get-ServiceStops ((Get-Date).AddDays(-7))) if ($stops.Count) { Say (" Services that stopped unexpectedly in the last 7 days: {0}" -f ($stops -join ', ')) 'Yellow' Say " Windows starts most of them again by itself. If one keeps stopping and something stops working with it, run the command again with -NoGroup (the service hosts go back to how Windows splits them) and email us which service it was. omnidx.net/studio/known-issues/ lists what is known, with the address." 'White' } else { Say " No service has stopped unexpectedly in the last 7 days." } $ar = Join-Path $script:Root 'after-restart.txt' $arCount = 0 if (Test-Path $ar) { $l = @(Get-Content $ar -ErrorAction SilentlyContinue) | Select-Object -Last 1; if ($l) { Say (" After restart: {0}" -f $l) 'White'; if ($l -match 'after restart: (\d+) processes') { $arCount = [int]$Matches[1] } } } $arList = Join-Path $script:Root 'processes-after-restart.txt' if (Test-Path $arList) { $top = @(Get-Content $arList -ErrorAction SilentlyContinue | Select-Object -First 6 | ForEach-Object { ($_ -replace '\s{2,}', ' ').Trim() }); if ($top.Count) { Say (" Most of them: {0}. All of them: {1}" -f ($top -join '; '), $arList) } } # The newest run still in place (its record is not in undo\done), so an undone Extreme does not speak for the tune. $stamps = @($files | ForEach-Object { $_.BaseName -replace '^changes-', '' }) $sum = Get-ChildItem $script:Root -Filter 'summary-*.json' -ErrorAction SilentlyContinue | Where-Object { $stamps -contains ($_.BaseName -replace '^summary-', '') } | Sort-Object Name -Descending | Select-Object -First 1 if ($sum) { try { $s = Get-Content $sum.FullName -Raw | ConvertFrom-Json; Say (" Last run in place: {0} -> {1} processes, target about {2} after a restart" -f $s.before, $s.after, $s.target) # The card with the number that counts: before the tune, and after a restart. if ($arCount -and $s.before) { $mm = @{ cpu = ''; gpu = '' } $mj = Join-Path $script:Root ("machine-{0}.json" -f ($sum.BaseName -replace '^summary-', '')) try { if (Test-Path $mj) { $mo = Get-Content $mj -Raw | ConvertFrom-Json; $mm = @{ cpu = $mo.cpu; gpu = $mo.gpu } } } catch { } if (-not $mm.cpu) { try { $mm = @{ cpu = (Get-CimInstance Win32_Processor | Select-Object -First 1).Name.Trim(); gpu = (Get-CimInstance Win32_VideoController | Where-Object { $_.Name -notmatch 'Basic Display|Remote|Virtual' } | Select-Object -First 1).Name } } catch { } } Write-Card $mm ([int]$s.before) $arCount 'after a restart' if ($script:Card) { Say (" A card with the after-restart number, for posting: {0}" -f $script:Card) 'White' } } } catch { } } $bootNow = Get-BootSeconds if ($bootNow -ne $null) { $was = $null; if ($sum) { try { $was = (Get-Content $sum.FullName -Raw | ConvertFrom-Json).bootBefore } catch { } } Say (" Last measured start: {0} s{1}. Windows' own figure for the last full start; it updates a few minutes after each restart." -f $bootNow, $(if ($was) { ' (before the tune: {0} s)' -f $was } else { '' })) 'White' } Say (" Processes running now: {0}" -f (Get-ProcessCount)) 'White' # The answer to "why is the number up again": what runs now that did not run right after the tune. $afterFile = Get-ChildItem $script:Root -Filter 'processes-after-*.txt' -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1 if ($afterFile) { $then = @(Get-Content $afterFile.FullName -ErrorAction SilentlyContinue | ForEach-Object { ($_ -split '\s{2,}')[0].Trim() } | Where-Object { $_ }) $now = @(Get-Process -ErrorAction SilentlyContinue | ForEach-Object { $_.ProcessName } | Sort-Object -Unique) $new = @($now | Where-Object { $then -notcontains $_ }) if ($new.Count) { Say (" Running now, not running after the tune: {0}{1}. Whatever you opened since, or a launcher that came back; nothing here is a setting." -f (($new | Select-Object -First 12) -join ', '), $(if ($new.Count -gt 12) { " and $($new.Count - 12) more" } else { '' })) } else { Say " Nothing runs now that was not running right after the tune." } } } <# Everything support would ask for, as one zip on the desktop: the run logs, the PC as it was read, the numbers, the change records. No key (any key in a log is masked), no registry exports, none of your files. #> function New-SupportBundle { Head "Support bundle" if (-not (Test-Path $script:Root)) { Say " Nothing to bundle: the tune has not run on this PC." 'Yellow'; return } $desk = [Environment]::GetFolderPath('Desktop'); if (-not $desk) { $desk = $env:USERPROFILE } $zip = Join-Path $desk ("omnidx-support-{0}.zip" -f $script:Stamp) $stage = Join-Path $env:TEMP ("omnidx-support-{0}" -f $script:Stamp) New-Item -ItemType Directory -Path $stage -Force | Out-Null $n = 0 foreach ($pat in 'log-*.txt', 'machine-*.json', 'summary-*.json', 'report-*.txt', 'report-preview-*.txt', 'keep-log.txt', 'after-restart.txt', 'processes-after-restart.txt', 'README.txt') { foreach ($f in Get-ChildItem $script:Root -Filter $pat -File -ErrorAction SilentlyContinue) { try { (Get-Content $f.FullName -Raw -ErrorAction Stop) -replace '\b(TUNE|SQUAD)-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}\b', '$1-****-****-****-****' | Set-Content -Path (Join-Path $stage $f.Name) -Encoding UTF8; $n++ } catch { } } } foreach ($f in Get-ChildItem (Join-Path $script:Root 'undo') -Filter 'changes-*.json' -File -ErrorAction SilentlyContinue) { try { Copy-Item $f.FullName (Join-Path $stage $f.Name) -Force; $n++ } catch { } } if (-not $n) { Say " Nothing to bundle yet." 'Yellow'; Remove-Item $stage -Recurse -Force -ErrorAction SilentlyContinue; return } try { Compress-Archive -Path (Join-Path $stage '*') -DestinationPath $zip -Force -ErrorAction Stop Say (" {0} files zipped to {1}" -f $n, $zip) 'Green' Say " Attach it to your email. It holds the run logs, the PC as it was read, the numbers and the change records; no key, no registry exports, none of your files." try { Start-Process explorer.exe ("/select,`"{0}`"" -f $zip) } catch { } } catch { Say (" Could not write the zip ({0})." -f $_.Exception.Message) 'Red' } Remove-Item $stage -Recurse -Force -ErrorAction SilentlyContinue } <# Ten seconds to change your mind. #> function Invoke-Restart { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Invoke-Restart is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# The one file in C:\OmniDx a person opens first. #> $script:ReadMe = @' OmniDx Tune __VERSION__ - what is in this folder This folder is everything the tune left on this PC. Nothing here starts by itself except the one-shot after-restart count below, which deletes its task once it has run, and nothing here talks to the internet. report-.html / .txt what it found, what it changed, the numbers, the BIOS checklist, the per-game settings, and what another tool left switched off, with the way back. Open the .html in a browser. summary-.json the same numbers, for machines log-.txt a transcript of the run machine-.json the PC as it read it processes-before/after-.txt every process, grouped, before and after after-restart.txt the process count taken at the next sign-in by a one-shot task that then deletes itself processes-after-restart.txt every process still running at that sign-in, grouped keep-log.txt one line each time keep.ps1 put things back: what a Windows update had turned back on bios-.txt the BIOS checklist on its own undo\undo.ps1 puts everything back, every run, newest first. Administrator PowerShell: powershell -ExecutionPolicy Bypass -File C:\OmniDx\undo\undo.ps1 undo\changes-.json the record of one run: every value, service and task, with what it was before. Undo moves these to undo\done\ when it has used them. undo\keep.ps1 compares the records with the machine and sets again what an update turned back on. Nothing starts it: the OmniDx Hub's Put them back runs it, or administrator PowerShell: powershell -ExecutionPolicy Bypass -File C:\OmniDx\undo\keep.ps1 Add -Check to only look. backup\\ registry exports of the areas touched, the service list, and Discord / Spotify settings files, from before the run Status, any time (changes nothing): $env:OMNIDX_MODE='status'; irm omnidx.net/go.ps1 | iex Run it again, same key, same PC, free: irm omnidx.net/go.ps1 | iex The last ten runs are kept here; older logs and reports are removed on the next run. Undo records stay until undo uses them. '@ # --------------------------------------------------------------------------- # restore point and backups # --------------------------------------------------------------------------- function New-Safety { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and New-Safety is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # the cut: startup # --------------------------------------------------------------------------- $script:StartupKeep = @('SecurityHealth', 'Windows Security notification icon') + $Keep function Test-Keep([string]$name) { foreach ($k in $script:StartupKeep) { if ($k -and ($name -like "*$k*")) { return $true } } return $false } # An app the person chose to leave starting with Windows: its own "open at startup" # setting stays as it is too, or the app would take itself off the list at its next start. function Test-KeepApp([string]$app) { foreach ($k in $script:StartupKeep) { if ($k -and (($k -like "*$app*") -or ($app -like "*$k*"))) { return $true } } return $false } <# Everything that starts with Windows for the signed-in person: the Run keys, the Startup folders and the Store apps that register themselves separately. Each entry knows the switch that turns it off. #> <# The startup entries an earlier run (still in place, not undone) switched off: if one of them is on again, a person turned it back on in Task Manager, and a later run must not cut it again without being told to. #> function Get-CutStartup { $cut = @{} foreach ($f in Get-ChildItem (Join-Path $script:Root 'undo') -Filter 'changes-20*.json' -ErrorAction SilentlyContinue) { try { foreach ($c in @(Get-Content $f.FullName -Raw | ConvertFrom-Json | ForEach-Object { $_ })) { if ($c.type -eq 'reg' -and $c.path -like '*StartupApproved*') { $cut[("{0}|{1}" -f $c.path, $c.name).ToLower()] = $true } elseif ($c.type -eq 'reg' -and $c.path -like '*AppModel\SystemAppData*' -and $c.name -eq 'State') { $cut[("{0}|State" -f $c.path).ToLower()] = $true } } } catch { } } return $cut } function Get-StartupEntries { $hk = $script:HKCU $cut = Get-CutStartup $entries = @() $pairs = @( @{ run = "$hk\Software\Microsoft\Windows\CurrentVersion\Run"; ok = "$hk\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run" }, @{ run = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run'; ok = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run' }, @{ run = 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run'; ok = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run32' } ) foreach ($p in $pairs) { if (-not (Test-Path $p.run)) { continue } $item = Get-Item $p.run foreach ($name in $item.GetValueNames()) { if (-not $name) { continue } $state = $null; try { $state = (Get-ItemProperty -Path $p.ok -Name $name -ErrorAction Stop).$name } catch { } $on = -not ($state -and $state[0] -eq 3) $entries += @{ name = $name; label = $name; ok = $p.ok; value = $name; on = $on; kind = 'run'; wasCut = [bool]$cut[("{0}|{1}" -f $p.ok, $name).ToLower()] } } } $folders = @( @{ dir = (Join-Path $script:AppData 'Microsoft\Windows\Start Menu\Programs\Startup'); ok = "$hk\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder" }, @{ dir = (Join-Path $env:ProgramData 'Microsoft\Windows\Start Menu\Programs\StartUp'); ok = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\StartupFolder' } ) foreach ($f in $folders) { if (-not (Test-Path $f.dir)) { continue } foreach ($file in Get-ChildItem $f.dir -File -ErrorAction SilentlyContinue | Where-Object { $_.Name -ne 'desktop.ini' }) { $base = [IO.Path]::GetFileNameWithoutExtension($file.Name) $state = $null; try { $state = (Get-ItemProperty -Path $f.ok -Name $file.Name -ErrorAction Stop).($file.Name) } catch { } $on = -not ($state -and $state[0] -eq 3) $entries += @{ name = $base; label = ("{0} (Startup folder)" -f $base); ok = $f.ok; value = $file.Name; on = $on; kind = 'folder'; wasCut = [bool]$cut[("{0}|{1}" -f $f.ok, $file.Name).ToLower()] } } } $appBase = "$hk\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\SystemAppData" if (Test-Path $appBase) { foreach ($pkg in Get-ChildItem $appBase -ErrorAction SilentlyContinue) { foreach ($task in Get-ChildItem $pkg.PSPath -ErrorAction SilentlyContinue) { $state = (Get-ItemProperty $task.PSPath -Name State -ErrorAction SilentlyContinue).State if ($state -eq 2) { $label = ($pkg.PSChildName -split '_')[0] $entries += @{ name = $label; label = ("{0} (Store app)" -f $label); ok = $task.PSPath; value = 'State'; on = $true; kind = 'store'; wasCut = [bool]$cut[("{0}|State" -f $task.PSPath).ToLower()] } } } } } return @($entries) } function Cut-Startup { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-Startup is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # the cut: services, decided by what the machine has # --------------------------------------------------------------------------- # Off entirely: telemetry and the things a PC built for games never needs. $script:ServiceOff = @( @('DiagTrack', 'telemetry'), @('dmwappushservice', 'telemetry push'), @('diagnosticshub.standardcollector.service', 'diagnostics hub'), @('WerSvc', 'error reporting'), @('wercplsupport', 'error reporting'), @('PcaSvc', 'compatibility assistant'), @('MapsBroker', 'offline maps'), @('lfsvc', 'geolocation'), @('RetailDemo', 'retail demo'), @('RemoteRegistry', 'remote registry'), @('Fax', 'fax'), @('WMPNetworkSvc', 'media sharing'), @('TrkWks', 'link tracking'), @('WalletService', 'wallet'), @('wisvc', 'insider program'), @('DoSvc', 'delivery optimisation p2p'), @('SEMgrSvc', 'payments and NFC'), @('MessagingService', 'SMS'), @('PhoneSvc', 'Phone Link'), @('TapiSrv', 'telephony'), @('WpcMonSvc', 'parental controls'), @('SharedAccess', 'internet connection sharing'), @('CscService', 'offline files'), @('icssvc', 'mobile hotspot'), @('edgeupdate', 'Edge updater (runs on demand)'), @('edgeupdatem', 'Edge updater'), @('WbioSrvc', 'biometrics'), @('SysMain', 'superfetch'), @('WSearch', 'search indexing'), @('Spooler', 'print spooler'), @('bthserv', 'bluetooth'), @('BTAGService', 'bluetooth audio'), @('BthAvctpSvc', 'bluetooth audio'), @('WlanSvc', 'wi-fi'), @('TabletInputService', 'touch keyboard'), @('SensorService', 'sensors'), @('SensrSvc', 'sensors'), @('SensorDataService', 'sensors'), @('AJRouter', 'AllJoyn'), @('NcbService', 'network connection broker'), @('CDPSvc', 'connected devices'), @('CDPUserSvc', 'connected devices'), @('OneSyncSvc', 'mail/contacts sync'), @('PimIndexMaintenanceSvc', 'contacts index'), @('UnistoreSvc', 'user data storage'), @('UserDataSvc', 'user data'), @('XblAuthManager', 'xbox sign-in'), @('XblGameSave', 'xbox game save'), @('XboxNetApiSvc', 'xbox networking'), @('XboxGipSvc', 'xbox accessories'), @('RmSvc', 'radio management'), @('WwanSvc', 'mobile broadband'), @('SSDPSRV', 'ssdp discovery'), @('upnphost', 'upnp'), @('FrameServer', 'camera frame server'), @('perceptionsimulation', 'mixed reality'), @('spectrum', 'mixed reality'), @('DPS', 'diagnostic policy'), @('WdiServiceHost', 'diagnostics'), @('WdiSystemHost', 'diagnostics'), @('stisvc', 'scanner (WIA)'), @('SCardSvr', 'smart card'), @('ScDeviceEnum', 'smart card'), @('CertPropSvc', 'smart card'), @('WebClient', 'webdav'), @('lmhosts', 'netbios'), @('iphlpsvc', 'ipv6 tunnels'), @('TermService', 'remote desktop'), @('SessionEnv', 'remote desktop'), @('UmRdpService', 'remote desktop'), @('Themes', 'themes'), @('cbdhsvc', 'clipboard history'), @('WpnService', 'push notifications'), @('WpnUserService', 'push notifications'), @('p2psvc', 'peer networking'), @('p2pimsvc', 'peer networking identity'), @('PNRPsvc', 'peer name resolution'), @('PNRPAutoReg', 'peer name publication'), @('RemoteAccess', 'routing and remote access'), @('SNMPTRAP', 'SNMP traps'), @('SharedRealitySvc', 'mixed reality spatial data'), @('shpamsvc', 'shared PC accounts'), @('SmsRouter', 'SMS routing'), @('TroubleshootingSvc', 'recommended troubleshooting'), @('UevAgentService', 'user experience virtualisation'), @('workfolderssvc', 'Work Folders sync'), @('WFDSConMgrSvc', 'Wi-Fi Direct'), @('WiaRpc', 'still image (scanners)'), @('svsvc', 'spot verifier'), @('autotimesvc', 'cellular time'), @('EntAppSvc', 'enterprise app management'), @('fhsvc', 'File History'), @('DusmSvc', 'data usage'), @('vmicguestinterface', 'Hyper-V guest'), @('vmicheartbeat', 'Hyper-V guest'), @('vmickvpexchange', 'Hyper-V guest'), @('vmicrdv', 'Hyper-V guest'), @('vmicshutdown', 'Hyper-V guest'), @('vmictimesync', 'Hyper-V guest'), @('vmicvmsession', 'Hyper-V guest'), @('vmicvss', 'Hyper-V guest'), # Helpers that come with some boards' and laptops' drivers and do nothing for a game (OmniDx Edition's lean stage # has cut them since 1.1): Nahimic's audio effects are off outright, known to crash games by injecting into them. @('NahimicService', 'Nahimic audio effects (known to crash games by injecting into them)'), @('AUEPLauncher', "AMD's user experience program"), @('Killer Analytics Service', 'Killer network analytics'), @('jhi_service', 'Intel DAL host (starts when an app asks)'), @('LMS', 'Intel ME local management (starts when an app asks)'), @('WMIRegistrationService', 'Intel ME WMI provider (starts when an app asks)'), @('igccservice', 'Intel Graphics Command Center helper (starts with the app)'), # Windows' AI runtime (Recall and Click to Do are off by policy below), the health-and-experiences helper and the # compatibility inventory: a service host each at rest (OmniDx Edition has cut them since 1.3). @('WSAIFabricSvc', "Windows' AI runtime (Recall and Click to Do are off)"), @('whesvc', 'health and optimized experiences helper (starts when asked)'), @('InventorySvc', 'compatibility inventory (starts when asked)') ) # Extreme only: the services a person might notice going. Manual unless the # list below says off; -Extreme sets them, the standard tune leaves them. $script:ExtremeServices = @( @('CaptureService', 'screen capture for apps'), @('NPSMSvc', 'media controls (Now Playing)'), @('PrintWorkflowUserSvc', 'print workflow (kept with a printer)'), @('AarSvc', 'voice activation'), @('BcastDVRUserService', 'Game DVR broadcasting'), @('cloudidsvc', 'cloud identity (kept on a domain)'), @('DevicesFlowUserSvc', 'connect-to-device flow'), @('DeviceAssociationBrokerSvc', 'device pairing broker'), @('DevQueryBroker', 'device query broker'), @('PenService', 'pen input (kept with touch)'), @('SysMain', 'superfetch, off on an SSD'), @('WSearch', 'search indexing, off'), @('WpnService', 'notifications, off'), @('WpnUserService', 'notifications, off'), @('cbdhsvc', 'clipboard history, off'), @('CDPSvc', 'nearby sharing, off'), @('CDPUserSvc', 'nearby sharing, off'), @('TabletInputService', 'touch keyboard (kept with touch)'), # (SgrmBroker, System Guard's attestation broker, was here to 1.98.2: Windows guards it, so it never changed, and it # is part of Windows Defender System Guard, which nothing of OmniDx touches.) @('SupportAssistAgent', 'Dell SupportAssist agent (support for the PC maker), off'), @('DDVDataCollector', 'Dell Data Vault (PC maker telemetry), off'), @('DDVRulesProcessor', 'Dell Data Vault (PC maker telemetry), off'), @('DDVCollectorSvcApi', 'Dell Data Vault (PC maker telemetry), off'), @('HpTouchpointAnalyticsService', 'HP Touchpoint Analytics (PC maker telemetry), off'), @('HPAppHelperCap', 'HP app helper (kept on a laptop)'), @('HPDiagsCap', 'HP diagnostics (kept on a laptop)'), @('HPNetworkCap', 'HP network helper (kept on a laptop)'), @('HPSysInfoCap', 'HP system information (kept on a laptop)'), @('LenovoVantageService', 'Lenovo Vantage (kept on a laptop)'), @('ImControllerService', 'Lenovo System Interface Foundation (kept on a laptop)'), @('AsusAppService', 'ASUS app service (kept on a laptop)'), @('ArmouryCrateService', 'Armoury Crate (kept on a laptop)'), @('ASUSOptimization', 'ASUS optimization (kept on a laptop)'), # Realtek's audio helper, two processes from the driver's own package: sound and the jacks work without it; the # Realtek Audio Console and its plug-in pop-up wait until it is back (undo, or Manual to Automatic in services.msc). @('RtkAudioUniversalService', 'Realtek Audio Console helper (sound keeps working; kept on a laptop)') ) # The PC maker's own apps, in Extreme. Support agents and promotions go on every PC; the hubs that run the fans, the # battery limit and the hotkeys (Vantage, MyASUS, Armoury Crate, myHP, My Dell, Acer Care Center) go only on a desktop. $script:OemApps = @( @('*SupportAssist*', 'Dell SupportAssist', 'any'), @('DellInc.DellDigitalDelivery', 'Dell Digital Delivery', 'any'), @('DellInc.PartnerPromo', 'Dell partner offers', 'any'), @('DellInc.DellCustomerConnect', 'Dell Customer Connect', 'any'), @('DellInc.MyDell', 'My Dell', 'desktop'), @('AD2F1837.HPPrivacySettings', 'HP Privacy Settings', 'any'), @('AD2F1837.HPJumpStarts', 'HP JumpStarts', 'any'), @('AD2F1837.HPSupportAssistant', 'HP Support Assistant', 'any'), @('AD2F1837.myHP', 'myHP', 'desktop'), @('LenovoCorporation.LenovoID', 'Lenovo ID', 'any'), @('E046963F.LenovoCompanion', 'Lenovo Vantage', 'desktop'), @('B9ECED6F.MyASUS', 'MyASUS', 'desktop'), @('B9ECED6F.ArmouryCrate', 'Armoury Crate', 'desktop'), @('AcerIncorporated.AcerCollectionS', 'Acer Collection', 'any'), @('AcerIncorporated.UserExperienceImprovementProgram', 'Acer user experience program', 'any'), @('AcerIncorporated.AcerCareCenterS', 'Acer Care Center', 'desktop') ) $script:ExtremeOff = @('SysMain', 'WSearch', 'WpnService', 'WpnUserService', 'cbdhsvc', 'CDPSvc', 'CDPUserSvc', 'BcastDVRUserService', 'AarSvc', 'SupportAssistAgent', 'DDVDataCollector', 'DDVRulesProcessor', 'DDVCollectorSvcApi', 'HpTouchpointAnalyticsService') # The ones this machine actually uses go to manual instead of off, or stay. $script:ManualOnly = @('jhi_service', 'LMS', 'WMIRegistrationService', 'igccservice', 'SysMain', 'WSearch', 'edgeupdate', 'edgeupdatem', 'DPS', 'WdiServiceHost', 'WdiSystemHost', 'iphlpsvc', 'SSDPSRV', 'upnphost', 'NcbService', 'CDPSvc', 'CDPUserSvc', 'OneSyncSvc', 'PimIndexMaintenanceSvc', 'UnistoreSvc', 'UserDataSvc', 'XblAuthManager', 'XblGameSave', 'XboxNetApiSvc', 'XboxGipSvc', 'RmSvc', 'FrameServer', 'stisvc', 'WebClient', 'lmhosts', 'TermService', 'SessionEnv', 'UmRdpService', 'WpnService', 'WpnUserService', 'cbdhsvc', 'SensorService', 'SensrSvc', 'SensorDataService', 'TabletInputService', 'BTAGService', 'BthAvctpSvc', 'DoSvc', 'WFDSConMgrSvc', 'WiaRpc', 'svsvc', 'autotimesvc', 'EntAppSvc', 'fhsvc', 'DusmSvc', 'whesvc', 'InventorySvc') # Other software's updaters: a service of their own checks for updates all day. On demand they start when their # program asks (Chrome, Edge, Acrobat, Dropbox and the rest still update themselves when they are opened). Found by # the folder their service runs from, so a renamed or versioned service (GoogleUpdaterService140.0...) is still found. $script:UpdaterPaths = @('\google\googleupdater\', '\google\update\', '\microsoft\edgeupdate\', '\bravesoftware\update\', '\mozilla maintenance service\', '\adobe\arm\', '\common files\adobe\arm\', '\dropbox\update\', '\intel\driver and support assistant\', '\intel\sur\', '\intel driver update utility\', '\java\java update\', '\common files\java\java update\') # The hardware and helper suites a home PC runs on top of Windows, by install folder: named in the breakdown of what is # still running, with their process and thread counts. OmniDx Hub > Running (the Edition) has a switch for each. $script:SuitePaths = [ordered]@{ 'Corsair iCUE' = '\corsair\'; 'Logitech G HUB' = '\lghub\'; 'Logi Options+' = '\logioptionsplus\'; 'Razer Synapse' = '\razer\|\razer chroma sdk\' 'SteelSeries GG' = '\steelseries\'; 'ASUS Armoury Crate' = '\asus\|\armourydevice\|\asusm\'; 'MSI Center' = '\msi\|\micro-star\'; 'Gigabyte Control Center' = '\gigabyte\' 'NZXT CAM' = '\nzxt cam\'; 'Lian Li L-Connect' = '\lian-li\|\l-connect'; 'ASRock Polychrome' = '\asrock\'; 'SignalRGB' = '\vortxengine\'; 'OpenRGB' = '\openrgb' 'Wallpaper Engine' = '\wallpaper_engine\'; 'Overwolf' = '\overwolf\'; 'Killer Control Center' = '\killer networking\|\rivet networks\' 'NVIDIA app extras' = '\nvidia corporation\nvidia app\|\nvidia corporation\nvcontainer\|\nvidia corporation\nvidia geforce experience\|\nvidia corporation\shadowplay\|\nvidia corporation\frameviewsdk\' 'AMD Software extras' = '\amd\cnext\'; 'Adobe Creative Cloud' = '\adobe\creative cloud|\common files\adobe\|\adobe\adobe desktop common\'; 'EA app background service' = '\electronic arts\ea desktop\' 'HP OMEN Gaming Hub' = '\ad2f1837.omen|\hp\omen'; 'Alienware Command Center' = '\alienware\|\dellinc.alienwarecommandcenter'; 'Lenovo Vantage and Legion' = '\lenovo\|\e046963f.lenovo' 'Acer NitroSense and PredatorSense' = '\acer\|\acerincorporated.'; 'Glorious CORE' = '\glorious core\'; 'Turtle Beach and ROCCAT' = '\turtle beach\|\roccat\' 'Cooler Master MasterPlus' = '\cooler master\|\coolermaster\'; 'Thermaltake TT RGB Plus' = '\thermaltake\'; 'Elgato' = '\elgato\'; 'EVGA Precision X1' = '\evga\' } function Cut-Services($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-Services is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # the cut: scheduled tasks and preinstalled apps # --------------------------------------------------------------------------- $script:TaskList = @( @('\Microsoft\Windows\Application Experience\', 'Microsoft Compatibility Appraiser'), @('\Microsoft\Windows\Application Experience\', 'ProgramDataUpdater'), @('\Microsoft\Windows\Application Experience\', 'StartupAppTask'), @('\Microsoft\Windows\Application Experience\', 'PcaPatchDbTask'), @('\Microsoft\Windows\Customer Experience Improvement Program\', 'Consolidator'), @('\Microsoft\Windows\Customer Experience Improvement Program\', 'UsbCeip'), @('\Microsoft\Windows\Customer Experience Improvement Program\', 'KernelCeipTask'), @('\Microsoft\Windows\DiskDiagnostic\', 'Microsoft-Windows-DiskDiagnosticDataCollector'), @('\Microsoft\Windows\Feedback\Siuf\', 'DmClient'), @('\Microsoft\Windows\Feedback\Siuf\', 'DmClientOnScenarioDownload'), @('\Microsoft\Windows\Windows Error Reporting\', 'QueueReporting'), @('\Microsoft\Windows\Maps\', 'MapsUpdateTask'), @('\Microsoft\Windows\Maps\', 'MapsToastTask'), @('\Microsoft\Windows\Autochk\', 'Proxy'), @('\Microsoft\Windows\CloudExperienceHost\', 'CreateObjectTask'), @('\Microsoft\Windows\Shell\', 'FamilySafetyMonitor'), @('\Microsoft\Windows\Shell\', 'FamilySafetyRefreshTask'), @('\Microsoft\Windows\Device Information\', 'Device'), @('\Microsoft\Windows\Device Information\', 'Device User'), @('\Microsoft\Windows\PushToInstall\', 'LoginCheck'), @('\Microsoft\Windows\Power Efficiency Diagnostics\', 'AnalyzeSystem'), @('\Microsoft\XblGameSave\', 'XblGameSaveTask'), @('\Microsoft\Windows\Speech\', 'SpeechModelDownloadTask'), @('\Microsoft\Windows\Retail Demo\', 'CleanupOfflineContent'), @('\Microsoft\Windows\NetTrace\', 'GatherNetworkInfo'), @('\Microsoft\Windows\Application Experience\', 'MareBackup'), @('\Microsoft\Windows\Location\', 'Notifications'), @('\Microsoft\Windows\Location\', 'WindowsActionDialog'), @('\Microsoft\Office\', 'OfficeTelemetryAgentLogOn'), @('\Microsoft\Office\', 'OfficeTelemetryAgentFallBack'), @('\Microsoft\Windows\Diagnosis\', 'Scheduled'), @('\Microsoft\Windows\WwanSvc\', 'OobeDiscovery'), # Background work a gaming PC never asked for: the compatibility appraisers, memory and disk-usage # diagnostics, data-usage bookkeeping, the flighting reporters, SQM, offline files, storage tiers, # remote assistance, print provisioning, media-library sharing, the indexer's maintenance, roaming # profile uploads, the shared-PC cleanup, the Store's remote install and the end-of-support nag. @('\Microsoft\Windows\Application Experience\', 'PcaWallpaperAppDetect'), @('\Microsoft\Windows\Application Experience\', 'Microsoft Compatibility Appraiser Exp'), @('\Microsoft\Windows\Diagnosis\', 'RecommendedTroubleshootingScanner'), @('\Microsoft\Windows\DiskFootprint\', 'Diagnostics'), @('\Microsoft\Windows\DUSM\', 'dusmtask'), @('\Microsoft\Windows\Flighting\FeatureConfig\', 'UsageDataFlushing'), @('\Microsoft\Windows\Flighting\FeatureConfig\', 'UsageDataReporting'), @('\Microsoft\Windows\HelloFace\', 'FODCleanupTask'), @('\Microsoft\Windows\MemoryDiagnostic\', 'ProcessMemoryDiagnosticEvents'), @('\Microsoft\Windows\MemoryDiagnostic\', 'RunFullMemoryDiagnostic'), @('\Microsoft\Windows\Offline Files\', 'Background Synchronization'), @('\Microsoft\Windows\Offline Files\', 'Logon Synchronization'), @('\Microsoft\Windows\PI\', 'Sqm-Tasks'), @('\Microsoft\Windows\Printing\', 'EduPrintProv'), @('\Microsoft\Windows\PushToInstall\', 'Registration'), @('\Microsoft\Windows\RemoteAssistance\', 'RemoteAssistanceTask'), @('\Microsoft\Windows\SharedPC\', 'Account Cleanup'), @('\Microsoft\Windows\Shell\', 'IndexerAutomaticMaintenance'), @('\Microsoft\Windows\Storage Tiers Management\', 'Storage Tiers Management Initialization'), @('\Microsoft\Windows\UPnP\', 'UPnPHostConfig'), @('\Microsoft\Windows\User Profile Service\', 'HiveUploadTask'), @('\Microsoft\Windows\WDI\', 'ResolutionHost'), @('\Microsoft\Windows\Windows Media Sharing\', 'UpdateLibrary'), @('\Microsoft\Windows\Work Folders\', 'Work Folders Logon Synchronization'), @('\Microsoft\Windows\Work Folders\', 'Work Folders Maintenance Work'), @('\Microsoft\Windows\Setup\', 'EOSNotify'), @('\Microsoft\Windows\Setup\', 'EOSNotify2'), # The old Internet Explorer engine's cache upkeep: a task host of its own at every sign-in (the Edition film at rest). @('\Microsoft\Windows\Wininet\', 'CacheTask') ) <# Every scheduled task once, then lookups: asking for them one by one took thirteen seconds on the build machine. #> function Get-TaskIndex { $idx = @{} foreach ($t in Get-ScheduledTask -ErrorAction SilentlyContinue) { $idx[($t.TaskPath + $t.TaskName).ToLower()] = $t } return $idx } function Cut-Tasks { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-Tasks is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } $script:JunkApps = @( 'Microsoft.YourPhone', 'MicrosoftWindows.CrossDevice', 'Microsoft.549981C3F5F10', 'Microsoft.WindowsFeedbackHub', 'Microsoft.GetHelp', 'Microsoft.Getstarted', 'Microsoft.WindowsMaps', 'Microsoft.MicrosoftSolitaireCollection', 'Microsoft.MixedReality.Portal', 'Microsoft.Microsoft3DViewer', 'Microsoft.People', 'Microsoft.SkypeApp', 'MicrosoftTeams', 'MSTeams', 'Clipchamp.Clipchamp', 'Microsoft.BingNews', 'Microsoft.BingWeather', 'Microsoft.BingSearch', 'Microsoft.Todos', 'Microsoft.MicrosoftOfficeHub', 'Microsoft.Office.OneNote', 'Microsoft.PowerAutomateDesktop', 'MicrosoftCorporationII.MicrosoftFamily', 'Microsoft.Copilot', 'MicrosoftWindows.Client.WebExperience', 'Microsoft.Windows.DevHome', 'Microsoft.Windows.Ai.Copilot.Provider', 'Microsoft.WindowsCommunicationsApps', 'Microsoft.Messaging', 'Microsoft.OneConnect', 'Microsoft.Print3D', 'Microsoft.Wallet', 'Microsoft.WindowsAlarms', 'Microsoft.MicrosoftStickyNotes', 'Microsoft.Advertising.Xaml', 'MicrosoftCorporationII.QuickAssist', 'Microsoft.OutlookForWindows', 'Microsoft.Edge.GameAssist', 'Microsoft.WidgetsPlatformRuntime', 'Microsoft.MicrosoftJournal', 'Microsoft.Whiteboard', 'Microsoft.BingTranslator', 'Microsoft.BingFinance', 'Microsoft.BingSports', 'Microsoft.News', 'Microsoft.MicrosoftPowerBIForWindows', 'Microsoft.NetworkSpeedTest', 'Microsoft.Office.Sway', 'Microsoft.WindowsReadingList', 'Microsoft.3DBuilder', 'Microsoft.Microsoft3DViewer', 'Microsoft.MicrosoftJigsaw', 'Microsoft.MicrosoftMahjong', '*Disney*', '*TikTok*', '*Instagram*', '*Facebook*', '*CandyCrush*', '*king.com*', '*Netflix*', '*Twitter*', '*Amazon*', '*Hulu*', '*Dolby*', '*Prime*', '*LinkedIn*', '*McAfee*', '*Norton*', '*Booking*', '*Duolingo*', '*Fitbit*', '*Flipboard*', '*HiddenCity*', '*Hearts*', '*Plex*', '*Roblox*Store*', '*Sway*', '*Wunderlist*', '*ESPN*', '*BubbleWitch*', '*MarchofEmpires*', '*RoyalRevolt*', '*Speed Test*', '*Sidia*', '*WhatsApp*Stub*', '*ACGMediaPlayer*', '*ActiproSoftware*', '*AdobePhotoshopExpress*', '*Asphalt*', '*AutodeskSketchBook*', '*CaesarsSlots*', '*COOKINGFEVER*', '*CyberLink*', '*DrawboardPDF*', '*EclipseManager*', '*FarmVille*', '*Keeper*', '*PandoraMedia*', '*PhototasticCollage*', '*PicsArt*', '*PolarrPhoto*', '*Shazam*', '*SlingTV*', '*TuneInRadio*', '*Viber*', '*WinZipUniversal*', '*XING*', '*Solitaire*', '*Pinterest*', '*Messenger*', '*Spotify*Stub*', '*ExpressVPN*', '*Simplenote*', '*Hidden*Object*' ) <# The rest of the debloat: what Windows and the PC maker put on the disk that nobody asked for. Each list says what a thing is, so the report and the site can say so too. Undo puts every one of these back except the cleared caches, which were junk. #> $script:Capabilities = @( @('App.StepsRecorder', 'Steps Recorder (screenshots every click, retired by Microsoft)'), @('Media.WindowsMediaPlayer', 'the 2009 Windows Media Player (the Store one stays)'), @('Microsoft.Windows.WordPad', 'WordPad (retired by Microsoft)'), @('MathRecognizer', 'Math Input Panel'), @('Browser.InternetExplorer', 'Internet Explorer 11'), @('Print.Fax.Scan', 'Windows Fax and Scan (kept when a printer is installed)'), @('Hello.Face', 'Windows Hello face recognition (kept when a Hello camera or reader is present)') ) # The exact package ids behind the list above. Asking DISM for a name it knows # takes a second or two; asking it to list every capability on the PC took a # minute and a half on the build machine. Hello.Face carries a build number, so # the ones that have shipped are all tried. $script:CapabilityIds = @{ 'App.StepsRecorder' = @('App.StepsRecorder~~~~0.0.1.0') 'Media.WindowsMediaPlayer' = @('Media.WindowsMediaPlayer~~~~0.0.12.0') 'Microsoft.Windows.WordPad' = @('Microsoft.Windows.WordPad~~~~0.0.1.0') 'MathRecognizer' = @('MathRecognizer~~~~0.0.1.0') 'Browser.InternetExplorer' = @('Browser.InternetExplorer~~~~0.0.11.0') 'Print.Fax.Scan' = @('Print.Fax.Scan~~~~0.0.1.0') 'Hello.Face' = @('Hello.Face.20134~~~~0.0.1.0', 'Hello.Face.18967~~~~0.0.1.0', 'Hello.Face.17658~~~~0.0.1.0') } # The component store's record of each capability, by package name. A name verified on the build machine # (the check's discovery step, 1.62.0) decides by state without DISM; a hint says whether the store mentions # the piece at all, and a piece it never mentions is not on the PC. Anything else is asked of DISM by name. # Steps Recorder, Math Input Panel, Internet Explorer and the Server name of the old Media Player were verified # by the check on the build machine; the client Media Player name, WordPad and Fax and Scan are the names the # feature-on-demand packages ship under (build 17763 onward). A name that matches nothing goes to DISM. $script:CapabilityPackages = @{ 'App.StepsRecorder' = @('Microsoft-Windows-StepsRecorder-Package~31bf3856ad364e35~amd64~~') 'Media.WindowsMediaPlayer' = @('Microsoft-Windows-MediaPlayer-Opt-Package~31bf3856ad364e35~amd64~~', 'Microsoft-Windows-MediaPlayer-Package~31bf3856ad364e35~amd64~~') 'Microsoft.Windows.WordPad' = @('Microsoft-Windows-WordPad-FoD-Package~31bf3856ad364e35~amd64~~') 'MathRecognizer' = @('Microsoft-Windows-TabletPCMath-Package~31bf3856ad364e35~amd64~~') 'Browser.InternetExplorer' = @('Microsoft-Windows-InternetExplorer-Optional-Package~31bf3856ad364e35~amd64~~') 'Print.Fax.Scan' = @('Microsoft-Windows-Printing-WFS-FoD-Package~31bf3856ad364e35~amd64~~') } # Hello Face ships as Microsoft-Windows-Hello-Face-Package, but its capability id carries a build number the # package name does not, so when the store mentions it DISM is asked which id is installed. $script:CapabilityHints = @{ 'App.StepsRecorder' = 'StepsRecorder' 'Media.WindowsMediaPlayer' = 'MediaPlayer' 'Microsoft.Windows.WordPad' = 'WordPad' 'MathRecognizer' = 'Math|TabletPC' 'Browser.InternetExplorer' = 'InternetExplorer-Optional' 'Print.Fax.Scan' = 'WFS|Fax-Client' 'Hello.Face' = 'Hello' } $script:Features = @( @('MicrosoftWindowsPowerShellV2Root', 'the PowerShell 2.0 engine: old, bypasses modern security logging, nothing needs it'), @('MicrosoftWindowsPowerShellV2', 'the PowerShell 2.0 engine'), @('Printing-XPSServices-Features', 'XPS printing and viewer'), @('WorkFolders-Client', 'Work Folders (corporate sync)'), @('WindowsMediaPlayer', 'the 2009 Windows Media Player'), @('Internet-Explorer-Optional-amd64', 'Internet Explorer 11') ) function Cut-Apps($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-Apps is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # debloat: gone, and kept gone # --------------------------------------------------------------------------- function Get-DebloatPlan($m) { # What is actually on this PC from the two lists above, with the keep rules applied. $caps = @(); $feats = @() $dismLog = Join-Path $env:TEMP 'omnidx-dism.log' # DISM is asked only where nothing cheaper can answer. Measured on the build machine (the check's discovery # step, 1.62.0): every name asked of DISM costs half a second warm and a second cold, and the free look asked # fifteen. The component store's own record in the registry lists all 7,844 packages in under a second and # says which are installed (state 112; an older version of the same package sits at 64, a removed one at 5 # and then 0), and the optional-feature class answers every feature name at once in one to four seconds. # A listing through DISM itself is the one thing never done: 77 to 90 seconds (1.44.0 to 1.47.0). $pk = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\Packages' $pkgs = $null; try { $pkgs = @(Get-ChildItem -Path $pk -Name -ErrorAction Stop) } catch { $pkgs = $null } foreach ($c in $script:Capabilities) { if ($c[0] -eq 'Print.Fax.Scan' -and $m.printers) { continue } if ($c[0] -eq 'Hello.Face' -and $m.biometric) { continue } $decided = $false if ($pkgs -ne $null) { # Nothing in the component store mentions the piece: it is not on this PC, and DISM need not be asked. $cands = @($pkgs | Where-Object { $_ -match $script:CapabilityHints[$c[0]] }) if (-not $cands.Count) { continue } # A known package name decides by its state; any other name is left to DISM. $prefixes = @(); if ($script:CapabilityPackages.ContainsKey($c[0])) { $prefixes = @($script:CapabilityPackages[$c[0]]) } if ($prefixes.Count) { $mine = @($cands | Where-Object { $n = $_; @($prefixes | Where-Object { $n.StartsWith($_, [System.StringComparison]::OrdinalIgnoreCase) }).Count -gt 0 }) if ($mine.Count) { $decided = $true foreach ($n in $mine) { $state = -1; try { $state = [int](Get-Item -Path (Join-Path $pk $n) -ErrorAction Stop).GetValue('CurrentState') } catch { } if ($state -eq 112) { $caps += @{ name = $script:CapabilityIds[$c[0]][0]; what = $c[1] }; break } } } } } if ($decided) { continue } foreach ($id in $script:CapabilityIds[$c[0]]) { try { $hit = Get-WindowsCapability -Online -Name $id -LogPath $dismLog -ErrorAction Stop; if ($hit -and $hit.State -eq 'Installed') { $caps += @{ name = $hit.Name; what = $c[1] }; break } } catch { } } } # Every optional feature's state in one read (1 is enabled); DISM by name is the fallback when the class fails. # Its cost is the servicing stack's cold start, six to ten seconds on the build machine, and that cost stays # here: read in a runspace alongside the machine read (1.65.0) it doubled the read's own WMI laps and the whole # look came out five seconds slower on two samples. $featState = $null try { $featState = @{}; foreach ($o in @(Get-CimInstance -ClassName Win32_OptionalFeature -OperationTimeoutSec 60 -ErrorAction Stop)) { $featState["$($o.Name)"] = [int]$o.InstallState } } catch { $featState = $null } foreach ($f in $script:Features) { # The old Media Player is a capability and a feature on newer builds; removing the capability takes the feature with it, so it is one item, not two. if ($f[0] -eq 'WindowsMediaPlayer' -and ($caps | Where-Object { $_.name -like 'Media.WindowsMediaPlayer*' })) { continue } if ($featState -ne $null) { if ($featState.ContainsKey($f[0]) -and $featState[$f[0]] -eq 1) { $feats += @{ name = $f[0]; what = $f[1] } }; continue } # A name this build does not have is an error here, and simply not on the list. try { $hit = Get-WindowsOptionalFeature -Online -FeatureName $f[0] -LogPath $dismLog -ErrorAction Stop; if ($hit -and $hit.State -eq 'Enabled') { $feats += @{ name = $hit.FeatureName; what = $f[1] } } } catch { } } $one = Get-OneDriveState @{ caps = $caps; feats = $feats; oneSignedIn = $one.signedIn; oneInstalled = $one.installed; oneSetup = $one.setup } } <# OneDrive for this account: its installer, whether it is installed, whether anyone is signed in to it. #> function Get-OneDriveState { $hk = $script:HKCU $signedIn = (Test-Path "$hk\Software\Microsoft\OneDrive\Accounts\Personal") -or (Test-Path "$hk\Software\Microsoft\OneDrive\Accounts\Business1") $setup = @("$env:SystemRoot\System32\OneDriveSetup.exe", "$env:SystemRoot\SysWOW64\OneDriveSetup.exe", (Join-Path $script:LocalAppData 'Microsoft\OneDrive\OneDriveSetup.exe')) | Where-Object { Test-Path $_ } | Select-Object -First 1 $installed = [bool]($setup -and ((Get-Process OneDrive -ErrorAction SilentlyContinue) -or (Test-Path (Join-Path $script:LocalAppData 'Microsoft\OneDrive\OneDrive.exe')) -or (Test-Path "$env:ProgramFiles\Microsoft OneDrive\OneDrive.exe"))) @{ signedIn = $signedIn; installed = $installed; setup = $setup } } function Debloat($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Debloat is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Caches and temp files that Windows never clears itself. Not undoable, because none of it is anything: downloaded update installers already applied, peer-to-peer update chunks, temp files older than a day. #> function Clear-Junk { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Clear-Junk is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # the cut: telemetry, background, the shell # --------------------------------------------------------------------------- function Cut-Telemetry($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-Telemetry is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # performance: scheduler, visuals, input, timers, memory, disk, gpu # --------------------------------------------------------------------------- function Tune-System($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Tune-System is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # bcdedit's Yes in the languages Windows ships it in (the element names stay English); ASCII, with \u escapes. $script:BcdYes = '(Yes|Ja|Oui|S\u00ed|Si|Sim|Tak|Evet|Ano|Igen|Kyll\u00e4|\u0414\u0430|\u662f|\u306f\u3044|\uC608|\u039d\u03b1\u03b9)' <# The boot menu's wait (1.104.0). With two or more entries in it (an old Windows left behind by an upgrade or a cloned drive, a second drive's Windows) Windows shows the menu at every start and waits, 30 seconds by default, for a choice nobody makes. Three seconds keeps the menu and the choice and gives the rest back; with one entry there is no menu to wait on and nothing changes. bcdedit's element names are the same in every Windows language. Undo puts the old wait back. #> function Set-BootMenu { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-BootMenu is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Defender stays, and its scheduled scans stop taking half the CPU: the cap is for scans only, never real-time protection, and only while Defender is the antivirus in charge. Undo puts the old figure back. #> function Limit-Defender($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Limit-Defender is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # the OmniDx power plan # --------------------------------------------------------------------------- function New-PowerPlan($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and New-PowerPlan is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # Hibernation off frees the hiberfile and ends Fast Startup for good - on a desktop. function Set-HibernateOff($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-HibernateOff is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # network: for the game's packets, not for downloads # --------------------------------------------------------------------------- function Tune-Network($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Tune-Network is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # the apps: Discord, Spotify, browsers # --------------------------------------------------------------------------- function Set-JsonFile([string]$path, [hashtable]$values) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-JsonFile is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Spotify in fewer processes (Extreme): its graphics part and its network part run inside its main process, from the switches its own shortcuts start it with. Measured on the test PC (the film rig's apps probe, 30 September 2026): six processes to four and about 60 MB less, the app the same on screen. The desktop app only (the Microsoft Store one takes no switches), started from its Start menu, taskbar or desktop shortcut. The cost: a graphics driver that resets closes Spotify instead of only its graphics part. Each shortcut is backed up and undo puts it back; a later run without Extreme takes the switches out again. #> $script:LeanSwitches = '--in-process-gpu --enable-features=NetworkServiceInProcess2' function Set-SpotifyLean([bool]$on) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-SpotifyLean is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } function Tune-Apps($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Tune-Apps is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # game profiles # --------------------------------------------------------------------------- $script:Games = @( @{ name = 'Fortnite'; exes = @('FortniteClient-Win64-Shipping.exe'); notes = @('Rendering mode Performance (lower graphical fidelity), in Video settings, is the biggest single gain on any GPU.', '3D resolution 100%, view distance far, everything else low or off; meshes low is what pros run.', 'Frame rate limit unlimited (or above your refresh rate) with NVIDIA Reflex On + Boost; with G-SYNC or FreeSync, a few frames under your refresh rate.', 'DirectX 12 only if Performance mode stutters for you; otherwise leave it.') }, @{ name = 'VALORANT'; exes = @('VALORANT-Win64-Shipping.exe', 'VALORANT.exe'); notes = @('Vanguard (vgc, vgk) is deliberately untouched - VALORANT will not start without it.', 'On Windows 11 it also needs Secure Boot and TPM 2.0 on; the BIOS checklist covers both.', 'Multithreaded rendering on, raw input buffer on, Nvidia Reflex on + boost, limit FPS off, V-Sync off.', 'Material, texture, detail and UI quality low; anti-aliasing MSAA 2x or none.') }, @{ name = 'Counter-Strike 2'; exes = @('cs2.exe'); notes = @('Launch options in Steam: -novid -nojoy (the tune already starts it at high priority). Leave out -allow_third_party_software: it turns Trusted Mode off, which can cost Trust Factor.', 'Boost Player Contrast on, Wait for Vertical Sync off, NVIDIA Reflex Enabled + Boost, FidelityFX Super Resolution off, shader detail low, MSAA 2x or 4x.', 'Set the max FPS in-game (fps_max) to 0 or just above your refresh rate for stable frame times.') }, @{ name = 'Marvel Rivals'; exes = @('Marvel-Win64-Shipping.exe', 'MarvelRivals_Launcher.exe'); notes = @('Graphics quality Low, then Model detail Low, shadows Low, post-processing Low: that is where the frames are.', 'Lumen (global illumination) off if the game offers it; Frame Generation only if you are GPU-bound.', 'Reflex / Anti-Lag 2 on. Limit FPS to your refresh rate to keep 1% lows steady.') }, @{ name = 'Apex Legends'; exes = @('r5apex.exe', 'r5apex_dx12.exe'); notes = @('Launch options: +fps_max 0 -novid -high', 'Texture streaming budget as high as your VRAM allows (it is a quality setting that costs nothing), everything else low.', 'V-Sync off, adaptive resolution FPS target 0, anti-aliasing TSAA or none.') }, @{ name = 'Call of Duty (Warzone / MW)'; exes = @('cod.exe', 'ModernWarfare.exe', 'Warzone.exe'); notes = @('Rendering resolution 100%, Nvidia Reflex on + boost, V-Sync off, texture resolution normal, shadows low, particle quality low.', 'On-demand texture streaming off if your connection stutters mid-match.') }, @{ name = 'Overwatch 2'; exes = @('Overwatch.exe'); notes = @('Render scale 100%, texture quality high (free), everything else low, Reflex enabled + boost, reduce buffering on if you play below 144 fps.') }, @{ name = 'Rainbow Six Siege'; exes = @('RainbowSix.exe', 'RainbowSix_DX11.exe', 'RainbowSix_Vulkan.exe'); notes = @('Vulkan if your GPU supports it well; texture filtering anisotropic 16x is free; shadows low, ambient occlusion off, lens effects off.') }, @{ name = 'Rocket League'; exes = @('RocketLeague.exe'); notes = @('Render quality high performance, render detail custom: world detail low, particle detail low, light shafts off, dynamic shadows off; FPS max 250.') }, @{ name = 'Minecraft'; exes = @('Minecraft.Windows.exe', 'MinecraftLauncher.exe', 'Minecraft.exe'); notes = @('Java edition: use Sodium (or OptiFine) and give Java 4 GB in the launcher, not more; render distance 12.', 'Bedrock: V-Sync off, render distance 12 chunks, fancy leaves off.') }, @{ name = 'Roblox'; exes = @('RobloxPlayerBeta.exe'); notes = @('Graphics mode Manual, quality level 3-5; Maximum Frame Rate in the settings (Esc > Settings) set to your refresh rate: Roblox caps at 60 until you raise it, up to 240.') }, @{ name = 'League of Legends'; exes = @('League of Legends.exe'); notes = @('Character quality medium, environment low, effects low, shadows off, anti-aliasing off, frame rate cap uncapped or 240.') }, @{ name = 'GTA V / Online'; exes = @('GTA5.exe', 'GTA5_Enhanced.exe', 'FiveM.exe', 'FiveM_GTAProcess.exe'); notes = @('FXAA on, MSAA off, VSync off, population density 60%, shadow quality normal, reflection quality normal, grass normal, extended distance scaling off.') }, @{ name = 'Rust'; exes = @('RustClient.exe'); notes = @('Launch options: -window-mode exclusive. Leave out -malloc=system and -force-feature-level-11-0 from old guides: current Rust does not need them.', 'Anti-aliasing FXAA, water quality 0, shadow quality 0, draw distance 1500, grass displacement off.') }, @{ name = 'Escape from Tarkov'; exes = @('EscapeFromTarkov.exe'); notes = @('Texture quality high (VRAM permitting), shadows low, object LOD 2, overall visibility 400, HBAO off, SSR off, anisotropic per texture.') }, @{ name = 'PUBG'; exes = @('TslGame.exe'); notes = @('Render scale 100, anti-aliasing low, post-processing very low, shadows very low, textures medium, effects very low, foliage very low, view distance medium.') }, @{ name = 'The Finals'; exes = @('Discovery-Win64-Shipping.exe', 'Discovery.exe'); notes = @('Destruction is CPU work, so the GPU settings are cheap: texture quality high is free, global illumination low is where the frames are.', 'DLSS / FSR on Quality at 1440p and above; Reflex on + boost; V-Sync and motion blur off.') }, @{ name = 'Dota 2'; exes = @('dota2.exe'); notes = @('Launch options in Steam: -high -novid', 'Video > use advanced settings: everything low except texture quality, shadow quality off, V-Sync off, max frames 240 or your refresh rate.') }, @{ name = 'Battlefield 6'; exes = @('BF6.exe', 'bf6.exe'); notes = @('Mesh quality low is the competitive setting (fewer objects drawn); texture quality high is free; effects, lighting and undergrowth low.', 'Future frame rendering on unless input feels heavy; Reflex / Anti-Lag on; DLSS / FSR Quality at 1440p and above.') }, @{ name = 'Deadlock'; exes = @('project8.exe'); notes = @('Launch options in Steam: -high -novid', 'Shadow quality low, texture quality high, effect detail low; the frame limiter is fps_max in the console.') }, @{ name = 'Delta Force'; exes = @('DeltaForceClient-Win64-Shipping.exe'); notes = @('DirectX 12 mode, global illumination off, shadows low, anti-aliasing DLSS / FSR Quality, frame limit at your refresh rate.') }, @{ name = 'ARC Raiders'; exes = @('PioneerGame-Win64-Shipping.exe', 'PioneerGame.exe'); notes = @('Frame generation off for aim; DLSS / FSR Quality; global illumination and shadows low, the rest medium; motion blur off.') }, @{ name = 'Helldivers 2'; exes = @('helldivers2.exe'); notes = @('Async compute on, render scale native or Quality upscale, anti-aliasing TAA, shadow quality low, particle quality medium, volumetric fog low.') }, @{ name = 'Warframe'; exes = @('Warframe.x64.exe'); notes = @('DirectX 12 and the enhanced graphics engine on a recent GPU; dynamic resolution off, motion blur off, V-Sync off, max framerate at your refresh rate.') }, @{ name = 'Destiny 2'; exes = @('destiny2.exe'); notes = @('Framerate cap at your refresh rate (uncapped spikes the CPU); shadow quality low, depth of field and motion blur off, texture quality high, field of view 105.') }, @{ name = 'Halo Infinite'; exes = @('HaloInfinite.exe'); notes = @('Quality preset low, texture filtering high, async compute on, maximum frame rate at your refresh, minimum frame rate off.') }, @{ name = 'Dead by Daylight'; exes = @('DeadByDaylight-Win64-Shipping.exe'); notes = @('Quality low, resolution 100%, anti-aliasing off, frame rate limit 120 (the highest the game offers).') }, @{ name = 'Hunt: Showdown 1896'; exes = @('HuntGame.exe'); notes = @('Graphics quality low, texture quality high, shadows medium (you need to see into them), anti-aliasing DLSS / FSR Quality, motion blur off.') }, @{ name = 'War Thunder'; exes = @('aces.exe'); notes = @('Movie preset off; grass, clouds and water low; anti-aliasing off or TAA at high refresh; V-Sync off.') }, @{ name = 'World of Warcraft'; exes = @('Wow.exe', 'WowClassic.exe'); notes = @('Graphics quality 5-7, shadow quality low, view distance 7, particle density low, DirectX 12, target FPS at your refresh rate.') }, @{ name = 'Squad'; exes = @('SquadGame.exe'); notes = @('Shadows low, ambient occlusion off, texture quality high, foliage medium (low removes cover for you only), anti-aliasing FXAA.') }, @{ name = 'Battlefield 2042'; exes = @('BF2042.exe'); notes = @('Mesh quality low (fewer objects drawn), undergrowth low, effects and lighting low, texture quality high; future frame rendering on; DLSS / FSR Quality at 1440p and above.') }, @{ name = 'EA Sports FC 26'; exes = @('FC26.exe', 'FC25.exe'); notes = @('Rendering quality medium, MSAA 2x, strand-based hair off, dynamic resolution off, in-game frame limiter off and the cap set at your refresh rate in the driver; DirectX 12.') }, @{ name = 'NBA 2K26'; exes = @('NBA2K26.exe', 'NBA2K25.exe'); notes = @('Anti-aliasing off or 2x, shadow quality low, crowd detail low, floor reflections off, depth of field off; the frame cap at your refresh rate.') }, @{ name = 'Palworld'; exes = @('Palworld-Win64-Shipping.exe'); notes = @('DLSS / FSR Quality, view distance medium, grass medium, shadows low, ray tracing off, max FPS at your refresh rate; the world is CPU work, the rest is cheap.') }, @{ name = 'Path of Exile 2'; exes = @('PathOfExileSteam.exe', 'PathOfExile.exe'); notes = @('DirectX 12 or Vulkan (try both), dynamic culling on, dynamic resolution off, shadows and global illumination low, textures high, frame cap at your refresh rate; the engine multithreading setting on.') }, @{ name = 'Diablo IV'; exes = @('Diablo IV.exe'); notes = @('Texture quality high (12 GB of VRAM or more), shadows low, SSAO off, fog quality low, reflections off, DLSS / FSR Quality, Reflex on.') }, @{ name = 'Monster Hunter Wilds'; exes = @('MonsterHunterWilds.exe'); notes = @('DirectX 12, DLSS / FSR Quality, frame generation off unless you are already above 60, shadows medium, ambient occlusion low, ray tracing off, texture quality high with 12 GB of VRAM or more.') }, @{ name = 'Elden Ring / Nightreign'; exes = @('eldenring.exe', 'nightreign.exe'); notes = @('The engine caps at 60; borderless, auto-detect off, shadows medium, SSAO low, ray tracing off, motion blur off. Steady 60 beats anything else here.') }, @{ name = 'Cyberpunk 2077'; exes = @('Cyberpunk2077.exe'); notes = @('DLSS / FSR Quality, ray tracing off below an RTX 4070 class card, crowd density medium, screen space reflections low, volumetric fog medium, Reflex on + boost.') }, @{ name = "Baldur's Gate 3"; exes = @('bg3.exe', 'bg3_dx11.exe'); notes = @('Vulkan on NVIDIA, DirectX 11 on AMD (try both), DLSS / FSR Quality, shadows medium, ambient occlusion off, model detail high, animation level of detail off.') }, @{ name = 'Star Citizen'; exes = @('StarCitizen.exe'); notes = @('Vulkan renderer, upscaling on, clouds low, volumetric fog low, scattered object distance low; the game is CPU and memory bound: 32 GB and an NVMe drive matter more than any setting.') }, @{ name = 'DayZ'; exes = @('DayZ_x64.exe', 'DayZ_BE.exe'); notes = @('Object detail low, terrain detail high (spotting), shadows low, ambient occlusion off, post-processing off, clouds low, anti-aliasing FXAA or off.') }, @{ name = 'Arma Reforger'; exes = @('ArmaReforgerSteam.exe', 'ArmaReforger.exe'); notes = @('Overall quality medium, object draw distance medium, shadows low, anti-aliasing FXAA, VRAM limit 90%, frame cap at your refresh rate.') }, @{ name = 'Hell Let Loose'; exes = @('HLL-Win64-Shipping.exe'); notes = @('Shadows low, foliage low (less grass drawn at range, for you only), post-processing low, anti-aliasing TAA, textures high, view distance high.') }, @{ name = 'Genshin Impact'; exes = @('GenshinImpact.exe'); notes = @('Render resolution 1.0, frame rate 120 where offered, shadows medium, volumetric fog off, motion blur off, anti-aliasing SMAA.') }, @{ name = 'Ready or Not'; exes = @('ReadyOrNot-Win64-Shipping.exe'); notes = @('Shadows low, anti-aliasing TAA, texture quality high, ambient occlusion low, DLSS / FSR Quality, motion blur off.') }, @{ name = 'Final Fantasy XIV'; exes = @('ffxiv_dx11.exe'); notes = @('DirectX 11, DLSS Quality, shadows: self and party only, ambient occlusion off, real-time reflections off, frame rate cap at your refresh rate.') }, @{ name = 'STALKER 2'; exes = @('Stalker2-Win64-Shipping.exe'); notes = @('Global illumination (Lumen) low is the whole cost, shadows medium, DLSS / FSR Quality, frame generation off, texture quality high, hair quality low.') }, @{ name = 'Sea of Thieves'; exes = @('SoTGame.exe'); notes = @('Water detail low, sail quality low, shadow quality low, model detail medium, particle detail low, anti-aliasing TAA, frame cap at your refresh rate.') } ) <# Where games live on this PC: every Steam library the client knows about, each Epic game's own folder, Riot, Xbox, and the usual places. Asking the launchers is faster and surer than crawling Program Files. #> function Get-GameRoots { $roots = New-Object System.Collections.ArrayList $steam = $null foreach ($k in 'HKLM:\SOFTWARE\WOW6432Node\Valve\Steam', 'HKLM:\SOFTWARE\Valve\Steam') { try { $steam = (Get-ItemProperty $k -ErrorAction Stop).InstallPath; if ($steam) { break } } catch { } } if ($steam) { $c = Join-Path $steam 'steamapps\common'; if ((Test-Path $c) -and -not $roots.Contains($c)) { [void]$roots.Add($c) } $vdf = Join-Path $steam 'steamapps\libraryfolders.vdf' if (Test-Path $vdf) { foreach ($mt in [regex]::Matches((Get-Content $vdf -Raw), '"path"\s+"([^"]+)"')) { $lib = Join-Path ($mt.Groups[1].Value -replace '\\\\', '\') 'steamapps\common' if ((Test-Path $lib) -and -not $roots.Contains($lib)) { [void]$roots.Add($lib) } } } } foreach ($mf in Get-ChildItem (Join-Path $env:ProgramData 'Epic\EpicGamesLauncher\Data\Manifests') -Filter '*.item' -ErrorAction SilentlyContinue) { try { $loc = (Get-Content $mf.FullName -Raw | ConvertFrom-Json).InstallLocation; if ($loc -and (Test-Path $loc) -and -not $roots.Contains($loc)) { [void]$roots.Add($loc) } } catch { } } foreach ($r in 'C:\Riot Games', 'D:\Riot Games', 'C:\XboxGames', 'D:\XboxGames', 'C:\Games', 'D:\Games', (Join-Path $script:LocalAppData 'Programs'), (Join-Path $script:LocalAppData 'Roblox\Versions')) { if ($r -and (Test-Path $r) -and -not $roots.Contains($r)) { [void]$roots.Add($r) } } # Program Files, folder by folder, leaving out the vendors no game ships under: walking all of it took twenty seconds. $noGame = '^(Microsoft.*|Windows.*|Common Files|Internet Explorer|Git|dotnet|NVIDIA.*|Intel.*|AMD.*|Google|Mozilla.*|Adobe|Java|Python.*|nodejs|Docker.*|WindowsApps|ModifiableWindowsApps|PowerShell|7-Zip|VideoLAN|Realtek.*|Logitech.*|Corsair|Razer.*|Oracle|OpenJDK|Eclipse.*|Android|JetBrains|MSBuild|Reference Assemblies|WindowsPowerShell|MySQL|PostgreSQL|Amazon.*|Azure.*|Notepad\+\+|CMake|LLVM|Go|Rust.*|Mercurial|Subversion|OpenSSL|Uninstall Information|Wolfram.*|Zoom|Slack|Dropbox|OneDrive|Waves.*|Dolby|Creative|Sonic.*|Hyper-V|Application Verifier|Debugging Tools.*|IIS.*|SQL Server.*|Visual Studio.*|Dell|HP|Lenovo|ASUS|MSI|Gigabyte)$' foreach ($pf in @("$env:ProgramFiles", "${env:ProgramFiles(x86)}")) { if (-not $pf -or -not (Test-Path $pf)) { continue } foreach ($d in Get-ChildItem $pf -Directory -ErrorAction SilentlyContinue) { if ($d.Name -notmatch $noGame -and -not $roots.Contains($d.FullName)) { [void]$roots.Add($d.FullName) } } } return @($roots) } <# The player's other games, beyond the list: Windows' own record of the games it has seen run (Game Bar's list, which stays even with Game Bar off), each Epic game's launch file from its manifest, each Xbox app game's from its MicrosoftGame.config, and each Steam game's program in its own folder (an Unreal game's *-Shipping.exe, else its largest program). Helpers, launchers, crash reporters, anti-cheat, servers and installers are never taken for the game, and names so common that a profile by name would reach other programs (game.exe, client.exe) are left out. $index is Set-GameProfiles' walk of the game folders: exe name -> full paths. #> function Find-OtherGames($index) { $known = @{}; foreach ($g in $script:Games) { foreach ($e in $g.exes) { $known[$e.ToLower()] = $true } } $skip = '(?i)^(unins.*|.*crash.*|.*report.*|.*setup.*|.*install.*|.*redist.*|dxsetup|dotnet.*|.*launcher.*|.*update.*|.*helper.*|.*anticheat.*|easyanticheat.*|beservice.*|battleye.*|start_protected_game|.*server.*|.*editor.*|.*benchmark.*|.*config.*|.*settings.*|.*tool.*|cefsharp.*|.*webhelper.*|.*bootstrap.*|.*prereq.*|vc_.*|oalinst|.*handler.*|.*sandbox.*|wallpaper.*|vrserver|vrmonitor|vrstartup|.*overlay.*|.*service.*|.*agent.*|.*register.*|.*activation.*)\.exe$' $generic = '(?i)^(game|client|launcher|start|play|main|app|run|win64|shipping|bin|x64|release|program|setup)\.exe$' $winDir = "$env:WINDIR".ToLower() $found = [ordered]@{} $add = { param([string]$name, [string]$path) if (-not $path -or -not $path.ToLower().EndsWith('.exe') -or $path.ToLower().StartsWith($winDir)) { return } $exe = [IO.Path]::GetFileName($path); $k = $exe.ToLower() if ($known.ContainsKey($k) -or $exe -match $skip -or $exe -match $generic) { return } if (-not $found.Contains($k)) { $found[$k] = @{ name = $name; exe = $exe; paths = New-Object System.Collections.ArrayList } } if ((Test-Path -LiteralPath $path) -and -not $found[$k].paths.Contains($path)) { [void]$found[$k].paths.Add($path) } } # 1. The games Windows has seen run. foreach ($c in Get-ChildItem (Resolve-Hive 'HKCU:\System\GameConfigStore\Children') -ErrorAction SilentlyContinue) { try { $p = (Get-ItemProperty -LiteralPath $c.PSPath -ErrorAction Stop).MatchedExeFullPath; if ($p) { & $add ([IO.Path]::GetFileNameWithoutExtension($p)) $p } } catch { } } # 2. Epic: the manifest names the game's own launch file (and an Unreal game's real program sits beside it). foreach ($mf in Get-ChildItem (Join-Path $env:ProgramData 'Epic\EpicGamesLauncher\Data\Manifests') -Filter '*.item' -ErrorAction SilentlyContinue) { try { $m = Get-Content $mf.FullName -Raw | ConvertFrom-Json if ($m.AppCategories -and -not (@($m.AppCategories) -contains 'games')) { continue } if ($m.LaunchExecutable -and $m.InstallLocation) { & $add $m.DisplayName (Join-Path $m.InstallLocation ($m.LaunchExecutable -replace '/', '\')) } if ($m.InstallLocation) { foreach ($v in $index.Values) { foreach ($p in $v) { if ($p.StartsWith($m.InstallLocation + '\', [StringComparison]::OrdinalIgnoreCase) -and $p -match '(?i)-Win(64|GDK)-Shipping\.exe$') { & $add $m.DisplayName $p } } } } } catch { } } # 3. The Xbox app's games: MicrosoftGame.config lists each program the game runs. foreach ($d in @(Get-CimInstance Win32_LogicalDisk -Filter 'DriveType=3' -ErrorAction SilentlyContinue)) { $xg = Join-Path ($d.DeviceID + '\') 'XboxGames' if (-not (Test-Path $xg)) { continue } foreach ($cfg in Get-ChildItem $xg -Filter 'MicrosoftGame.config' -Recurse -Depth 2 -ErrorAction SilentlyContinue) { try { $x = [xml](Get-Content $cfg.FullName -Raw) $title = Split-Path (Split-Path $cfg.DirectoryName) -Leaf foreach ($e in @($x.SelectNodes('//*[local-name()="Executable"]'))) { $n = $e.GetAttribute('Name'); if ($n) { & $add $title (Join-Path $cfg.DirectoryName $n) } } } catch { } } } # 4. Steam: each installed game's folder, from its manifest; tools, runtimes, soundtracks and servers left out. $byDir = @{} foreach ($v in $index.Values) { foreach ($p in $v) { $mt = [regex]::Match($p, '(?i)^(.*\\steamapps\\common\\[^\\]+)\\'); if ($mt.Success) { $g = $mt.Groups[1].Value.ToLower(); if (-not $byDir.ContainsKey($g)) { $byDir[$g] = New-Object System.Collections.ArrayList }; [void]$byDir[$g].Add($p) } } } $commons = @($byDir.Keys | ForEach-Object { ($_ -replace '\\[^\\]+$', '') } | Select-Object -Unique) foreach ($common in $commons) { foreach ($acf in Get-ChildItem (Split-Path $common) -Filter 'appmanifest_*.acf' -ErrorAction SilentlyContinue) { try { $t = Get-Content $acf.FullName -Raw $name = [regex]::Match($t, '"name"\s+"([^"]+)"').Groups[1].Value $dir = [regex]::Match($t, '"installdir"\s+"([^"]+)"').Groups[1].Value if (-not $dir -or $name -match '(?i)Redistributable|Steamworks|Proton|Runtime|SteamVR|Soundtrack|Dedicated Server|SDK|Wallpaper Engine|Source Filmmaker|Blender|Benchmark') { continue } $exes = @($byDir[(Join-Path $common $dir).ToLower()] | Where-Object { $_ -and ([IO.Path]::GetFileName($_) -notmatch $skip) -and ([IO.Path]::GetFileName($_) -notmatch $generic) -and $_ -notmatch '(?i)\\_CommonRedist\\|\\Redist\\|\\Engine\\Binaries\\ThirdParty\\' }) if (-not $exes.Count) { continue } $ship = @($exes | Where-Object { $_ -match '(?i)-Win(64|GDK)-Shipping\.exe$' }) if ($ship.Count) { foreach ($p in $ship) { & $add $name $p } } else { $big = $exes | Sort-Object { try { (Get-Item -LiteralPath $_).Length } catch { 0 } } -Descending | Select-Object -First 1; & $add $name $big } } catch { } } } # Forty at most: a profile per game, not per program on the disk. return @($found.Values | Select-Object -First 40) } function Set-GameProfiles($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-GameProfiles is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Windows' crash shim on games (1.111.0). After a program crashes a few times in an hour, Windows puts its Fault Tolerant Heap on it: from then on every start of it pads and delays its memory work, to ride out a bug that may never come back. A game that crashed with a driver once too often keeps paying that on every frame, and nothing says so. Windows keeps the list under HKLM\SOFTWARE\Microsoft\FTH\State, one value per program. The games' values go (the listed games, the player's other games, anything in a game library folder); every other program keeps its shim. Windows puts it back by itself if the game keeps crashing, and undo puts the value back as it was. #> $script:FthState = 'SOFTWARE\Microsoft\FTH\State' $script:GameDirs = '(?i)\\steamapps\\common\\|\\Epic Games\\|\\Riot Games\\|\\XboxGames\\|\\ModifiableWindowsApps\\|\\Ubisoft Game Launcher\\games\\|\\EA Games\\|\\Electronic Arts\\|\\GOG Galaxy\\Games\\|\\GOG Games\\|\\Rockstar Games\\|\\Amazon Games\\|\\Battle\.net\\|\\Call of Duty\\|\\Overwatch\\|\\World of Warcraft\\|\\Wargaming\.net\\|\\Games\\' # The game a program belongs to (1.111.0; shared with the crash advice in 1.114.0): by the tune's list of games' programs # and the other games found on the PC, else by the library folder it is in, else none. function Get-GameListed { $listed = @{} foreach ($g in $script:Games) { foreach ($e in $g.exes) { $listed[$e.ToLower()] = $g.name } } foreach ($o in @($script:OtherGameList)) { if ($o -and $o.exe) { $listed[("$($o.exe)").ToLower()] = $o.name } } return $listed } function Resolve-GameName([string]$path, $listed) { if (-not $path) { return $null } $leaf = ($path -split '\\')[-1] if ($listed.ContainsKey($leaf.ToLower())) { return $listed[$leaf.ToLower()] } if ($path -match $script:GameDirs) { # The game's own folder under the library (steamapps\common\Neon Drift\...), the most telling library first, else # the program's name. foreach ($lib in 'steamapps\\common', 'Epic Games', 'Riot Games', 'XboxGames', 'GOG Games', 'Rockstar Games', 'Amazon Games', 'EA Games', 'Games') { if ($path -match ('(?i)\\' + $lib + '\\([^\\]+)\\')) { return $Matches[1] } } return [IO.Path]::GetFileNameWithoutExtension($leaf) } return $null } function Get-GameCrashShims { $k = $null try { $k = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($script:FthState) } catch { } if (-not $k) { return @() } $hits = @() try { $listed = Get-GameListed foreach ($n in @($k.GetValueNames())) { if (-not $n) { continue } $name = Resolve-GameName $n $listed if ($name) { $hits += @{ value = $n; name = $name; exe = ($n -split '\\')[-1] } } } } catch { } finally { $k.Close() } return $hits } function Reset-GameCrashShims { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Reset-GameCrashShims is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Competitive settings written into the games' own settings files. Only a line already in the file changes, and only when its value has the shape the game uses (a key a game renames is left alone, never added). Each file is backed up before the first change; each change is recorded, and undo puts back the value it replaced, if the line still holds what the tune wrote. A running game is skipped: it rewrites its file when it closes. VALORANT, Overwatch 2 and League of Legends sync settings from the account, so their files are left alone and the report carries their settings instead. #> $script:GameFiles = @( @{ game = 'Fortnite'; where = '%LOCALAPPDATA%\FortniteGame\Saved\Config\WindowsClient\GameUserSettings.ini'; procs = @('FortniteClient-Win64-Shipping'); format = 'ini' find = { @(Join-Path $script:LocalAppData 'FortniteGame\Saved\Config\WindowsClient\GameUserSettings.ini') } set = @(@('bUseVSync', 'False', 'V-Sync off'), @('bMotionBlur', 'False', 'motion blur off'), @('sg.ShadowQuality', '0', 'shadows off'), @('sg.PostProcessQuality', '0', 'post-processing low')); raisecap = 'FrameRateLimit' }, @{ game = 'Marvel Rivals'; where = '%LOCALAPPDATA%\Marvel\Saved\Config\Windows\GameUserSettings.ini'; procs = @('Marvel-Win64-Shipping'); format = 'ini' find = { @(Join-Path $script:LocalAppData 'Marvel\Saved\Config\Windows\GameUserSettings.ini') } set = @(@('bUseVSync', 'False', 'V-Sync off'), @('sg.ShadowQuality', '0', 'shadows low'), @('sg.PostProcessQuality', '0', 'post-processing low')); raisecap = 'FrameRateLimit' }, # Two more Unreal games (1.110.0), the same way: only lines the file already has change. @{ game = 'PUBG: Battlegrounds'; where = '%LOCALAPPDATA%\TslGame\Saved\Config\WindowsNoEditor\GameUserSettings.ini'; procs = @('TslGame'); format = 'ini' find = { @(Join-Path $script:LocalAppData 'TslGame\Saved\Config\WindowsNoEditor\GameUserSettings.ini') } set = @(@('bUseVSync', 'False', 'V-Sync off'), @('sg.PostProcessQuality', '0', 'post-processing very low'), @('sg.ShadowQuality', '0', 'shadows very low'), @('sg.FoliageQuality', '0', 'foliage very low')); raisecap = 'FrameRateLimit' }, @{ game = 'THE FINALS'; where = '%LOCALAPPDATA%\Discovery\Saved\Config\WindowsClient\GameUserSettings.ini'; procs = @('Discovery', 'Discovery-Win64-Shipping'); format = 'ini' find = { @(Join-Path $script:LocalAppData 'Discovery\Saved\Config\WindowsClient\GameUserSettings.ini') } set = @(@('bUseVSync', 'False', 'V-Sync off'), @('sg.PostProcessQuality', '0', 'post-processing low')); raisecap = 'FrameRateLimit' }, @{ game = 'Apex Legends'; where = '%USERPROFILE%\Saved Games\Respawn\Apex\local\videoconfig.txt'; procs = @('r5apex', 'r5apex_dx12'); format = 'kv' find = { @(Join-Path $script:UserProfile 'Saved Games\Respawn\Apex\local\videoconfig.txt') } set = @(@('setting.mat_vsync_mode', '0', 'V-Sync off'), @('setting.csm_enabled', '0', 'sun shadows off'), @('setting.shadow_enable', '0', 'spot shadows off'), @('setting.dvs_enable', '0', 'adaptive resolution off')) }, @{ game = 'Counter-Strike 2'; where = 'Steam\userdata\\730\local\cfg\cs2_video.txt'; procs = @('cs2'); format = 'kv' find = { $s = Get-SteamDir; if ($s) { @(Get-ChildItem -Path (Join-Path $s 'userdata') -Directory -ErrorAction SilentlyContinue | ForEach-Object { Join-Path $_.FullName '730\local\cfg\cs2_video.txt' }) } else { @() } } set = @(@('setting.mat_vsync', '0', 'V-Sync off'), @('setting.r_player_visibility_mode', '1', 'boost player contrast on')) # NVIDIA Reflex (r_low_latency: 0 off, 1 on, 2 on + boost), on an NVIDIA card only: boost on a desktop, on for a laptop. nvidia = ,@('setting.r_low_latency', '2', 'NVIDIA Reflex: on + boost'); nvidiaLaptop = ,@('setting.r_low_latency', '1', 'NVIDIA Reflex: on') }, @{ game = 'Rocket League'; where = 'Documents\My Games\Rocket League\TAGame\Config\TASystemSettings.ini'; procs = @('RocketLeague'); format = 'ini' find = { @(Join-Path (Get-DocumentsDir) 'My Games\Rocket League\TAGame\Config\TASystemSettings.ini') } set = @(@('UseVsync', 'False', 'V-Sync off'), @('MotionBlur', 'False', 'motion blur off'), @('DynamicShadows', 'False', 'dynamic shadows off'), @('DepthOfField', 'False', 'depth of field off')) }, @{ game = 'Rainbow Six Siege'; where = 'Documents\My Games\Rainbow Six - Siege\\GameSettings.ini'; procs = @('RainbowSix', 'RainbowSix_Vulkan', 'RainbowSix_BE'); format = 'ini' find = { @(Get-ChildItem -Path (Join-Path (Get-DocumentsDir) 'My Games\Rainbow Six - Siege') -Directory -ErrorAction SilentlyContinue | ForEach-Object { Join-Path $_.FullName 'GameSettings.ini' }) } # One pair, written with a leading comma: @(@(...)) would flatten it into three strings, and until 1.110.0 Siege's # V-Sync was never written for that reason. set = ,@('VSync', '0', 'V-Sync off') }, @{ game = 'Minecraft (Java)'; where = '%APPDATA%\.minecraft\options.txt'; procs = @('javaw'); format = 'colon' find = { @(Join-Path $script:AppData '.minecraft\options.txt') } set = @(@('enableVsync', 'false', 'V-Sync off'), @('entityShadows', 'false', 'entity shadows off')) } ) # Documents as Windows has it for this person: moved to OneDrive, another drive, or the default. function Get-DocumentsDir { $d = $null try { $d = (Get-Item -Path (Join-Path $script:HKCU 'Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders') -ErrorAction Stop).GetValue('Personal', $null, 'DoNotExpandEnvironmentNames') } catch { } if ($d) { $d = [Environment]::ExpandEnvironmentVariables(($d -replace '%USERPROFILE%', $script:UserProfile)) } if (-not $d -or -not (Test-Path -Path $d)) { $d = Join-Path $script:UserProfile 'Documents' } return $d } function Get-SteamDir { foreach ($k in 'HKLM:\SOFTWARE\WOW6432Node\Valve\Steam', 'HKLM:\SOFTWARE\Valve\Steam') { try { $p = (Get-ItemProperty $k -ErrorAction Stop).InstallPath; if ($p -and (Test-Path $p)) { return $p } } catch { } } return $null } # One line's key, current value and the text around the value, in the file's own syntax. function Get-GameLinePattern([string]$format, [string]$key) { $k = [regex]::Escape($key) switch ($format) { 'ini' { return "^(\s*$k\s*=\s*)(.*?)(\s*)$" } 'kv' { return "^(\s*`"$k`"\s+`")(.*?)(`"\s*)$" } 'colon' { return "^($k\:)(.*?)(\s*)$" } } } # Whether the value on disk is the kind the tune would write: a switch for a switch, a number for a number. function Test-GameValueShape([string]$current, [string]$target) { if ($target -match '^(?i)(true|false)$') { return [bool]($current -match '^(?i)(true|false)$') } if ($target -match '^-?[0-9]+(\.[0-9]+)?$') { return [bool]($current -match '^-?[0-9]+(\.[0-9]+)?$') } return $false } <# The file's text with the pairs applied, and the list of what changed. Nothing is written here. #> function Get-GameFileEdit([string]$text, [string]$format, $pairs) { $parts = [regex]::Split($text, '(\r?\n)') $done = New-Object System.Collections.ArrayList foreach ($pair in $pairs) { $pat = Get-GameLinePattern $format $pair[0] for ($i = 0; $i -lt $parts.Count; $i += 2) { $mt = [regex]::Match($parts[$i], $pat) if (-not $mt.Success) { continue } $cur = $mt.Groups[2].Value if ($cur -eq $pair[1]) { break } if (-not (Test-GameValueShape $cur $pair[1])) { break } # A frame cap is only raised from 60 or below (a game's default on a fast screen); unlimited (0), or a cap # somebody chose above 60 (one just under the refresh rate with G-SYNC or FreeSync), is left as it is. if ($pair.Count -ge 4 -and $pair[3] -eq 'raise') { $cn = 0.0 if (-not [double]::TryParse($cur, [Globalization.NumberStyles]::Float, [Globalization.CultureInfo]::InvariantCulture, [ref]$cn)) { break } if ($cn -le 0 -or $cn -gt 60) { break } } $parts[$i] = $mt.Groups[1].Value + $pair[1] + $mt.Groups[3].Value [void]$done.Add(@{ key = $pair[0]; prev = $cur; value = $pair[1]; what = $pair[2] }) break } } return @{ text = ($parts -join ''); changes = @($done) } } # The file as text in its own encoding (UTF-16 or UTF-8, with or without a byte-order mark), and that encoding. function Read-GameFile([string]$path) { $bytes = [IO.File]::ReadAllBytes($path) $enc = New-Object System.Text.UTF8Encoding($false) if ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFF -and $bytes[1] -eq 0xFE) { $enc = New-Object System.Text.UnicodeEncoding($false, $true) } elseif ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { $enc = New-Object System.Text.UTF8Encoding($true) } return @{ text = [IO.File]::ReadAllText($path, $enc); enc = $enc } } <# What the tune would change in every game file it finds, without changing anything: for the free look and the run. #> function Get-GameFilePlan { $plan = New-Object System.Collections.ArrayList # What the graphics card is decides the NVIDIA-only settings (Reflex); read here, so the free look shows the same. $nv = $false; $lap = $false try { $nv = [bool]@(Get-CimInstance Win32_VideoController -ErrorAction Stop | Where-Object { $_.Name -match 'NVIDIA' }).Count } catch { } try { $lap = [bool]@(Get-CimInstance Win32_Battery -ErrorAction Stop).Count } catch { } # The main screen's highest refresh rate, for a game's frame cap left at 60 on a faster screen. (OMNIDX_TEST_MAXHZ # stands in for a fast screen on the build machine, whose virtual one runs at 64 Hz.) $maxHz = 0 if ($env:OMNIDX_TEST_MAXHZ -match '^\d{2,3}$') { $maxHz = [int]$env:OMNIDX_TEST_MAXHZ } else { try { if (-not ('OmniDisplay' -as [type])) { Add-Type -TypeDefinition $script:DisplaySource -ErrorAction Stop }; $pr = @([OmniDisplay]::List() | Where-Object { $_.Primary })[0]; if ($pr) { $maxHz = [int]$pr.MaxHz } } catch { } } foreach ($g in $script:GameFiles) { $pairs = @($g.set) if ($nv -and $g.nvidia) { $pairs += $(if ($lap -and $g.nvidiaLaptop) { @($g.nvidiaLaptop) } else { @($g.nvidia) }) } if ($g.raisecap -and $maxHz -ge 75) { $pairs += ,@($g.raisecap, ('{0}.000000' -f $maxHz), ("frame cap raised to {0} (the screen's rate)" -f $maxHz), 'raise') } foreach ($path in @(& $g.find)) { if (-not $path -or -not (Test-Path -LiteralPath $path -PathType Leaf)) { continue } try { $f = Read-GameFile $path $e = Get-GameFileEdit $f.text $g.format $pairs [void]$plan.Add(@{ game = $g.game; procs = $g.procs; path = $path; format = $g.format; set = $pairs; changes = $e.changes; enc = $f.enc }) } catch { } } } return @($plan) } function Set-GameFiles($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-GameFiles is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # GPU vendor extras # --------------------------------------------------------------------------- <# The NVIDIA driver's profile settings, through its own interface (NVAPI, the one NVIDIA Control Panel and NVIDIA Profile Inspector use). Kept next to undo.ps1 as omnidx-nv.cs when used, so undo can put the values back. #> $script:NvSource = @' using System; using System.Runtime.InteropServices; // The NVIDIA driver's profile settings (NVAPI DRS), the ones NVIDIA Control Panel > Manage 3D settings writes. Every // function comes from nvapi64.dll by its published ID (NVIDIA's nvapi_interface.h); every structure is laid out as // NVIDIA's nvapi.h has it, and carries its version (size | version << 16), so a driver that expects another layout // refuses the call (-9) instead of reading past it. Statuses are NVAPI's: 0 is OK, -163 no such profile, -166 no such // application, -167 application already in another profile. public static class OmniNv { [DllImport("nvapi64.dll", EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr QueryInterface(uint id); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int F0(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FOut(out IntPtr h); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FH(IntPtr h); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FHOut(IntPtr s, out IntPtr h); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FHH(IntPtr s, IntPtr p); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FHPOut(IntPtr s, IntPtr p, out IntPtr h); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FHPOutP(IntPtr s, IntPtr name, out IntPtr prof, IntPtr app); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FHHP(IntPtr s, IntPtr prof, IntPtr p); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FHHUP(IntPtr s, IntPtr prof, uint id, IntPtr p); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate int FHHU(IntPtr s, IntPtr prof, uint id); static IntPtr session = IntPtr.Zero; // NVDRS_SETTING_V1: 12328 bytes since its unions gained a 64-bit value (current value at 8224); 12320 before // (current value at 8220). The first is tried, the second if the driver refuses it. static int setSize = 12328, setCur = 8224; // NVDRS_APPLICATION: version 4 (20492 bytes), or version 3 (16396) for an older driver. static int appSize = 20492, appVer = 4; static T Fn(uint id) where T : class { IntPtr p = QueryInterface(id); if (p == IntPtr.Zero) throw new InvalidOperationException("this driver has no NVAPI function 0x" + id.ToString("X8")); return (T)(object)Marshal.GetDelegateForFunctionPointer(p, typeof(T)); } static IntPtr Block(int size) { IntPtr b = Marshal.AllocHGlobal(size); for (int i = 0; i < size; i += 4) Marshal.WriteInt32(b, i, 0); return b; } // NvAPI_UnicodeString: 2048 UTF-16 units, zero-terminated, written at an offset in a block. static void Text(IntPtr b, int at, string s) { s = s ?? ""; int n = Math.Min(s.Length, 2047); for (int i = 0; i < n; i++) Marshal.WriteInt16(b, at + i * 2, (short)s[i]); Marshal.WriteInt16(b, at + n * 2, 0); } static string ReadText(IntPtr b, int at) { var sb = new System.Text.StringBuilder(); for (int i = 0; i < 2048; i++) { short c = Marshal.ReadInt16(b, at + i * 2); if (c == 0) break; sb.Append((char)c); } return sb.ToString(); } // null when ready; otherwise what stopped it. public static string Open() { try { int r = Fn(0x0150E828)(); // NvAPI_Initialize if (r != 0) return "NvAPI_Initialize returned " + r; IntPtr s; r = Fn(0x0694D52E)(out s); // NvAPI_DRS_CreateSession if (r != 0) return "NvAPI_DRS_CreateSession returned " + r; session = s; r = Fn(0x375DBD6B)(session); // NvAPI_DRS_LoadSettings if (r != 0) return "NvAPI_DRS_LoadSettings returned " + r; return null; } catch (DllNotFoundException) { return "no NVIDIA driver interface (nvapi64.dll)"; } catch (EntryPointNotFoundException) { return "nvapi64.dll without nvapi_QueryInterface"; } catch (Exception e) { return e.Message; } } public static int Save() { return Fn(0xFCBC7E14)(session); } // NvAPI_DRS_SaveSettings public static void Close() { try { if (session != IntPtr.Zero) Fn(0xDAD9CFF8)(session); } catch { } // NvAPI_DRS_DestroySession session = IntPtr.Zero; } // The profile an executable belongs to (NVIDIA's own game profiles included), and that profile's name. public static int FindApp(string exe, out IntPtr profile, out string profileName) { profile = IntPtr.Zero; profileName = ""; IntPtr name = Block(4096); try { Text(name, 0, exe); for (int attempt = 0; attempt < 2; attempt++) { IntPtr app = Block(appSize); try { Marshal.WriteInt32(app, 0, appSize | (appVer << 16)); IntPtr p; int r = Fn(0xEEE566B2)(session, name, out p, app); // NvAPI_DRS_FindApplicationByName if (r == -9 && appVer == 4) { appSize = 16396; appVer = 3; continue; } if (r != 0) return r; profile = p; profileName = ProfileName(p); return 0; } finally { Marshal.FreeHGlobal(app); } } return -9; } finally { Marshal.FreeHGlobal(name); } } static string ProfileName(IntPtr profile) { IntPtr info = Block(4116); // NVDRS_PROFILE_V1 try { Marshal.WriteInt32(info, 0, 4116 | (1 << 16)); if (Fn(0x61CD6FD6)(session, profile, info) != 0) return ""; // NvAPI_DRS_GetProfileInfo return ReadText(info, 4); } catch { return ""; } finally { Marshal.FreeHGlobal(info); } } public static int FindProfile(string name, out IntPtr profile) { profile = IntPtr.Zero; IntPtr n = Block(4096); try { Text(n, 0, name); IntPtr p; int r = Fn(0x7E4A9A0B)(session, n, out p); if (r == 0) profile = p; return r; } // NvAPI_DRS_FindProfileByName finally { Marshal.FreeHGlobal(n); } } public static int CreateProfile(string name, out IntPtr profile) { profile = IntPtr.Zero; IntPtr info = Block(4116); try { Marshal.WriteInt32(info, 0, 4116 | (1 << 16)); Text(info, 4, name); IntPtr p; int r = Fn(0xCC176068)(session, info, out p); // NvAPI_DRS_CreateProfile if (r == 0) profile = p; return r; } finally { Marshal.FreeHGlobal(info); } } public static int DeleteProfile(IntPtr profile) { return Fn(0x17093206)(session, profile); } // NvAPI_DRS_DeleteProfile // The global profile, the one NVIDIA Control Panel > Manage 3D settings > Global Settings writes. public static int GlobalProfile(out IntPtr profile) { return Fn(0x617BFF9F)(session, out profile); } // NvAPI_DRS_GetCurrentGlobalProfile public static int CreateApp(IntPtr profile, string exe, string friendly) { for (int attempt = 0; attempt < 2; attempt++) { IntPtr app = Block(appSize); try { Marshal.WriteInt32(app, 0, appSize | (appVer << 16)); Text(app, 8, exe); // appName Text(app, 8 + 4096, friendly); // userFriendlyName int r = Fn(0x4347A9DE)(session, profile, app); // NvAPI_DRS_CreateApplication if (r == -9 && appVer == 4) { appSize = 16396; appVer = 3; continue; } return r; } finally { Marshal.FreeHGlobal(app); } } return -9; } // A DWORD setting on a profile: its value, where it comes from (0 this profile, 1 global, 2 base, 3 driver // default) and whether it is NVIDIA's predefined value (non-zero) rather than one a person set. public static int GetDword(IntPtr profile, uint id, out uint value, out int location, out int predefined) { value = 0; location = -1; predefined = 0; for (int attempt = 0; attempt < 2; attempt++) { IntPtr b = Block(12328); try { Marshal.WriteInt32(b, 0, setSize | (1 << 16)); int r = Fn(0x73BF8338)(session, profile, id, b); // NvAPI_DRS_GetSetting if (r == -9 && setSize == 12328) { setSize = 12320; setCur = 8220; continue; } if (r != 0) return r; if (Marshal.ReadInt32(b, 4104) != 0) return -5; // not a DWORD setting (NVAPI_INVALID_ARGUMENT) location = Marshal.ReadInt32(b, 4108); predefined = Marshal.ReadInt32(b, 4112); value = (uint)Marshal.ReadInt32(b, setCur); return 0; } finally { Marshal.FreeHGlobal(b); } } return -9; } public static int SetDword(IntPtr profile, uint id, uint value) { for (int attempt = 0; attempt < 2; attempt++) { IntPtr b = Block(12328); try { Marshal.WriteInt32(b, 0, setSize | (1 << 16)); Marshal.WriteInt32(b, 4100, (int)id); // settingId Marshal.WriteInt32(b, 4104, 0); // NVDRS_DWORD_TYPE Marshal.WriteInt32(b, setCur, (int)value); // u32CurrentValue int r = Fn(0x577DD202)(session, profile, b); // NvAPI_DRS_SetSetting if (r == -9 && setSize == 12328) { setSize = 12320; setCur = 8220; continue; } return r; } finally { Marshal.FreeHGlobal(b); } } return -9; } // Takes a setting off a profile: the driver's predefined (or global) value applies again. public static int DeleteSetting(IntPtr profile, uint id) { return Fn(0xE4A26362)(session, profile, id); } // NvAPI_DRS_DeleteProfileSetting } '@ <# Each listed game found on this PC, in the driver: what NVIDIA Control Panel > Manage 3D settings > Program Settings would set for it by hand. Power management at prefer maximum performance (the card does not drop its clocks mid-match), low latency mode on (one frame queued, not the driver's up to three), texture filtering at high performance (a sharpness difference nobody sees in motion). A game NVIDIA already has a profile for gets these on that profile; one it does not gets a profile of its own, "OmniDx - ". V-Sync, the frame cap and G-Sync are left to the game and the player: Reflex and G-Sync want them set together, not by a script. Every value replaced is recorded; undo puts it back, or takes the setting off the profile so the driver's own value applies again. #> $script:NvSettings = @( @(0x1057EB71, 1, 'power management: prefer maximum performance'), @(0x007BA09E, 1, 'low latency mode: on'), @(0x00CE2691, 0x14, 'texture filtering: high performance') ) function Set-NvidiaProfiles($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-NvidiaProfiles is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# One setting for every game, on the global profile (1.104.0): Preferred refresh rate = Highest available. A game in exclusive fullscreen otherwise takes whatever refresh rate it asks Windows for, and some ask for 60 Hz on a 144 or 240 Hz monitor; with it, the driver runs each game at the monitor's highest rate at that resolution. G-SYNC and a frame cap work with it as before. Recorded like the game settings; undo puts back the value a person set, or takes it off so the driver's own applies. Shader cache size is left at Driver Default: today's drivers keep more than the 10 GB old guides set it to. #> function Set-NvidiaGlobal { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-NvidiaGlobal is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# The driver itself: its version as NVIDIA numbers it (32.0.15.6094 is 560.94) and its age. A driver months old misses the per-game fixes and profiles NVIDIA ships with each Game Ready release. #> function Test-NvidiaDriver { $vc = Get-CimInstance Win32_VideoController -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'NVIDIA' } | Select-Object -First 1 if (-not $vc -or -not $vc.DriverVersion) { return } $digits = ($vc.DriverVersion -replace '\D', '') $ver = $(if ($digits.Length -ge 5) { $d = $digits.Substring($digits.Length - 5); '{0}.{1}' -f $d.Substring(0, 3).TrimStart('0'), $d.Substring(3) } else { $vc.DriverVersion }) if (-not $vc.DriverDate) { Say (" NVIDIA driver {0}" -f $ver); return } $age = [int]((Get-Date) - $vc.DriverDate).TotalDays if ($age -gt 120) { Warn ("NVIDIA driver {0} is {1} days old. NVIDIA app > Drivers, or nvidia.com/drivers: the latest Game Ready driver, Custom install, Clean install ticked. New drivers carry per-game fixes and profiles." -f $ver, $age) } else { Say (" NVIDIA driver {0}, {1} days old: current." -f $ver, $age) } } function Tune-Gpu($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Tune-Gpu is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # extreme (opt-in): fewer conveniences, a few more frames # --------------------------------------------------------------------------- <# What the people who tune for a living do after the safe tune, minus the myths and minus anything that lowers security: the shell drawn plain, the services a person might notice going, memory compression off where there is RAM to spare, the dynamic tick off. Every line recorded; undo puts it back; keep.ps1 leaves the personal ones alone. Asked about first, because "you will notice" is the point. #> function Set-Extreme($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-Extreme is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # Instant sign-in's two calls into Windows, compiled once when needed: LogonUser, to check the password before anything # is changed (a wrong one would leave the PC at a failed sign-in), and the LSA secret store, where Windows' own # automatic sign-in reads the password from (the registry value it also accepts is readable by every account). $script:SignInSource = 'using System; using System.Runtime.InteropServices; public static class OmniDxSignIn { [StructLayout(LayoutKind.Sequential)] struct U { public ushort L; public ushort M; public IntPtr B; } [StructLayout(LayoutKind.Sequential)] struct OA { public int Len; public IntPtr R; public IntPtr N; public uint A; public IntPtr S; public IntPtr Q; } [DllImport("advapi32.dll")] static extern uint LsaOpenPolicy(IntPtr s, ref OA o, uint a, out IntPtr h); [DllImport("advapi32.dll", EntryPoint = "LsaStorePrivateData")] static extern uint StoreData(IntPtr h, ref U k, ref U d); [DllImport("advapi32.dll", EntryPoint = "LsaStorePrivateData")] static extern uint ClearData(IntPtr h, ref U k, IntPtr d); [DllImport("advapi32.dll")] static extern uint LsaClose(IntPtr h); [DllImport("advapi32.dll")] static extern int LsaNtStatusToWinError(uint s); [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool LogonUser(string u, string d, string p, int t, int v, out IntPtr h); [DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h); static U Str(string s) { U u = new U(); u.B = Marshal.StringToHGlobalUni(s); u.L = (ushort)(s.Length * 2); u.M = (ushort)(s.Length * 2 + 2); return u; } public static int Secret(string key, string value) { OA o = new OA(); o.Len = Marshal.SizeOf(typeof(OA)); IntPtr h; uint st = LsaOpenPolicy(IntPtr.Zero, ref o, 0xF0FFF, out h); if (st != 0) return LsaNtStatusToWinError(st); U k = Str(key); try { if (value == null) { st = ClearData(h, ref k, IntPtr.Zero); } else { U d = Str(value); try { st = StoreData(h, ref k, ref d); } finally { Marshal.FreeHGlobal(d.B); } } } finally { Marshal.FreeHGlobal(k.B); LsaClose(h); } return LsaNtStatusToWinError(st); } public static int TryLogon(string user, string domain, string password) { IntPtr t; if (LogonUser(user, domain, password, 2, 0, out t)) { CloseHandle(t); return 0; } return Marshal.GetLastWin32Error(); } }' function Add-SignInApi { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Add-SignInApi is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Instant sign-in (1.88.0, comes with Extreme): Windows signs this account in by itself at every start, and waking from sleep asks for nothing. The PIN stays on the account; Windows + L still locks with it. Checked first with the account's own password (or none, for an account without one), so a wrong one never leaves the PC at a failed sign-in; the password goes where Windows' automatic sign-in reads it, the LSA secret store, never the registry. Undo puts the sign-in, the wake prompt and the passwordless-account switch back and wipes the stored password. #> function Set-AutoSignIn($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-AutoSignIn is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Windows Update on your say-so (1.89.0; comes with Extreme, or its own box and -ManualUpdates). Automatic updates off by Windows' own policy, the one "Configure Automatic Updates: Disabled" writes: nothing downloads, installs or restarts by itself, and the scans that wake the PC and start the update workers in the middle of a game stop. Windows Update itself stays (its service is untouched, Settings > Windows Update > Check for updates finds and installs every update when pressed), Defender is not touched and keeps fetching its virus definitions on its own schedule. Windows Update never swaps a driver, so the graphics driver stays the one you installed; a new Windows version (a feature update) waits a year; Store apps update when the Store is opened, unless Game Pass or the Xbox app is in use (games have to be current to play online). The update orchestrator's scheduled scans, wakes and restarts are switched off where Windows lets an administrator (some builds protect them; the policy covers those). Every value and task is recorded and undone, and keep.ps1 puts the policy back if an update removes it. #> $script:UpdateTasks = @( '\Microsoft\Windows\WindowsUpdate\Scheduled Start', '\Microsoft\Windows\UpdateOrchestrator\Schedule Scan', '\Microsoft\Windows\UpdateOrchestrator\Schedule Scan Static Task', '\Microsoft\Windows\UpdateOrchestrator\Schedule Wake To Work', '\Microsoft\Windows\UpdateOrchestrator\Schedule Maintenance Work', '\Microsoft\Windows\UpdateOrchestrator\USO_UxBroker', '\Microsoft\Windows\UpdateOrchestrator\Reboot_AC', '\Microsoft\Windows\UpdateOrchestrator\Reboot_Battery' ) function Set-ManualUpdates($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-ManualUpdates is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Extreme's third-party sweep (1.88.0). On a home laptop most of what runs past Windows' own share is other companies' services that start with Windows: the graphics driver's extras, the PC maker's agents, audio and touchpad helpers, RGB and peripheral suites, launchers' and updaters' services, each with a process or two and often a tray program started by it at every sign-in. Each goes to on-demand: its program still starts it when opened, and anything that needs it asks for it. Nothing is stopped now (the restart applies it, so the touchpad or the sound never drops out mid-run), each is recorded, undo puts it back, and keep.ps1 sets it again after an update resets it. Left alone, by name, company or folder: anti-cheat, the antivirus Windows knows about, the graphics driver's core service and content protection, fan and thermal control, the touchpad and pen, Bluetooth, the disk controller's storage service, VPNs and remote-desktop tools, virtual machines and cloud agents, the filming helper of the build machine, and on a laptop the speaker tuning (Waves, Dolby, Nahimic, Realtek's console, B&O) and the maker's hotkey and battery services named in the keep list. #> $script:ThirdPartyKeep = '(?i)vanguard|\bvgc\b|easyanticheat|battleye|\bbeservice|faceit|eaanticheat|anti-?cheat|punkbuster|pnkbstr|nprotect|xigncode|gameguard|mhyprot|' + 'nvdisplay\.container|display container ls|amd external events|atiesrxx|\bhdcp|cphs|\bheci|cplsp|content protection|' + 'dynamic tuning|\besif|\bdptf|innovation platform|\bipfsvc|intelligent thermal|litssvc|\bpmf\b|power management framework|thermal|fan control|' + 'synaptics|syntp|\belan|\betd|\balps\b|apntex|precision touch|wacom|wtablet|\bpen\b|bluetooth|ibtsiva|' + 'rapid storage|\birst|iastor|rstmw|optane|' + 'vpn|wireguard|openvpn|tailscale|zerotier|nordvpn|nordsec|expressvpn|protonvpn|mullvad|surfshark|cloudflare warp|anyconnect|globalprotect|forticlient|pulse secure|' + 'teamviewer|anydesk|parsec|rustdesk|sunshine|splashtop|chrome remote|' + 'vmware|virtualbox|\bvbox|docker|\bwsl\b|qemu|virtio|spice|hyper-v|azure|rdagent|\bhimds|gcarcservice|actions\.runner|github' $script:LaptopKeep = '(?i)waves|maxxaudio|dolby|nahimic|rtkaudio|realtek audio|b&o|bang ?& ?olufsen|harman|dts |hotkey|fn ?and ?function|\batk\b|asusoptimization|imcontroller|omen|battery|charge|power manager' function Get-ServiceExe([string]$pathName) { $t = "$pathName".Trim() if (-not $t) { return '' } if ($t.StartsWith('"')) { $e = $t.IndexOf('"', 1); if ($e -gt 1) { return $t.Substring(1, $e - 1) } } $i = $t.ToLower().IndexOf('.exe') if ($i -gt 0) { return $t.Substring(0, $i + 4) } return ($t -split ' ')[0] } function Cut-ThirdParty($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-ThirdParty is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Lenovo Vantage at rest (1.90.0). A home Lenovo laptop's after-restart list showed Vantage, the System Interface Foundation that hosts its plugins, and Lenovo's telemetry client as twelve processes and nearly three hundred threads. On a Lenovo whose performance modes (Fn+Q) run through its own thermal service (LITSSVC or LNBITSSvc) and whose Fn keys run through its own hotkey service, Vantage is not needed at rest: the battery limit (conservation mode) is kept by the laptop itself once set. Those services live in the Windows folder, where the third-party sweep never looks, so they are named here: set to start when asked (nothing is stopped mid-run), their scheduled tasks off, each recorded and undone, and keep.ps1 sets them again after an update. On a desktop the Vantage app is already gone in Extreme. The trade: Vantage opens with less until its services are started (one line, in the report), or undo-last. A Lenovo laptop without those two services of its own keeps Vantage as before. #> function Test-LenovoOwnControls { $thermal = @(Get-Service -Name 'LITSSVC', 'LNBITSSvc' -ErrorAction SilentlyContinue) $hotkeys = @(Get-Service -Name 'LenovoUtility*', 'LenovoFnAndFunctionKeys' -ErrorAction SilentlyContinue) return [bool]($thermal.Count -and $hotkeys.Count) } function Cut-LenovoHub($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-LenovoHub is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Cuts you choose (1.91.0). Past what the tune decides for itself, a home PC keeps a handful of processes that belong to things some people use and others never do: Office's updater, the laptop maker's sound helpers, AMD's display-event helper, network device discovery, Windows' diagnostics, printing. Each is a box of its own in the app, off unless ticked, with what it does, the risk and what it saves written under it; the app's Info tab and the report say the same, from this one list. Each is set to start on demand or off, recorded with its reason and undone like the rest, and keep.ps1 sets it again after an update. Unticked in a later run from the app (-RestoreUncut), it goes back to how it was before it was cut. Nothing here touches Windows Security, the firewall or an antivirus. #> $script:ChoiceCuts = @( @{ id = 'Office'; label = 'Office''s background service'; svc = '^ClickToRunSvc$'; path = '\\OfficeClickToRun\.exe'; procs = @('OfficeClickToRun'); start = 'Manual' what = 'Microsoft Office keeps its Click-to-Run service (OfficeClickToRun) running all day to fetch and stage Office updates. This sets it to start when you open Word, Excel, Outlook or another Office app instead of with Windows.' risk = 'Office updates itself only while one of its apps is open, and the first Office app you open after a restart takes a second or two longer. If an Office app ever will not open, untick this and run again.' gain = 'One process fewer at rest (about 20 threads and 50 to 100 MB of memory) on a PC with Office installed.' } @{ id = 'Dolby'; label = 'Dolby''s sound service'; svc = '^(DolbyDAXAPI|DolbyDAX2API|DAX3API)$'; path = '\\(dax3api|dolbydax2api)\.exe'; procs = @('dax3api', 'DolbyDAX2API'); start = 'Manual' what = 'The Dolby Audio or Dolby Atmos service a laptop maker adds (dax3api) applies Dolby''s sound profile to the speakers and serves the Dolby app. This sets it to start only when something asks for it.' risk = 'The laptop''s own speakers can sound flatter or quieter without Dolby''s profile, and the Dolby app''s settings stop applying. Sound itself keeps working and headphones change little. Listen after the restart, and untick it and run again if you miss it.' gain = 'Two processes fewer at rest (about 20 threads).' } @{ id = 'Realtek'; label = 'Realtek''s audio console helper, on a laptop too'; svc = '^RtkAudioUniversalService$'; path = '\\RtkAudUService64\.exe'; procs = @('RtkAudUService64'); start = 'Manual' what = 'Realtek''s helper (RtkAudUService64) serves the Realtek Audio Console app and its plug-in pop-up. Extreme already sets it to on demand on a desktop; on a laptop the tune keeps it unless you tick this.' risk = 'The Realtek Audio Console cannot change settings, and the pop-up when you plug something in stops. Sound keeps working through the driver. A few laptops tune their speakers through it: listen after the restart.' gain = 'Two processes fewer at rest (30 to 40 threads).' } @{ id = 'Nahimic'; label = 'Nahimic''s sound effects'; svc = '^Nahimic'; path = '\\Nahimic[^\\]*\.exe'; procs = @('NahimicService', 'NahimicSvc64', 'NahimicSvc32', 'Nahimic3'); start = 'Manual' what = 'Nahimic (by A-Volute, on many MSI, Lenovo Legion, Dell and ASRock PCs) runs a service and a helper all day for its sound effects: virtual surround, the sound tracker, voice clean-up. Extreme already sets it to on demand on a desktop; on a laptop the tune keeps it unless you tick this.' risk = 'Nahimic''s effects, its virtual surround and its app''s settings stop applying, and the speakers may sound a little different. Sound itself keeps working through the audio driver. Some games have stuttered or crashed with Nahimic running, which this also removes. Untick it and run again to have it back.' gain = 'One to three processes fewer at rest; the box says how many on this PC.' } @{ id = 'Waves'; label = 'Waves MaxxAudio'; svc = '^WavesSysSvc'; path = '\\Waves(Sys)?Svc64\.exe'; procs = @('WavesSysSvc64', 'WavesSvc64'); start = 'Manual' what = 'Waves MaxxAudio (on many Dell and some ASUS and MSI laptops) runs a service and a helper all day to tune the speakers and show the "what did you plug in" pop-up for the headset jack. Extreme already sets it to on demand on a desktop; on a laptop the tune keeps it unless you tick this.' risk = 'The speakers may sound flatter without MaxxAudio''s tuning, its app stops applying settings, and the headset-jack pop-up stops: the jack keeps working, though a headset microphone may need picking once in Sound settings. Sound itself keeps working. Listen after the restart.' gain = 'Two processes fewer at rest.' } @{ id = 'Dts'; label = 'DTS sound'; svc = '^DtsApo'; path = '\\DtsApo\d*Service\.exe'; procs = @('DtsApo4Service', 'DTSAPO3Service'); start = 'Manual' what = 'DTS:X Ultra or DTS Audio (on many HP, Acer, Lenovo and ASUS laptops) runs a service all day for its speaker tuning and the DTS app. Extreme already sets it to on demand on a desktop; on a laptop the tune keeps it unless you tick this.' risk = 'The speakers may sound flatter or quieter without DTS''s tuning and the DTS app stops applying its settings. Sound itself keeps working. Listen after the restart, and untick it and run again if you miss it.' gain = 'One process fewer at rest.' } @{ id = 'AmdEvents'; label = 'AMD External Events Utility'; svc = '^AMD External Events Utility$'; path = '\\atiesrxx\.exe'; procs = @('atiesrxx', 'atieclxx'); start = 'Manual' what = 'AMD''s graphics driver runs this (atiesrxx and atieclxx) to catch display events: a screen plugged in, a laptop lid closing, a display hotkey.' risk = 'On some AMD PCs FreeSync stops working in some games, the brightness keys of some AMD laptops stop, and a second screen may need Windows + P to come on. Games and the driver itself keep working. Leave it if you use FreeSync.' gain = 'Two processes fewer at rest (about 25 threads).' } @{ id = 'Discovery'; label = 'Network device discovery'; svc = '^(SSDPSRV|upnphost)$'; path = ''; procs = @(); start = 'Disabled' what = 'Two Windows services (SSDP Discovery and UPnP Device Host) listen for smart TVs, media players and some printers on your network. This switches both off.' risk = 'Cast to device stops finding TVs and media players, and some network printers and scanners stop showing up by themselves. A few older programs and some VPNs that open router ports through Windows'' own UPnP cannot; games do it themselves.' gain = 'One service host fewer when they run in one of their own; the box says what it saves on this PC.' } @{ id = 'Diagnostics'; label = 'Windows'' diagnostic services'; svc = '^(DPS|WdiServiceHost|WdiSystemHost)$'; path = ''; procs = @(); start = 'Disabled' what = 'The Diagnostic Policy Service and its two hosts watch for problems for Windows'' troubleshooters and count each app''s network and battery use. This switches all three off.' risk = 'Windows'' troubleshooters (Get Help, network diagnose) cannot run, Settings stops counting data and battery use per app, and Task Manager''s App history stays empty. Nothing a game uses.' gain = 'One service host fewer when they run in one of their own; the box says what it saves on this PC.' } @{ id = 'Printing'; label = 'Printing, even with a printer installed'; svc = '^Spooler$'; path = ''; procs = @('spoolsv'); start = 'Disabled' what = 'The Print Spooler (spoolsv) handles every print job, Microsoft Print to PDF included. The tune already switches it off where no printer is installed; this switches it off even with one.' risk = 'Nothing prints and Print to PDF stops until you untick this and run again. On the plus side, the spooler has had serious security holes (PrintNightmare); switched off, they are closed.' gain = 'One process fewer at rest (about 10 threads).' } @{ id = 'FileSharing'; label = 'Opening other PCs'' shared folders'; svc = '^LanmanWorkstation$'; path = ''; procs = @(); start = 'Manual' what = 'The Workstation service is Windows'' client for folders shared by other PCs: \\name\folder in File Explorer, mapped network drives, a NAS by its name. It runs all day in a service host of its own. This sets it to start only when something asks for it.' risk = 'Shared folders on other PCs, mapped network drives, a NAS by name and some network printers stop opening until you untick this and run again. The internet, Steam, games, Discord and streaming never use it.' gain = 'One service host fewer at rest; the box says what it saves on this PC.' } @{ id = 'Storage'; label = 'Windows'' storage service'; svc = '^StorSvc$'; path = ''; procs = @(); start = 'Disabled' what = 'The storage service (StorSvc) looks after Settings > System > Storage, Storage Sense and where the Microsoft Store puts apps. Windows starts it at every sign-in and it stays, in a service host of its own. This turns it off.' risk = 'Microsoft Store and Xbox app (Game Pass) installs and updates can fail, and Settings > System > Storage and Storage Sense stop working, until you untick this and run again. Steam, Epic, Battle.net and the other launchers do not use it. If a Windows update will not install, untick this first.' gain = 'One service host fewer at rest; the box says what it saves on this PC.' } @{ id = 'NvidiaPanel'; label = 'NVIDIA''s control panel helper'; svc = '^NVDisplay\.ContainerLocalSystem$'; path = '\\NVDisplay\.Container\.exe'; procs = @('NVDisplay.Container'); start = 'Manual' what = 'NVIDIA''s display driver runs a helper (NVDisplay.Container, two processes) for the NVIDIA Control Panel, its tray icon and the colour settings it applies at sign-in. This sets it to start only when something asks for it. The driver itself, G-SYNC and the 3D settings of your games keep working.' risk = 'The NVIDIA Control Panel does not open (it says NVIDIA display settings are not available), and digital vibrance and the other colour settings made there stop being applied at sign-in, until you untick this and run again. Settings you already made in it for games stay. Leave it if you use digital vibrance or change NVIDIA settings often.' gain = 'Two processes fewer at rest on a PC with an NVIDIA card.' } ) # The after-restart list and the report name the box for what is still running. Format-RunningGroups also travels # alone into the after-restart task's file, so this goes as plain text: label~service pattern~process names. $script:CutHints = @($script:ChoiceCuts | ForEach-Object { '{0}~{1}~{2}' -f $_.label, $_.svc, ((@($_.procs) | ForEach-Object { "$_".ToLower() }) -join ',') }) function Test-ChoiceService($c, $w) { return [bool](("$($w.Name)" -match $c.svc) -or ($c.path -and ("$($w.PathName)" -match $c.path))) } # The names given to -Cut, matched to the list without regard to case; a name it does not know is said, not guessed at. function Get-ChosenCuts([switch]$Quiet) { $ids = @($script:ChoiceCuts | ForEach-Object { $_.id }) $out = @() foreach ($n in @($script:CutNames | ForEach-Object { "$_" -split '[\s,]+' } | Where-Object { $_ })) { $hit = @($ids | Where-Object { $_ -ieq $n }) if ($hit.Count) { $out += $hit[0] } elseif (-not $Quiet) { Warn ("-Cut {0}: there is no cut by that name. The names: {1}" -f $n, ($ids -join ', ')) } } return @($out | Select-Object -Unique) } <# The chosen cuts still in place, by service: the newest record of each service across the runs still on record and this run so far, where that record is a chosen cut's. A run undone, or a service put back since, drops out. #> function Get-ChoiceRecords { $entries = @() foreach ($f in @(Get-ChildItem (Join-Path $script:Root 'undo') -Filter 'changes-20*.json' -ErrorAction SilentlyContinue | Sort-Object Name)) { try { $entries += @(Get-Content $f.FullName -Raw | ConvertFrom-Json | ForEach-Object { $_ }) } catch { } } $entries += @($script:Changes) $last = @{} foreach ($c in $entries) { if ("$($c.type)" -eq 'service') { $last["$($c.name)"] = $c } } $live = @{} foreach ($k in @($last.Keys)) { if ("$($last[$k].choice)") { $live[$k] = $last[$k] } } return $live } # Before the rest of the run: a cut chosen in an earlier run whose box is unticked now goes back to how it was, so the # standard tune then treats it as it would any other service (a printer's spooler stays, discovery goes to on demand). function Restore-ChoiceCuts { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Restore-ChoiceCuts is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # After Extreme: each chosen cut, with what it does, the risk and what it saves written into the log and the report. function Set-ChoiceCuts($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-ChoiceCuts is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# For the app, before anything runs: each choice as it stands on this PC. Found or not; already off or on demand; cut by an earlier run's choice; and what it would take away now: its own processes by name, plus a service host whose running services are all its own (a host it shares with other services stays, so that saves nothing here). #> function Get-ChoiceView { $wmi = @(Get-CimInstance Win32_Service -ErrorAction SilentlyContinue) $procs = @(Get-Process -ErrorAction SilentlyContinue) $byPid = @{} foreach ($w in $wmi) { if ($w.State -eq 'Running' -and $w.ProcessId) { $k = [int]$w.ProcessId; if (-not $byPid.ContainsKey($k)) { $byPid[$k] = New-Object System.Collections.ArrayList }; [void]$byPid[$k].Add([string]$w.Name) } } $live = Get-ChoiceRecords foreach ($c in $script:ChoiceCuts) { $mine = @($wmi | Where-Object { Test-ChoiceService $c $_ }) $names = @($mine | ForEach-Object { [string]$_.Name }) $pids = @{}; $shared = $false foreach ($w in $mine) { if ($w.State -ne 'Running' -or -not $w.ProcessId) { continue } $others = @($byPid[[int]$w.ProcessId] | Where-Object { $names -notcontains $_ }) if ($others.Count) { $shared = $true } else { $pids[[int]$w.ProcessId] = $true } } foreach ($p in $procs) { if (@($c.procs) -contains $p.ProcessName) { $pids[[int]$p.Id] = $true } } $notYet = @($mine | Where-Object { if ($c.start -eq 'Disabled') { $_.StartMode -ne 'Disabled' } else { $_.StartMode -eq 'Auto' } }) @{ id = $c.id; found = [bool]$mine.Count; running = $pids.Count; shared = [bool]($shared -and -not $pids.Count); off = [bool]($mine.Count -and -not $notYet.Count); chosen = [bool](@($names | Where-Object { $live.ContainsKey($_) }).Count) } } } # The Info tab's part on these, written from the same list as the boxes, so the two never say different things. function Get-ChoiceXaml { $x = { param($t) [System.Security.SecurityElement]::Escape([string]$t) } $sb = New-Object System.Text.StringBuilder foreach ($c in $script:ChoiceCuts) { [void]$sb.Append('' + (& $x $c.label) + '') foreach ($row in @(@('What it does', $c.what), @('The risk', $c.risk), @('What you get', $c.gain))) { [void]$sb.Append('' + $row[0] + ': ' + (& $x $row[1]) + '') } } return $sb.ToString() } <# The browsers' background update tasks (Edge on every Windows PC, Chrome on most): time-triggered and Microsoft- or Google-signed, so neither sweep above catches them, yet MicrosoftEdgeUpdate.exe sits at rest on a real home PC (the take-13 at-rest list). Off in Extreme, recorded and undone. The browser still updates when it is opened and Windows Update still delivers Edge, so nothing is left unpatched for long; a gaming PC does not need the updater running in the background between sessions. #> function Cut-UpdaterTasks { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-UpdaterTasks is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } <# Other companies' scheduled tasks that start a program at every sign-in or boot: the tray programs and "helpers" (the PC maker's, the graphics suite's, launchers') that Task Manager's startup list never shows. Switched off, each recorded; a task that runs a script (PowerShell, cmd, wscript) is somebody's own and is left alone, and so is anything the keep list above names. Updaters' own tasks were already put on demand by every run since 1.82.0. #> function Cut-VendorTasks { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Cut-VendorTasks is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # service hosts, grouped (every run since 1.85.0; Extreme only before) # --------------------------------------------------------------------------- <# On a PC with more than 3.5 GB of RAM Windows gives every service a svchost.exe of its own: most of the svchost lines in Task Manager. Grouped, the restart film went from 95 to 78 three minutes after the sign-in (services share a host only with others of their own group, so about twenty go, not every one). A split threshold above the installed RAM puts them back into shared hosts, the way Windows ran until 2017 and still runs on PCs with less memory. The cost: a service that crashes takes the others in its host down with it until Windows starts them again (most restart themselves within a minute). From 1.85.0 to 1.91.0 eight services kept a host of their own (sound, sound devices, the network address, name lookups, Wi-Fi, Bluetooth, WMI, Windows Update): up to eight processes at rest, six on a home laptop's after-restart list. Since 1.92.0 they share their group's host as every other service does, which is how Windows itself runs them on every PC with less than 3.5 GB (Microsoft's own tested arrangement; WMI's providers, the part other companies' tools crash, run in WmiPrvSE.exe of their own either way). A PC where an earlier run set them apart gets Windows' own setting back (the Retired list). -NoGroup leaves the hosts as Windows splits them. #> $script:OwnHost = @() $script:SplitDefaultKb = 3670016 # Windows' own threshold: 3.5 GB function Group-ServiceHosts($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Group-ServiceHosts is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # memory integrity (opt-in) # --------------------------------------------------------------------------- function Set-Vbs($m) { throw 'This is the free look''s copy of OmniDx Tune: it only reads, and Set-Vbs is not in it. The tune itself comes with a key: irm omnidx.net/go.ps1 | iex' } # --------------------------------------------------------------------------- # the BIOS checklist, for this board # --------------------------------------------------------------------------- function Get-BiosChecklist($m) { $v = "$($m.boardVendor) $($m.bios)".ToLower() $keyHint = if ($v -match 'asus|asustek') { 'Del (or F2) while the ROG / ASUS logo shows' } elseif ($v -match 'msi|micro-star') { 'Del while the MSI logo shows' } elseif ($v -match 'gigabyte|aorus') { 'Del while the logo shows' } elseif ($v -match 'asrock') { 'F2 or Del while the logo shows' } elseif ($v -match 'dell|alienware') { 'F2 while the Dell logo shows' } elseif ($v -match 'hp|hewlett|omen') { 'F10 while the HP logo shows' } elseif ($v -match 'lenovo|legion') { 'F2 (or the Novo button) while the Lenovo logo shows' } elseif ($v -match 'acer|predator') { 'F2 while the Acer logo shows' } else { 'Del or F2 while the logo shows' } $where = if ($v -match 'asus') { 'Advanced Mode (F7) > Ai Tweaker for memory, Advanced > CPU Configuration, Advanced > PCI Subsystem Settings, Boot > CSM, Advanced > Trusted Computing' } elseif ($v -match 'msi') { 'Advanced Mode (F7) > OC for memory and PBO, Settings > Advanced > PCI Subsystem Settings, Settings > Boot, Settings > Security > Trusted Computing' } elseif ($v -match 'gigabyte|aorus') { 'Tweaker for memory and PBO, Settings > IO Ports for Above 4G / Re-Size BAR, Boot > CSM, Settings > Miscellaneous > Trusted Computing' } elseif ($v -match 'asrock') { 'OC Tweaker for memory, Advanced > CPU Configuration, Advanced > PCI Configuration, Boot > CSM, Security > Intel Platform Trust / AMD fTPM' } else { 'the Advanced / Overclocking pages for memory, the PCI or Chipset page for Above 4G / Re-Size BAR, the Boot page for CSM, the Security page for TPM and Secure Boot' } $mem = if ($m.cpuVendor -eq 'AMD') { 'EXPO (or DOCP / A-XMP on older boards)' } else { 'XMP' } $lines = @( "BIOS checklist for: $($m.board) ($($m.bios))", "CPU $($m.cpu) | GPU $($m.gpu) | $($m.ramGb) GB RAM", "", "How to get in: restart, then press $keyHint.", "Where things are: $where.", "", $(if ($m.laptop -and -not $m.ramSlow) { "1. Memory profile: laptops set the memory speed themselves and most hide the option; if yours shows $mem, enable it, otherwise there is nothing to do here." } elseif ($m.ramSlow) { "1. Memory profile: enable $mem. CONFIRMED OFF on this PC: the RAM runs at $($m.ramNow) MT/s and is rated for $($m.ramRated). This is the single biggest free gain on any PC. Pick the profile matching the speed printed on the sticks$(if ($m.laptop) { ' (on a laptop the option, when there is one, is usually under Advanced > Memory)' })." } elseif ($m.ramRated -and $m.ramNow) { "1. Memory profile: $mem looks to be on already ($($m.ramNow) MT/s of $($m.ramRated) rated). Check it stayed on after any BIOS update or CMOS reset." } else { "1. Memory profile: enable $mem. Your RAM is running at the slow default until you do; this is the single biggest free gain on any PC. Pick the profile matching the speed printed on the sticks." }), $(if ($m.rebar -eq 'on') { "2. Re-Size BAR: already ON here (NVIDIA's own tool shows the full window into the card's memory). Leave Above 4G Decoding and Re-Size BAR as they are." } else { "2. Re-Size BAR / Smart Access Memory: set Above 4G Decoding = Enabled, then Re-Size BAR Support = Auto/Enabled. Needs CSM off (next line)." + $(if ($m.rebar -eq 'off') { " CONFIRMED OFF on this PC (NVIDIA's own tool shows the old 256 MB window)." } else { '' }) + $(if ($m.gpu -match 'Arc') { " On an Intel Arc card this one is not optional: Intel lists it as required, and without it the card loses a large share of its speed." } else { " Worth 5-15% in many games on RTX 30/40/50 and RX 6000+ (NVIDIA turns it on game by game; AMD calls it Smart Access Memory)." }) }), $(if ($m.uefi) { "3. CSM (Compatibility Support Module): Disabled. You already boot UEFI, so nothing depends on it, and Re-Size BAR needs it off." } else { "3. CSM: you are booting in legacy mode, so leave CSM on for now. Converting to UEFI (mbr2gpt) first is a separate job; Re-Size BAR will wait until then." }), $(if ($m.secureBoot) { "4. Secure Boot: already on. Leave it on (VALORANT, Fortnite's anti-cheat and Windows 11 all expect it)." } else { "4. Secure Boot: Enabled. Windows 11 and Vanguard expect it; on 10 it costs nothing. Set OS Type / Secure Boot Mode to Windows UEFI if asked." }), $(if ($m.tpm) { "5. TPM: present. Leave fTPM / PTT enabled." } else { "5. TPM: not detected. Enable AMD fTPM (AMD) or Intel PTT (Intel) under Security / Trusted Computing. VALORANT on Windows 11 will not run without it." }), $(if ($m.laptop) { "6. Power mode: the maker's app's Performance / Turbo mode raises the CPU and GPU power limits the BIOS does not expose; use it on mains for games and Balanced on battery. Windows' own power mode slider (Settings > System > Power) at Best performance on mains; the OmniDx plan already sets that." } elseif ($m.cpuVendor -eq 'AMD') { "6. Precision Boost Overdrive: Enabled (or Advanced with the Curve Optimizer at a modest negative offset like -15 all-core if you know your cooler). Never a positive voltage offset." } else { "6. Intel: leave MultiCore Enhancement at Auto; set the long and short power limits (PL1/PL2) to the chip's rated maximum if the board runs them lower. Do not touch voltages." }), "7. Global C-states / package C-states: Auto is fine. Disabling them buys nothing measurable in games and raises idle heat.", ("8. Fast boot: Fast Boot = Enabled, Full Screen Logo / Boot Logo = Disabled, and Windows Boot Manager first in the boot order (a USB stick or the network tried first adds seconds every start)." + $(if ($m.boot -and $m.boot.fwMs) { " Measured on this PC: the motherboard took {0:0.0} s before Windows started last time{1}." -f ($m.boot.fwMs / 1000), $(if ($m.boot.fwMs -lt 10000) { ', which is already quick' } else { '' }) } else { '' }) + $(if ($m.cpuVendor -eq 'AMD' -and $m.ddr5 -and -not $m.laptop) { " AM5 with DDR5: Memory Context Restore = Enabled (AMD CBS > UMC Common Options > DDR Options, or the board's memory page; leave Power Down Enable on with it). Without it the board trains the memory again at every start, often 20 to 60 seconds; with it, the training is reused. If the PC ever fails to start or crashes after a memory change, turn it back off." } elseif ($m.cpuVendor -ne 'AMD' -and $m.ddr5 -and -not $m.laptop) { " Intel with DDR5: Memory Fast Boot (MRC Fast Boot) = Enabled, so the board reuses its memory training instead of redoing it at every start." } else { '' })), $(if ($m.laptop) { "9. Fans: the BIOS rarely offers a curve on a laptop; the maker's app does (Armoury Crate, Legion Vantage, Omen Gaming Hub, Predator Sense, Dragon Center): set the performance or turbo fan mode for games, and raise the back of the laptop so the intakes breathe. Sustained boost needs airflow more than anything." } else { "9. Fan curves: set the CPU fan to reach 100% by 80 C and the case fans to a steady ramp. Sustained boost needs airflow more than anything." }), $(if ($m.laptop) { "10. The GPU switch: in the maker's app (or the BIOS as Hybrid / Discrete / MUX), set Discrete or Ultimate for games so the display is driven by the $($m.gpu) directly rather than through the integrated one; Hybrid is for battery life. Windows > Settings > System > Display > Graphics: set each game to High performance." } elseif ($m.wifiLive) { "10. Onboard devices you do not use: serial port, onboard audio if you use USB audio, RGB controllers. Keep Wi-Fi and Bluetooth on: Wi-Fi is your connection." } else { "10. Onboard devices you do not use: serial port, Wi-Fi/Bluetooth if wired, onboard audio if you use USB audio, RGB controllers. Each one removed is an interrupt source gone." }), "11. HPET: leave at default. The 'disable HPET' tweak is from 2013 and hurts more than it helps on modern Windows.", $(if ($m.riot -or $m.faceit) { "12. Virtualisation (SVM / VT-x) and IOMMU (AMD-Vi / VT-d): both Enabled. VALORANT's Vanguard and FACEIT require IOMMU with memory integrity since 2026" + $(if ($m.iommu) { "; IOMMU is on here already, so leave it." } else { "; IOMMU is OFF on this PC, and this is the line that matters." }) + " AMD boards: SVM under CPU Configuration, IOMMU under AMD CBS > NBIO. Intel boards: VT-x under CPU Configuration, VT-d under System Agent Configuration." } else { "12. Virtualisation (SVM / VT-x): leave on if you use WSL, Docker, an emulator, or play VALORANT or on FACEIT (their anti-cheat needs it, with IOMMU); otherwise off saves a little scheduling overhead." }), $(if ($Extreme) { "" } else { $null }), $(if ($Extreme) { "EXTREME - only with a cooler you trust and a memory test to hand. Each of these is what the people tuning for a living set after the list above; none of them is free." } else { $null }), $(if ($Extreme) { " E1. Global C-states: Disabled. Steadier 1% lows on some boards; more idle heat and power. Try it, measure it, keep it only if the lows improve." } else { $null }), $(if ($Extreme) { " E2. Spread Spectrum (CPU and PCIe): Disabled. A hair of clock stability for no emissions margin you will ever measure at home." } else { $null }), $(if ($Extreme -and $m.gpuVendor -ne 'Intel' -and -not $m.laptop) { " E3. Integrated graphics: Disabled, since the $($m.gpu) does the work. Skip this if you use Quick Sync for recording." } elseif ($Extreme -and $m.laptop) { " E3. Integrated graphics stays: a laptop needs it for battery life and often for the screen itself; the GPU switch in item 10 is the laptop version of this." } else { $null }), $(if ($Extreme -and -not $m.laptop) { " E4. Onboard devices you do not use: the second network port, the serial header, Wake-on-LAN, Bluetooth if you never pair anything, the audio codec if you use USB or a DAC. Each one is a driver and an interrupt fewer." } else { $null }), $(if ($Extreme) { " E5. ErP / EuP: Disabled, so USB keeps power for the mouse and keyboard to wake the PC; Enabled only if you want the lowest off-state draw." } else { $null }), $(if ($Extreme) { " E6. PCIe link speed for the graphics slot: set to the card's generation (Gen 4 or Gen 5) instead of Auto, which stops the link renegotiating under load on a few boards." } else { $null }), $(if ($Extreme -and $m.laptop) { " E7. Undervolting: where the maker's app offers a CPU or GPU undervolt (Legion Vantage, some Armoury Crate models, MSI Center), a small one found by testing lowers heat and holds boost longer; the BIOS on most laptops locks it, and that is that." } elseif ($Extreme -and $m.cpuVendor -eq 'AMD') { " E7. PBO limits: Motherboard, with a Curve Optimizer negative offset found by testing (start at -10 all-core, run a stress test an hour, step down). Never a positive voltage offset; never above 1.30 V SoC." } elseif ($Extreme) { " E7. Power limits: PL1 = PL2 at the board's cooling can hold, with the CPU temperature watched under a stress test; an undervolt found by testing (start at -0.050 V) if the board offers one." } else { $null }), $(if ($Extreme -and -not $m.laptop) { " E8. Memory, after the profile: raise tREFI toward 65535 and lower tRFC in steps, testing an hour each; this is where a memory test earns its keep. Stop at the first error and go back one step." } else { $null }), $(if ($Extreme) { " E9. Resizable BAR: on (it is above); with it, also 'Above 4G Decoding' on and CSM off, or the card silently falls back." } else { $null }), "", "After saving: Windows will boot normally. If it does not (rare, usually CSM), go back in and re-enable the last thing you changed.", "Do not update the BIOS unless the vendor's notes mention a fix for your exact problem; a failed flash is the one thing here that cannot be undone from Windows." ) return $lines } # --------------------------------------------------------------------------- # what to do next: five lines at the top of the report # --------------------------------------------------------------------------- function Get-NextSteps($m) { $steps = @('Restart. The services told to stop are still unwinding until you do, and the after-restart count lands in C:\OmniDx\after-restart.txt at your next sign-in, with the boot time of that start next to the one from before the tune.') if ($m.ramSlow) { $steps += "BIOS, item 1: the memory profile is OFF on this PC (the RAM runs at $($m.ramNow) MT/s, rated $($m.ramRated)). That one setting is worth more than the rest of this report." } elseif ($m.laptop) { $steps += 'BIOS: item 10, the GPU switch, is the one that matters on a laptop; then the power mode in the maker''s app.' } else { $steps += 'BIOS: items 1 to 3 (memory profile, Re-Size BAR, CSM). Ten minutes, and the memory profile alone is the biggest free gain any PC has.' } $steps += 'GPU control panel: low latency mode, power management and V-Sync, from the list below. Two minutes.' $steps += 'Launchers and overlays: the list below names the one or two settings in each that matter; the overlays are the usual cause of a stutter that "came from nowhere".' $steps += 'Your frame rate, measured, after the restart: irm omnidx.net/bench.ps1 | iex records a minute of a game with PresentMon (free, changes nothing). Ran it before the tune? It puts the two side by side and draws a card to post.' if (-not (Test-Path (Join-Path $env:ProgramFiles 'OmniDx\Edition\setup.ps1'))) { $steps += 'The whole OmniDx setup, included with your key: OmniDx Edition (the OmniDx look, Search, Browser, Hub, the lean stage; nothing of it touches a game while it runs). irm omnidx.net/edition.ps1 | iex, then 2 for this PC as it is, or 1 for a USB stick and a clean install. In the app it has its own tab, OmniDx OS, with your key already in.' } if (Test-TuneSet 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' 'NoAutoUpdate') { $steps += 'Windows Update is on your say-so: once a month, Settings > Windows Update > Check for updates, install what it finds, restart when it suits you. Defender updates its virus definitions by itself.' } $steps += 'Any time: $env:OMNIDX_MODE=''status''; irm omnidx.net/go.ps1 | iex shows what is still in place; undo is one line, and it is in this report.' return $steps } # --------------------------------------------------------------------------- # GPU control-panel notes # --------------------------------------------------------------------------- function Get-GpuNotes($m) { switch ($m.gpuVendor) { 'NVIDIA' { @('The tune sets each listed game you have in the driver itself (power management prefer maximum performance, low latency mode on, texture filtering high performance) and, for every game, preferred refresh rate = Highest available; NVIDIA Control Panel > Manage 3D settings shows them.', 'By hand, globally: Shader cache size = Driver Default (current drivers keep more than the 10 GB old guides set; 100 GB only if you play many big games and have the room). With a G-SYNC or G-SYNC Compatible monitor: G-SYNC on (Set up G-SYNC), Vertical sync = On in the control panel and Off in every game, and a frame cap 3 under your refresh rate (Reflex in a game caps for you). Without one: Vertical sync = Off.', 'Low latency mode: the tune sets On per game; Ultra only in a game without Reflex and with G-SYNC. In a game with Reflex, Reflex On replaces it (On + Boost also holds the GPU clocks up when the CPU is the limit: a little lower delay for more power).', 'NVIDIA app: in-game overlay off unless you record; Reflex on in every game that has it.', 'The driver: the newest Game Ready driver (NVIDIA app > Drivers, or nvidia.com/drivers), Custom install with Perform a clean installation ticked. Studio drivers are for creative apps; for games, Game Ready. If a new driver brings a problem, the one before it from nvidia.com/drivers is the way back.') + $(if ($m.laptop) { @('Laptop: NVIDIA Control Panel > Manage 3D settings > Preferred graphics processor = High-performance NVIDIA processor, and the same per game under Windows > Settings > System > Display > Graphics. Whisper Mode and Battery Boost off on mains; the frame cap they set is what many "my laptop is stuck at 60" threads are about.') } else { @() }) } 'AMD' { @('AMD Software > Gaming > Graphics (Global): Radeon Anti-Lag = On (Anti-Lag 2 in games that offer it, where it replaces this), Radeon Boost = Off, Radeon Chill = Off, HYPR-RX = Off (it switches Boost on), Radeon Super Resolution = Off (FSR inside the game looks better), Enhanced Sync = Off.', 'Advanced: Wait for Vertical Refresh = Off, unless application specifies; Texture Filtering Quality = Performance; Surface Format Optimization = On; Tessellation Mode = AMD Optimized; Shader Cache = AMD Optimized.', 'Display: AMD FreeSync = On if the monitor has it, then a frame cap 3 under your refresh rate in the game (or Radeon Chill with min and max both at that figure). Performance > Tuning: Smart Access Memory on (needs Re-Size BAR in the BIOS, item 2).', 'Instant Replay and the in-game overlay off unless you record. The driver: the newest AMD Software: Adrenalin from amd.com/support; coming from an old version, tick Factory Reset. Installing with the Minimal or Driver Only option leaves fewer AMD programs running.') + $(if ($m.laptop) { @('Laptop: AMD Software > Gaming > Graphics > Switchable Graphics: set each game to High performance; Radeon Chill off on mains (it is a frame cap dressed as a power saver).') } else { @() }) } 'Intel' { $(if ($m.gpu -match 'Arc') { @('Intel Graphics Software: Low Latency = On (XeLL in games that offer it), Vertical Sync = Off or Application choice, Frame Rate Limiter off unless you cap in the game. Resizable BAR on in the BIOS is required for an Arc card (item 2). The driver: the newest from intel.com/arc; Arc gains more from new drivers than any other card.') } else { @('Intel Graphics Command Center: Endurance Gaming off on mains, Adaptive tessellation on, V-Sync off, Anisotropic filtering per-application. The driver: Intel Driver & Support Assistant (intel.com/support) finds the newest.') }) } default { @('GPU not identified; use the vendor control panel to set low-latency mode on, power management to maximum performance and V-Sync off.') } } } <# The launchers and helper apps on this PC, and the one or two settings in each that matter: the things the script cannot set for you because each keeps its own settings store, so they go in the report instead. #> <# Marks other "optimizer" scripts leave behind: the ones that cost security or updates rather than frames. The tune changes none of them; it names them with the way back, so nobody blames the tune for a hole another tool made, and nobody keeps a hole they never knew about. #> <# The safe way to more clock: an undervolt, not an overclock, and a frame cap under the refresh rate. Words, not changes: voltage lives in the vendor's tools and the BIOS, and the tune never touches either. #> function Get-UndervoltNotes($m) { $n = @() switch ($m.gpuVendor) { 'NVIDIA' { $n += 'GPU undervolt: MSI Afterburner > Ctrl+F opens the voltage/frequency curve. Pick a point at about 0.900 V, drag it up to the clock the card already holds under load (typically 1,900 to 2,000 MHz on 30 and 40 series), flatten everything to its right, apply. Same frames, less heat and fan noise, no thermal throttling. Test with twenty minutes of a demanding game; a crash means the clock at that voltage is too high, drop it 30 MHz.' $n += 'Frame cap: NVIDIA app or Control Panel > Manage 3D settings > Max Frame Rate, three below the refresh rate (141 on 144 Hz, 237 on 240 Hz), with G-Sync on and V-Sync on in the driver and off in the game. Smoothest input you can get without a tearing line.' } 'AMD' { $n += 'GPU undervolt: AMD Software > Performance > Tuning > Custom > Voltage: lower it in 25 mV steps, twenty minutes of a game each step, stop one step above the first crash. On RDNA 3 and 4 the power limit slider can also go to its maximum without extra voltage.' $n += 'Frame cap: AMD Software > Gaming > Radeon Chill on, min and max both three below the refresh rate, with FreeSync on. Chill is a frame cap with a power saver name.' } 'Intel' { $n += 'GPU: Intel Graphics Software > Performance: a modest voltage offset is where Arc gains; the frame limiter is in the same panel, three below the refresh rate.' } default { $n += 'GPU: the vendor tool has a frame limiter; set it three below the refresh rate with adaptive sync on.' } } if ($m.cpuVendor -eq 'AMD') { $n += 'CPU undervolt (Ryzen): BIOS > PBO > Curve Optimizer > all cores, negative 15 to start (negative 30 on most 7000 and 9000 chips), then an hour of a game and a stress test; one step back on any crash or reboot. Lower temperature, higher sustained boost, nothing else changes.' } else { $n += 'CPU (Intel): on 13th and 14th gen, install the latest BIOS first (the microcode that stops the degradation), and leave the voltage alone unless the board exposes an offset; on a K chip a small negative offset is the same idea as Ryzen''s Curve Optimizer.' } $n += 'Never an overclock: it is more heat and more voltage for frames you cannot see, and it is the first thing a crash report points at.' return $n } <# Whether a run of the tune set this value (this run, or one still in place): then it is the tune's, not another tool's. #> function Test-TuneSet([string]$path, [string]$name) { $hit = { param($c) "$($c.type)" -eq 'reg' -and "$($c.path)" -eq $path -and "$($c.name)" -eq $name -and -not $c.removed } foreach ($c in $script:Changes) { if (& $hit $c) { return $true } } foreach ($f in @(Get-ChildItem (Join-Path $script:Root 'undo') -Filter 'changes-20*.json' -ErrorAction SilentlyContinue)) { try { foreach ($c in @(Get-Content $f.FullName -Raw | ConvertFrom-Json | ForEach-Object { $_ })) { if (& $hit $c) { return $true } } } catch { } } return $false } function Get-LeftoverNotes($m) { $notes = @() $v = { param($path, $name) try { return (Get-ItemProperty -Path $path -Name $name -ErrorAction Stop).$name } catch { return $null } } if ((& $v 'HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender' 'DisableAntiSpyware') -eq 1) { $notes += 'Defender is switched off by a policy value another tool set (HKLM\SOFTWARE\Policies\Microsoft\Windows Defender, DisableAntiSpyware = 1). The tune never touches Defender. The way back: delete that value, restart, then Windows Security > Virus and threat protection.' } if ((& $v 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' 'NoAutoUpdate') -eq 1 -and -not (Test-TuneSet 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' 'NoAutoUpdate')) { $notes += 'Automatic Windows updates are off by a policy another tool set (WindowsUpdate\AU, NoAutoUpdate = 1): updates arrive only when you press Check for updates. Delete that value to let them come by themselves.' } $wu = Get-Service -Name wuauserv -ErrorAction SilentlyContinue if ($wu -and "$($wu.StartType)" -eq 'Disabled') { $notes += 'The Windows Update service (wuauserv) is disabled, and not by the tune: Settings > Windows Update cannot work until it is Manual again (services.msc, or in an admin prompt: sc config wuauserv start= demand).' } $fso = & $v 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management' 'FeatureSettingsOverride' if ($fso -ne $null -and (([int64]$fso) -band 3) -ne 0) { $notes += 'The CPU security mitigations (Spectre, Meltdown) are switched off by another tool (Memory Management, FeatureSettingsOverride). A few percent in some benchmarks; a real hole in a browser. The tune leaves it as found. The way back: delete FeatureSettingsOverride and FeatureSettingsOverrideMask there and restart.' } if ((& $v 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' 'EnableSmartScreen') -eq 0) { $notes += 'SmartScreen is off by policy (Windows\System, EnableSmartScreen = 0), so a downloaded installer is not checked. Delete the value to get it back.' } if ((& $v 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' 'EnableLUA') -eq 0) { $notes += 'User Account Control is off (EnableLUA = 0): every program runs with full rights, and Store apps stop opening. Set it to 1 and restart; the tune runs fine with it on.' } if (-not $notes.Count) { $notes += 'Nothing found: no other tool has left Defender, Windows Update, SmartScreen, User Account Control or the CPU mitigations switched off on this PC.' } return $notes } function Get-LauncherNotes($m) { $notes = @() if ($m.streamer.Count) { $notes += 'OBS Studio / Streamlabs: Settings > Output > Encoder: NVIDIA NVENC, AMD AMF or Intel QSV, never x264 on the gaming PC; Settings > Advanced > Process priority: Above normal; capture the game with Game Capture, not Display Capture; Video > Output resolution at the stream size, not the monitor. The tune keeps the camera frame server and Windows Graphics Capture running for it.' } $pf = "$env:ProgramFiles"; $pf86 = "${env:ProgramFiles(x86)}" $has = { param($p) return [bool]($p -and (Test-Path $p)) } $steam = $null; foreach ($k in 'HKLM:\SOFTWARE\WOW6432Node\Valve\Steam', 'HKLM:\SOFTWARE\Valve\Steam') { try { $steam = (Get-ItemProperty $k -ErrorAction Stop).InstallPath; if ($steam) { break } } catch { } } if ($steam) { $notes += 'Steam: Settings > Downloads > "Allow background processing of Vulkan shaders" off (it compiles while you play), "Enable shader pre-caching" on. Settings > In Game > the overlay off unless you use it (a browser process inside every game). Settings > Interface > "Run Steam when my computer starts" off (the tune switched the entry off) and "GPU accelerated rendering in web views" on. Per game: Properties > General > Launch options from the PER GAME list.' if (& $has (Join-Path $steam 'steamapps\common\wallpaper_engine')) { $notes += 'Wallpaper Engine: Settings > Performance > "Other application fullscreen: pause" and "Other application focused: pause", playback FPS 30; a live wallpaper is a game running behind your game.' } } if (& $has (Join-Path $env:ProgramData 'Epic\EpicGamesLauncher')) { $notes += 'Epic Games Launcher: Settings > "Run when my computer starts" off, "Enable notifications" off; leave "Throttle downloads" off. The overlay is off by default; keep it so.' } if ((& $has (Join-Path $env:ProgramData 'Battle.net')) -or (& $has (Join-Path $pf86 'Battle.net'))) { $notes += 'Battle.net: Settings > General > "Launch Battle.net when I start my computer" off, "On game launch: exit Battle.net completely" (the client is a browser), "Use browser hardware acceleration" on. Streaming off.' } if (& $has (Join-Path $env:ProgramData 'Riot Games')) { $notes += 'Riot Client: Settings > General > "Open Riot Client on startup" off. Vanguard stays: VALORANT will not run without it, and Vanguard On-Demand (Windows 11 25H2) needs Secure Boot, TPM 2.0, IOMMU and memory integrity on; the tune leaves all four alone (BIOS items 4, 5 and 12 say where).' } if ($m.faceit) { $notes += 'FACEIT: its anti-cheat needs TPM 2.0, Secure Boot, IOMMU and memory integrity on, and Windows 11 from October 2026. The tune leaves all of them alone; the BIOS checklist says where each one is.' } if ((& $has (Join-Path $pf 'Electronic Arts\EA Desktop')) -or (& $has (Join-Path $pf86 'Origin'))) { $notes += 'EA app: Settings > Application > "Automatically launch EA app on startup" off; "In-game overlay" off.' } if (& $has (Join-Path $pf86 'Ubisoft\Ubisoft Game Launcher')) { $notes += 'Ubisoft Connect: Settings > General > "Launch at startup" off, "Enable in-game overlay" off, "Display notifications" off.' } if ((& $has (Join-Path $pf86 'GOG Galaxy')) -or (& $has (Join-Path $pf 'GOG Galaxy'))) { $notes += 'GOG Galaxy: Settings > General > "Launch at startup" off; Game features > overlay off.' } if (& $has (Join-Path $script:LocalAppData 'Discord')) { $notes += 'Discord: the tune set hardware acceleration on and start-with-Windows off. Also Settings > Voice & Video > "H.264 hardware acceleration" on, "OpenH264" off; Settings > Windows Settings > "Minimise to tray" on, so closing it does not quit it mid-call.' } if ((& $has (Join-Path $pf 'NVIDIA Corporation\NVIDIA app')) -or (& $has (Join-Path $pf 'NVIDIA Corporation\NVIDIA GeForce Experience'))) { $notes += 'NVIDIA app: Settings > "In-game overlay" off unless you record or use Reflex analyzer; "Automatically optimise games" off (it picks quality over frames). Drivers > Game Ready, clean install.' } if ((& $has (Join-Path $pf 'AMD\CNext')) -or (& $has (Join-Path $pf 'AMD\RadeonSoftware'))) { $notes += 'AMD Software: Settings > Preferences > "In-game overlay" off unless you record; "Startup" as needed; Performance > Metrics off outside a benchmark.' } if ($m.xboxUsed) { $notes += 'Xbox app: Settings > General > "Auto-start" off (the entry the tune switched off), "Show notifications" off. Game Pass games install under C:\XboxGames and are already in the CPU-priority list where the game is listed.' } if (& $has (Join-Path $script:LocalAppData 'Overwolf')) { $notes += 'Overwolf (and every app built on it: CurseForge, Outplayed, Tracker.gg, Lethal Company mods): the background app most often behind a stutter that "came from nowhere". Close it before a match, or uninstall it and use the browser versions.' } $suites = @() if (& $has (Join-Path $pf 'Corsair\CORSAIR iCUE 5 Software')) { $suites += 'iCUE' } elseif (& $has (Join-Path $pf86 'Corsair\CORSAIR iCUE 4 Software')) { $suites += 'iCUE' } if (& $has (Join-Path $pf86 'Razer\Synapse3')) { $suites += 'Razer Synapse' } elseif (& $has (Join-Path $pf 'Razer\Synapse')) { $suites += 'Razer Synapse' } if (& $has (Join-Path $pf 'LGHUB')) { $suites += 'Logitech G HUB' } if (& $has (Join-Path $pf 'SteelSeries\GG')) { $suites += 'SteelSeries GG' } if ($m.oem -match 'Armoury Crate') { $suites += 'Armoury Crate' } if ($m.oem -match 'MSI Center|Dragon Center') { $suites += 'MSI Center' } if ($suites.Count) { $notes += ("{0}: once your lighting and macros are saved to the device (onboard memory), set it not to start with Windows; each of these runs four to twelve processes and polls the hardware all day. Open it when you want to change something." -f ($suites -join ', ')) } if (-not $notes.Count) { $notes += 'None of the usual launchers, overlays or peripheral suites found. The same notes apply the day you install one; run the command again and they appear here.' } return $notes } # --------------------------------------------------------------------------- # the app: a window around the same script # --------------------------------------------------------------------------- <# Everything the window shows before it runs anything, as one object: the machine, the advice, the startup entries, the process count, the target. -Probe prints it as JSON, which is how the window asks for it from a second PowerShell instance without blocking. #> function Get-Probe { $m = Get-Machine $script:Warnings.Clear() Show-Advice $m $script:Drivers = Get-DriverCheck $m foreach ($n in $script:Drivers.notes) { Warn $n } $entries = @(Get-StartupEntries | Where-Object { $_.on }) @{ version = $script:Version os = $m.os; cpu = $m.cpu; gpu = $m.gpu; ramGb = $m.ramGb; board = $m.board; bios = $m.bios laptop = $m.laptop; nvme = $m.nvme; allSsd = $m.allSsd; refresh = $m.refresh; driverVer = $m.driverVer win = $m.win; build = $m.build; gpuVendor = $m.gpuVendor; sticks = $m.sticks; ramNow = $m.ramNow; ramRated = $m.ramRated uefi = $m.uefi; secureBoot = $m.secureBoot; tpm = $m.tpm; vbs = $m.vbs; server = [bool]($m.os -match 'Server') keeps = @(@($(if ($m.printers) { 'printer' }), $(if ($m.btDevices) { 'Bluetooth' }), $(if ($m.wifi) { 'Wi-Fi' }), $(if ($m.touch) { 'touch' }), $(if ($m.biometric) { 'Windows Hello' }), $(if ($m.vpn) { 'VPN' }), $(if ($m.xboxUsed) { 'Xbox / Game Pass' }), $(if ($m.xboxPad) { 'Xbox controller' }), $(if (@($m.streamer).Count) { 'streaming' })) | Where-Object { $_ }) warnings = @($script:Warnings) startup = @($entries | ForEach-Object { @{ name = $_.name; label = $_.label; wasCut = [bool]$_.wasCut } }) before = (Get-ProcessCount) target = (Get-SafeBar $m) targetExtreme = (Get-SafeBar $m -AsExtreme) lastRun = (Get-LastRun) choices = @(Get-ChoiceView) appsOn = @(Get-InstalledCleanApps) keyBound = $(try { [string](Get-ItemProperty 'HKLM:\SOFTWARE\OmniDx\Tune' -ErrorAction Stop).Key } catch { '' }) timings = $m.timings } } <# The newest summary in C:\OmniDx, for the window's status line. #> function Get-LastRun { $sum = Get-ChildItem $script:Root -Filter 'summary-*.json' -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1 if (-not $sum) { return $null } try { $s = Get-Content $sum.FullName -Raw | ConvertFrom-Json; return @{ stamp = $s.stamp; before = $s.before; after = $s.after; changes = $s.changes; version = $s.version } } catch { return $null } } <# The window. WPF, drawn from XAML held here, so the app is the same one file as the command. The work itself runs in a second PowerShell instance with this script's text, so the window never freezes; its Write-Host lines arrive on the information stream and land in the log as they happen. #> $script:Xaml = @'