A single-file PowerShell tool that reclaims disk space on Windows. Its core removes orphaned .msi/.msp packages from C:\Windows\Installer, and it also runs a few standard cleanup tasks (WinSxS, Windows Update cache, temp folders, Delivery Optimization).
Inspired by the excellent free utility PatchCleaner by HomeDev. This is an independent PowerShell implementation of the same publicly documented idea, run in aggressive mode: it applies no exclude filters, so Adobe/Acrobat orphans are removed too.
This script can free up 60+ GB in the worst-case scenario.
⚠️ Deletion underC:\Windows\Installeris irreversible. Always run with$DryRun = $truefirst and review the output.
C:\Windows\Installer caches the .msi/.msp packages Windows Installer needs to repair, modify or uninstall software. Over time it accumulates packages that no installed product references any more (orphans), which can grow to many GB. This script finds and deletes those orphans, then runs additional cleanup tasks.
| Step | What it cleans | How |
|---|---|---|
| Installer store | Orphaned .msi/.msp in C:\Windows\Installer |
see below |
| WinSxS | Component store / superseded updates | DISM /Online /Cleanup-Image /StartComponentCleanup [/ResetBase] |
| Windows Update cache | C:\Windows\SoftwareDistribution\Download |
stops wuauserv+bits, clears, restarts |
| Temp | %TEMP% and C:\Windows\Temp |
deletes contents (in-use items are skipped) |
| Delivery Optimization | DO download cache | Delete-DeliveryOptimizationCache |
Each run prints initial/final free space and a per-step summary.
The technique for safely removing unused packages from the Windows Installer folder is well documented (see, e.g., raymond.cc). This script implements it as follows:
-
List candidates — enumerate
C:\Windows\Installer\*.mspand*.msi(non-recursive, hidden files included). -
Determine what's in use — query installed products and their applied patches through the Windows Installer automation interface:
WindowsInstaller.Installer.Products→ProductInfo(code, "LocalPackage")Installer.Patches(product)→PatchInfo(code, "LocalPackage")
This enumeration is run through a small helper VBScript executed with
cscript, which writes the referenced package paths to text files that the script reads back. The script keeps the file names of all in-use packages.Why VBScript instead of in-process COM? Enumerating
Installer.Productsdirectly from PowerShell is unreliable (it can return zero products depending on host/bitness). Driving it throughcscriptis consistent and robust. -
Find orphans — a store file is an orphan when its file name is referenced by no in-use package. Matching is by file name, case-insensitive — all packages live in the same folder, so the name is unique, and this avoids path-format pitfalls (8.3/long/case).
-
Delete — clears
ReadOnly/Hiddenattributes and, on an access-denied error, takes ownership (takeown) and grantsEveryone:F(icacls) before retrying the delete.
- If the in-use set cannot be determined (broken VBScript engine, no output), the step aborts and deletes nothing. There is no fall-back to registry guessing, which would either keep far too much or, if empty, risk deleting everything.
- Per-product MSI quirks (e.g. error
80004005onProductInfofor a package whose attributes aren't readable) are tolerated: that product is skipped and the rest proceed.
Stock PatchCleaner ships with an "Acrobat" exclude filter and therefore keeps Adobe orphans. This script applies no exclude filters, so Adobe/Acrobat packages are deleted like any other orphan. If you want to protect a vendor, you can filter orphans by MSI metadata (Author/Title/Subject/digital-signature OU) before deletion.
- Windows with Windows PowerShell 5.1
- Administrator rights (the script auto-elevates)
cscript.exe(always present on Windows)
-
Dry run first — edit the script and set:
$DryRun = $true
Run it and review
Required filesandORPHANED files to delete(count + size). -
Live run — set
$DryRun = $falseand run:powershell -ExecutionPolicy Bypass -File .\Clean-System.ps1
(or right-click → Run with PowerShell; it will prompt for elevation).
$DryRun = $false # simulate only, delete nothing
$CleanWinSxS = $true # DISM component store cleanup
$WinSxSResetBase = $true # add /ResetBase (auto-retries without it on failure)
$CleanWindowsUpdateCache = $true # clear SoftwareDistribution\Download
$CleanTempFiles = $true # clear %TEMP% and C:\Windows\Temp
$CleanDeliveryOptimization = $true # clear Delivery Optimization cache- Irreversible: deleted
.msi/.mspfiles are gone. Future repair, modify or uninstall of an affected product may then ask for the original media. This is inherent to removing installer-cache orphans. DISM /ResetBasecan fail with exit code1168("Element not found") on some systems; the script automatically retries plainStartComponentCleanup. Set$WinSxSResetBase = $falseto skip it entirely.
Inspired by PatchCleaner (HomeDev) — a great, free tool; go support it. This project is an independent implementation and is not affiliated with or endorsed by HomeDev.
Provided as-is, without warranty. You are responsible for what you delete on your own system. Test with $DryRun = $true before any live run.
GPL v3