Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dungsaga/ae06fa1b69246dfc81f2b3011b0c5dc1 to your computer and use it in GitHub Desktop.
Save dungsaga/ae06fa1b69246dfc81f2b3011b0c5dc1 to your computer and use it in GitHub Desktop.
Clear-WindowsInstallerPatchInfo; cleanly/safely remove files from c:\windows\installer
#based on
# - http://blogs.msdn.com/b/heaths/archive/2007/01/31/how-to-safely-delete-orphaned-patches.aspx
# - http://www.bryanvine.com/2015/06/powershell-script-cleaning-up.html
# - https://p0w3rsh3ll.wordpress.com/2012/01/10/working-with-the-windowsinstaller-installer-object/
clear-host
function Get-WindowsInstallerPatchInfo {
[CmdletBinding()]
Param ()
$msi = New-Object -ComObject 'WindowsInstaller.Installer'
<# didn't need this; all "methods" were actually properties
$msi | Add-Member -Name 'InvokeMethod' -MemberType ScriptMethod -Value {
$type = $this.GetType();
$index = $args.Count -1 ;
$methodargs=$args[1..$index]
$type.invokeMember($args[0],[System.Reflection.BindingFlags]::InvokeMethod,$null,$this,$methodargs)
}
#>
$msi | Add-Member -Name 'GetProperty' -MemberType ScriptMethod -Value {
$type = $this.gettype();
$index = $args.count -1 ;
$methodargs=$args[1..$index]
$type.invokeMember($args[0],[System.Reflection.BindingFlags]::GetProperty,$null,$this,$methodargs)
}
$products = $msi.GetProperty('Products')
if ($products) {
Write-Verbose "Product Count: $($products.Count)"
foreach ($productCode in $products) {
Write-Verbose "Product: $productCode"
$patches = $msi.GetProperty('Patches',$productCode)
if ($patches) {
foreach ($patchCode in $patches) {
Write-Verbose "Patch: $patchCode"
$location = $msi.GetProperty('PatchInfo', $patchCode, 'LocalPackage')
Write-Verbose "Location: $location"
if ($location) {
(New-Object -TypeName 'PSObject' -Property @{
ProductCode = $productCode
PatchCode = $patchCode
Location = $location
})
}
}
}
}
}
}
function Clear-WindowsInstallerPatchInfo {
[CmdletBinding()]
Param ()
[string]$installDir = Join-Path -Path $env:windir -ChildPath 'Installer'
[PSObject[]]$installerInfo = Get-WindowsInstallerPatchInfo -Verbose
[string[]]$itemsOfInterest = $installerInfo | select-Object -ExpandProperty 'location'
[string[]]$guidsOfInterest = @($installerInfo | select-Object -ExpandProperty 'productCode') + @($installerInfo | select-Object -ExpandProperty 'patchCode')
[PSObject[]]$pathsAndActions = Get-ChildItem -Path $installDir -Recurse | Select-Object -ExpandProperty 'FullName' | ForEach-Object {
[bool]$keepPath = if ($_ -in $itemsOfInterest) {
$true
} else {
[string[]]$guids = [Regex]::Matches($_,'\{[0-9A-F]{8}\-([0-9A-F]{4}\-){3}[0-9A-F]{12}\}') | Select-Object -ExpandProperty value
if ($guids | Where-Object {$_ -in $guidsOfInterest}) {
$true
} else {
$false
}
}
(New-Object -TypeName 'PSObject' -Property @{
Path = $_
Keep = $keepPath
})
}
$pathsAndActions | ForEach-Object {
if ($_.Keep) {
Write-Verbose "Keep $($_.Path)"
} else {
Write-Verbose "Remove $($_.Path)"
Remove-Item -Path $_.Path -Recurse -Force
}
}
}
Clear-WindowsInstallerPatchInfo -Verbose
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment