This project is read-only.

Get-MSIProperty

Topics: Archive - Toolkit Extensions
Oct 7, 2014 at 9:55 PM
Hello all,
This extension retrieves the value of any Property stored in the Property Table of a target MSI file.
Function Get-MSIProperty {
<#
.SYNOPSIS
    Retrieves the value of a property defined in target MSI file Property Table
.DESCRIPTION
    Specify the full path to the MSI and the property name that stores the value to retrieve. Function will return the value if property is found in the MSI.
.EXAMPLE
    Get-MSIProperty "C:\windows\installer\abc123.msi" "ProductCode"
    Returns the ProductCode value from abc123.msi file.
.EXAMPLE
    $MyProductCode = Get-MSIProperty "C:\windows\installer\abc123.msi" "ProductCode"
    Returns the ProductCode value from abc123.msi file and then stores it in $MyProductCode variable.
.PARAMETER MSIFileName
    The full path to the target MSI file.
.PARAMETER MSIPropertyName
    The name of the property to retrieve.
.NOTES
.LINK
    Http://psappdeploytoolkit.codeplex.com
#>
    Param(
        [Parameter(Mandatory = $true)]
        [string] $MSIFileName,
        [string] $MSIPropertyName
    )
    
    $WindowsInstaller = New-Object -com WindowsInstaller.Installer
    $Database = $WindowsInstaller.GetType().InvokeMember(“OpenDatabase”, “InvokeMethod”, $Null, $WindowsInstaller, @($MSIFileName,0))
    $View = $Database.GetType().InvokeMember(“OpenView”, “InvokeMethod”, $Null, $Database, (“SELECT * FROM Property”))
    $View.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $View, $Null)

    $Record = $View.GetType().InvokeMember(“Fetch”, “InvokeMethod”, $Null, $View, $Null)

    while($Record -ne $Null)
    {
        $PropertyName = $Record.GetType().InvokeMember(“StringData”, “GetProperty”, $Null, $Record, 1)
        if ($PropertyName -eq $MSIPropertyName)
        {
            $PropertyValue = $Record.GetType().InvokeMember(“StringData”, “GetProperty”, $Null, $Record, 2)
            $PropertyValue
        }
        $Record = $View.GetType().InvokeMember(“Fetch”, “InvokeMethod”, $Null, $View, $Null)
    }
}
Developer
Oct 8, 2014 at 7:39 PM
You should pipe this line to Out-Null:
$View.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $View, $Null)

Like so:
$View.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $View, $Null) | Out-Null

The reason being that executing that method sends a null value into the pipeline. When you read a property value from the MSI table, the $null value in the pipeline gets added to the property value you read from the MSI table and you now have an array where the first element is $null. Because of the way PowerShell treats arrays, especially when the first element is $null, you will get a string with a space at the front if you treat this array as as string. You can test this by reading the ProductCode from an MSI and then checking to see if there is a mysterious space at the front of the ProductCode or not.

Also, you should close the view to the MSI once you're done.
$View.GetType().InvokeMember(“Close”, “InvokeMethod”, $Null, $View, $Null) | Out-Null
Oct 8, 2014 at 9:29 PM
Thanks for the feedback mmashwani.

Here is the updated code.
Function Get-MSIProperty {
<#
.SYNOPSIS
    Retrieves the value of a property defined in target MSI file Property Table
.DESCRIPTION
    Specify the full path to the MSI and the property name that stores the value to retrieve. Function will return the value if property is found in the MSI.
.EXAMPLE
    Get-MSIProperty "C:\windows\installer\abc123.msi" "ProductCode"
    Returns the ProductCode value from abc123.msi file.
.EXAMPLE
    $MyProductCode = Get-MSIProperty "C:\windows\installer\abc123.msi" "ProductCode"
    Returns the ProductCode value from abc123.msi file and then stores it in $MyProductCode variable.
.PARAMETER MSIFileName
    The full path to the target MSI file.
.PARAMETER MSIPropertyName
    The name of the property to retrieve.
.NOTES
.LINK
    Http://psappdeploytoolkit.codeplex.com
#>
    Param(
        [Parameter(Mandatory = $true)]
        [string] $MSIFileName,
        [string] $MSIPropertyName
    )
    
    $WindowsInstaller = New-Object -com WindowsInstaller.Installer
    $Database = $WindowsInstaller.GetType().InvokeMember(“OpenDatabase”, “InvokeMethod”, $Null, $WindowsInstaller, @($MSIFileName,0))
    $View = $Database.GetType().InvokeMember(“OpenView”, “InvokeMethod”, $Null, $Database, (“SELECT * FROM Property”))
    $View.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $View, $Null) | Out-Null

    $Record = $View.GetType().InvokeMember(“Fetch”, “InvokeMethod”, $Null, $View, $Null)

    while($Record -ne $Null)
    {
        $PropertyName = $Record.GetType().InvokeMember(“StringData”, “GetProperty”, $Null, $Record, 1)
        if ($PropertyName -eq $MSIPropertyName)
        {
            $PropertyValue = $Record.GetType().InvokeMember(“StringData”, “GetProperty”, $Null, $Record, 2)
            $PropertyValue
        }
        $Record = $View.GetType().InvokeMember(“Fetch”, “InvokeMethod”, $Null, $View, $Null)
    }

    $View.GetType().InvokeMember(“Close”, “InvokeMethod”, $Null, $View, $Null) | Out-Null
}