Get-MsiPropertyValue.ps1

<#PSScriptInfo
 
.Version
    1.1
.Guid
    dc64634e-86a9-4ed5-bc7f-f2a55fa3bb0a
.Author
    Thomas Malkewitz @dotps1
.Tags
    MSI, PSCustomObject
.ProjectUri
    https://github.com/dotps1/PSFunctions
.ReleaseNotes
    Move Out-Null cmdlets to left side of pipeline for reduced performance cost.
 
#>


<#
 
.Synopsis
    Gets a property value from a Windows Installer Database.
.Description
    Opens a Windows Installer Database (.msi) and queries for the specified property value.
.Inputs
    System.String
.Outputs
    System.String
.Parameter Path
    System.String
    The location of the Windows Installer Database.
.Parameter Property
    System.String
    The Property to get the value of.
.Example
    PS C:\> Get-MsiPropertyValue -Path .\jre1.8.0_121.msi -Property ProductVersion, ProductCode
 
    Name ProductVersion ProductCode
    ---- -------------- -----------
    jre1.8.0_121.msi 8.0.1210.13 {26A24AE4-039D-4CA4-87B4-2F32180121F0}
.Example
    PS C:\> Get-ChildItem -Path ".\Installers" -Filter "*.msi" | Select -ExpandProperty FullName | Get-MsiPropertyValue -Property ProductVersion
     
    Name ProductVersion ProductCode
    ---- -------------- -----------
    jre1.8.0_101.msi 8.0.1010.13 {26A24AE4-039D-4CA4-87B4-2F32180101F0}
    jre1.8.0_111.msi 8.0.1110.14 {26A24AE4-039D-4CA4-87B4-2F32180111F0}
    jre1.8.0_121.msi 8.0.1210.13 {26A24AE4-039D-4CA4-87B4-2F32180121F0}
.Link
    https://dotps1.github.io
.Link
    https://www.powershellgallery.com/packages/Get-MsiPropertyValue
.Link
    https://grposh.github.io
 
#>


    
[CmdletBinding()]
[OutputType(
    [PSCustomObject]
)]

param (
    [Parameter(
        Mandatory = $true,
        ValueFromPipeLine = $true,
        ValueFromPipelineByPropertyName = $true
    )]
    [ValidateScript({
        if (([System.IO.FileInfo]$_).Extension -eq ".msi") {
            $true
        } else {
            throw "Path must be a Windows Installer Database (*.msi) file."
        }
    })]
    [String[]]
    $Path,

    [Parameter(
        Mandatory = $true,
        ValueFromPipelineByPropertyName = $true
    )]
    [String[]]
    $Property
)

begin {
    $windowsInstaller = New-Object -ComObject WindowsInstaller.Installer
}

process {
    foreach ($pathValue in $Path) {
        try {
            $item = Get-Item -Path $pathValue -ErrorAction Stop
                
            $database = $windowsInstaller.GetType().InvokeMember(
                "OpenDatabase", "InvokeMethod", $null, $windowsInstaller, ($item.FullName, 0)
            )

            $output = [PSCustomObject]@{
                Name = $item.Name
            }

            foreach ($propertyValue in $Property) {
                $view = $database.GetType().InvokeMember(
                    "OpenView", "InvokeMethod", $null, $database, "SELECT Value FROM Property WHERE Property = '$propertyValue'"
                )

                Out-Null -InputObject ($view.GetType().InvokeMember(
                    "Execute", "InvokeMethod", $null, $view, $null
                ))

                $record = $view.GetType().InvokeMember(
                    "Fetch", "InvokeMethod", $null, $view, $null
                )

                $value = $record.GetType().InvokeMember(
                    "StringData", "GetProperty", $null, $record, 1
                )

                Add-Member -InputObject $output -Name $propertyValue -Value $value -MemberType NoteProperty
            }

            Out-Null -InputObject ($database.GetType().InvokeMember(
                "Close", "InvokeMethod", $null, $view, $null
            ))

            Write-Output -InputObject $output
        } catch {
            Write-Error $_
            continue
        }
    }
}

end {
    Out-Null -InputObject ([System.Runtime.InteropServices.Marshal]::ReleaseComObject(
        $windowsInstaller
    ))
}