A Windows installer patch (MSP file) is a package file that contains updates for a certain application and describes which version of an application can be patched. The advantage of an MSP is that it contains only files that then become an MSI. MSPs are generally used for minor releases or small updates. I myself have a tool up my sleeve that can distribute binary differences for extremely small patches (to be released soon).
The patch contains, among other things, a product code and a patch code for the application to be changed. If the product code matches with the installed application, patching is possible. It is practical then to be able to determine this code before an installation in order to optimize a software distribution. I'll show you two solutions how to do so with PowerShell.
How do I find out the product code? One variant is found at Codeplex: MSI Powershell Module.
The disadvantage of this module is that you need to install an MSI for it, which further contains components of the Windows Installer XML. Thus nothing which can easily be packed into a package. Otherwise the MSI Powershell Module is excellent and I recommend it greatly for more complex things. After installing, one can determine the data of an MSI file like so:
get-msicomponentinfo ` | where { $_.Path -like 'C:\Program Files\*\Common7\IDE\devenv.exe'} ` | get-msiproductinfo
A short time ago I looked for another possibility to readout the product code of an MSP without the aforementioned overhead. The COM object “WindowsInstaller.Installer” offers this possibility. The advantage here is that this function can easily be used with an installation in order to determine if a system can be patched. In the near future I'll be incorporating this very thing into our Citrix XenDesktop patcher (http://www.software-virtualisierung.de/download/nitctxpatcher.html).
Readout of the “Display Name” of a patch with PowerShell. Note that not every MSP contains this:
function Get-MSPDisplayName { <# .SYNOPSIS Get the Display Name from an Microsoft Installer Patch MSP .DESCRIPTION Get Display Name from an Microsoft Installer Patch MSP (Andreas Nick 2015) .NOTES $NULL for an error .LINK .RETURNVALUE [String] Display Name .PARAMETER [IO.FileInfo] Path to the msp file #> function Get-MSPDisplayName { param ( [IO.FileInfo] $patchnamepath ) try { $wi = New-Object -com WindowsInstaller.Installer $mspdb = $wi.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $wi, $($patchnamepath.FullName, 32)) $su = $mspdb.GetType().InvokeMember("SummaryInformation", "GetProperty", $Null, $mspdb, $Null) [String] $displayName = $su.GetType().InvokeMember("Property", "GetProperty", $Null, $su, 6) return $displayName } catch { Write-Output $_.Exception.Message return $NULL } }
Readout of the Product Code of a Microsoft Patch (MSP) with PowerShell:
<# .SYNOPSIS Get the Product Code from an Microsoft Installer Patch MSP .DESCRIPTION Get a Product Code from an Microsoft Installer Patch MSP (Andreas Nick 2015) .NOTES $NULL for an error .LINK .RETURNVALUE [String] Product Code .PARAMETER [IO.FileInfo] Path to the msp file #> function Get-MSPProductcode { param ( [IO.FileInfo] $patchnamepath ) try { $wi = New-Object -com WindowsInstaller.Installer $mspdb = $wi.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $wi, $($patchnamepath.FullName, 32)) $su = $mspdb.GetType().InvokeMember("SummaryInformation", "GetProperty", $Null, $mspdb, $Null) #$pc = $su.GetType().InvokeMember("PropertyCount", "GetProperty", $Null, $su, $Null) [String] $productcode = $su.GetType().InvokeMember("Property", "GetProperty", $Null, $su, 7) return $productcode } catch { Write-Output $_.Exception.Message return $NULL } }
Readout of the patch code of an MSP with PowerShell:
<# .SYNOPSIS Get the Patch Code from an Microsoft Installer Patch MSP .DESCRIPTION Get a Patch Code from an Microsoft Installer Patch MSP (Andreas Nick 2015) .NOTES $NULL for an error .LINK .RETURNVALUE [String] Product Code .PARAMETER [IO.FileInfo] Path to the msp file #> function Get-MSPPatchcode { param ( [IO.FileInfo] $patchnamepath ) try { $wi = New-Object -com WindowsInstaller.Installer $mspdb = $wi.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $wi, $($patchnamepath.FullName, 32)) $su = $mspdb.GetType().InvokeMember("SummaryInformation", "GetProperty", $Null, $mspdb, $Null) $pc = $su.GetType().InvokeMember("PropertyCount", "GetProperty", $Null, $su, $Null) #Write-Host $pc [String] $patchcode = $su.GetType().InvokeMember("Property", "GetProperty", $Null, $su, 9) return $patchcode } catch { Write-Output $_.Exception.Message return $NULL } }
Comments