[APORTE] [PowerShell] Remove Windows Installer product registration for missing MSI packages

(1/1)

Eleкtro:
El siguiente script desarrollado en Powershell sirve para realizar un tipo de limpieza que CCleaner y limpiadores especializados del registro (como por ejemplo Wise Registry Cleaner) no pueden llevar a cabo:

Limpiar todo rastro, en el registro de Windows, de entradas a instaladores MSI que no se encuentren presentes en el sistema o que simplemente den conflictos pos fallas de instalación o desinstalación.

Es magia pura para solucionar cierto tipo de problemas relacionados con entradas de registro de paquetes MSI huérfanos.

Autor del código original: https://gist.github.com/heaths/77fbe0b44496960fab25c2eb0b9e8475



Código
#Requires -Version 3
 
# https://gist.github.com/heaths/77fbe0b44496960fab25c2eb0b9e8475
 
[CmdletBinding(SupportsShouldProcess = $true)]
param (
   [Parameter(Position = 0, ValueFromPipeline = $true)]
   [ValidateNotNullOrEmpty()]
   [string[]] $ProductCode
)
 
[System.Console]::Title = "Remove Windows Installer product registration for missing MSI packages"
[CultureInfo]::CurrentUICulture = "en-US"
 
try { Set-ExecutionPolicy -ExecutionPolicy "Unrestricted" -Scope "Process" } catch { }
 
$ErrorActionPreference = 'Stop'
[int[]] $translation = 7,6,5,4,3,2,1,0,11,10,9,8,15,14,13,12,17,16,19,18,21,20,23,22,25,24,27,26,29,28,31,30
 
$loc = data {
   ConvertFrom-StringData @'
       Error_Elevation_Required = You must run this script in an elevated command prompt
       Error_64Bit_Required = You must run this in a 64-bit command prompt
       Error_PackageManagement_Required = Please install PackageManagement from http://go.microsoft.com/fwlink/?LinkID=746217
       Process_Remove_Args1 = Remove registration for {0}
       Verbose_Install_MSI = Installing the "MSI" module
       Verbose_Scan_Missing = Scanning for products missing cached packages
       Verbose_Remove_Key_Args1 = Removing key  : {0}
       Verbose_Remove_Value_Args2 = Removing value: {0}\\{1}
       Verbose_Remove_Source_Reg = Removing source registration
       Verbose_Remove_Product_Reg = Removing product registration
       Verbose_Remove_Upgrade_Reg = Removing upgrade registration
       Verbose_Remove_Component_Reg = Removing component registration
       Verbose_Found_Source_Args2 = Cache missing for {0} but found source at {1}
'@
}
 
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object System.Security.Principal.WindowsPrincipal $identity
if (!$principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
   throw $loc.Error_Elevation_Required
}
 
if ([System.Environment]::Is64BitOperatingSystem) {
   if (![System.Environment]::Is64BitProcess) {
       throw $loc.Error_64Bit_Required
   }
}
 
$pack = {
   param (
       [string] $Guid
   )
 
   if (!$Guid) {
       return
   }
 
   $Guid = (New-Object System.Guid $Guid).ToString("N").ToUpperInvariant()
 
   $sb = New-Object System.Text.StringBuilder $translation.Count
   foreach ($i in $translation) {
       $null = $sb.Append($Guid[$i])
   }
 
   $sb.ToString()
}
 
$test = {
   param (
       $Product
   )
 
   if ($Product.PSPath -and ($Product | Test-Path)) {
       return $true
   }
 
   if ($Product.PackageName) {
       $Product | Get-MSISource | ForEach-Object {
           $path = Join-Path $_.Path $Product.PackageName
           if ($path | Test-Path) {
               Write-Host ($loc.Verbose_Found_Source_Args2 -f $Product.ProductCode, $path)
               return $true
           }
       }
   }
 
   $false
}
 
$remove = {
   param (
       [string] $Key
   )
 
   if (Test-Path $Key) {
       Write-Host ($loc.Verbose_Remove_Key_Args1 -f $Key)
       Remove-Item -Recurse -Force $Key
   }
}
 
$removeChild = {
   param (
       [string] $Key,
       [string] $Name
   )
 
   if (Test-Path $Key) {
       Get-ChildItem $Key | ForEach-Object {
           $obj = $_ | Get-ItemProperty
           if ($obj.$Name -ne $null) {
               Write-Host ($loc.Verbose_Remove_Value_Args2 -f $_.Name, $Name)
               Remove-ItemProperty -Force -Name $Name -LiteralPath $_.PSPath
 
               $obj = Get-ItemProperty -LiteralPath $_.PSPath
               if (!$obj) {
                   Write-Host ($loc.Verbose_Remove_Key_Args1 -f $_.Name)
                   Remove-Item -Recurse -Force -LiteralPath $_.PSPath
               }
           }
       }
   }
}
 
if (!$ProductCode) {
   # Install the MSI module if missing.
   if (!(Get-Module -ListAvailable MSI)) {
       Write-Host $loc.Verbose_Install_MSI
 
       # Make sure PackageManagement is installed (comes with WMF 5.0 / Windows 10).
       if (!(Get-Module -ListAvailable PackageManagement)) {
           throw $loc.Error_PackageManagement_Required
       }
 
       Install-Module MSI -Scope CurrentUser -SkipPublisherCheck -Force
   }
 
   Write-Host $loc.Verbose_Scan_Missing
   foreach ($msi in (Get-MSIProductInfo -UserContext Machine)) {
       if (!(&$test $msi)) {
           $ProductCode += $msi.ProductCode
       }
   }
}
 
foreach ($code in $ProductCode) {
   if ($PSCmdlet.ShouldProcess($msi, $loc.Process_Remove_Args1 -f $code)) {
       $packedProductCode = &$pack $code
 
       Write-Host $loc.Verbose_Remove_Source_Reg
       &$remove "Registry::HKCL\SOFTWARE\Classes\Installer\Products\$packedProductCode"
       &$remove "Registry::HKCL\SOFTWARE\Classes\Installer\Features\$packedProductCode"
       &$remove "Registry::HKLM\SOFTWARE\Classes\Installer\Products\$packedProductCode"
       &$remove "Registry::HKLM\SOFTWARE\Classes\Installer\Features\$packedProductCode"
 
       Write-Host $loc.Verbose_Remove_Product_Reg
       &$remove "Registry::HKCL\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\$packedProductCode"
       &$remove "Registry::HKCL\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$code"
       &$remove "Registry::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\$packedProductCode"
       &$remove "Registry::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$code"
       &$remove "Registry::HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$code"
 
       Write-Host $loc.Verbose_Remove_Upgrade_Reg
       &$removeChild "Registry::HKCL\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes" $packedProductCode
       &$removeChild "Registry::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes" $packedProductCode
 
       Write-Host $loc.Verbose_Remove_Component_Reg
       &$removeChild "Registry::HKCL\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" $packedProductCode
       &$removeChild "Registry::HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components" $packedProductCode
 
   }
}
 
Write-Host "Press any key to exit..."
$key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Exit(0)
 
<#
.SYNOPSIS
Removes Windows Installer product registrtation for missing or specified MSIs
.DESCRIPTION
If Windows Installer product registration is corrupt (exit code 1610) or package
sources are missing (exit code 1603, error message 1714; or exit code 1612),
you can use this script in an elevated PowerShell command shell to clean up the
registration is a transactional manner to avoid making machine state worse.
Please note that this should be a last resort and only for those issues above.
The old msizap.exe program was frought with issues and can make matters worse
if not used properly.
.PARAMETER ProductCode
Optional list of ProductCode to clean up; otherwise, ProductCodes are scanned
from products with missing sources.
.EXAMPLE
PS> Unregister-MissingMSIs.ps1
Removes per-machine product registration for products with missing cached MSIs.
.EXAMPLE
PS> Unregister-MissingMSIs.ps1 '{7B88D6BB-A664-4E5A-AB81-C435C8639A4D}'
Remove per-machine product registration for the specified ProductCode only.
#>

Navegación

[0] Índice de Mensajes