Public/Get-MsiFileInfo.ps1
<# .SYNOPSIS Get properties out of MSI file. .DESCRIPTION Usage Documentation: https://github.com/UNT-CAS/MsiHandler/wiki/Get-MsiFileInfo #> function Get-MsiFileInfo { [OutputType([hashtable])] param( [Parameter( Mandatory = $true, ValueFromPipeLine = $true, ValueFromPipelineByPropertyName = $true )] [ValidateNotNullOrEmpty()] [IO.FileInfo] $Path, [parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [string[]] $Properties = @('Manufacturer', 'ProductName', 'ProductVersion', 'ProductCode', 'ProductLanguage', 'FullVersion'), [parameter(Mandatory = $false)] [switch] $GetPublicProperties, [parameter(Mandatory = $false)] [switch] $DoNotIncludeFileInfo, [parameter(Mandatory = $false)] [switch] $IncludeNonMsiFileInfo ) Begin { $alwaysGetProperties = @('Manufacturer', 'ProductName', 'ProductVersion', 'ProductCode', 'ProductLanguage', 'FullVersion') if (-not $Properties -or $Properties -eq '*' -or $GetPublicProperties.IsPresent) { $query = 'SELECT * FROM Property' } else { $queryWhere = (($Properties + $alwaysGetProperties | Select-Object -Unique) | Foreach-Object { 'Property = ''{0}''' -f $_ }) -join ' OR ' $query = 'SELECT Property, Value FROM Property WHERE {0}' -f $queryWhere } Write-Verbose "[Get-MsiFileInfo] MSI Query: ${query}" } Process { Write-Verbose "[Get-MsiFileInfo] Path: ${Path}" if (-not $Path.Exists) { $resolvedPath = (Resolve-Path $Path -ErrorAction 'Ignore').ProviderPath Write-Verbose "[Get-MsiFileInfo] ResolvedPath: ${resolvedPath}" if ($resolvedPath) { [IO.FileInfo] $Path = $resolvedPath } } [hashtable] $msiProperties = @{} if ($IncludeNonMsiFileInfo.IsPresent -or -not $DoNotIncludeFileInfo.IsPresent) { $msiProperties.Add('.IO.FileInfo', $Path) } if ($Path.Exists) { $windowsInstaller = New-Object -ComObject windowsInstaller.Installer try { $msiDatabase = $windowsInstaller.GetType().InvokeMember('OpenDatabase', 'InvokeMethod', $null, $windowsInstaller, @($Path.FullName, 0)) $view = $msiDatabase.GetType().InvokeMember('OpenView', 'InvokeMethod', $null, $msiDatabase, ($query)) [void] $view.GetType().InvokeMember('Execute', 'InvokeMethod', $null, $view, $null) do { $record = $view.GetType().InvokeMember('Fetch', 'InvokeMethod', $null, $view, $null) if (-not [string]::IsNullOrEmpty($record)) { $addMember = $false # Return the value $name = $record.GetType().InvokeMember('StringData', 'GetProperty', $null, $record, 1) Write-Debug " [Get-MsiFileInfo] 1 (name): ${name}" $value = $record.GetType().InvokeMember('StringData', 'GetProperty', $null, $record, 2) Write-Debug " [Get-MsiFileInfo] 2 (value): ${value}" if ($GetPublicProperties.IsPresent) { if ($alwaysGetProperties -contains $name) { $addMember = $true } elseif ($name -cnotmatch '[a-z]') { $addMember = $true } } else { $addMember = $true } if ($addMember) { Write-Debug " [Get-MsiFileInfo] Adding to return set." [void] $msiProperties.Add($name, $value) } } } until ([string]::IsNullOrEmpty($record)) # Commit database and close view [void] $msiDatabase.GetType().InvokeMember('Commit', 'InvokeMethod', $null, $msiDatabase, $null) [void] $view.GetType().InvokeMember('Close', 'InvokeMethod', $null, $view, $null) } catch { Write-Debug ('[Get-MsiFileInfo] Error Caught' -f $_.Exception.Message) Write-Warning ('Unable to open MSI database; it''s either not an MSI file or the file is corrupted: {0}' -f $Path.FullName) } finally { $view = $null $msiDatabase = $null [void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($windowsInstaller) $windowsInstaller = $null } } Write-Debug ('msiProperties: {0}' -f ($msiProperties | Out-String)) # Write-Output $msiProperties Write-Output (New-Object PSObject -Property $msiProperties) } End { # Run garbage collection and release ComObject if ($windowsInstaller) { [void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($windowsInstaller) } [void] [System.GC]::Collect() } } |