<#PSScriptInfo .VERSION 2024.12.8.0 .GUID f711f1e2-f8c2-412b-97a5-60a2cf6f1510 .AUTHOR Michael Escamilla .COMPANYNAME .COPYRIGHT .TAGS .LICENSEURI .PROJECTURI https://github.com/MichaelEscamilla/GetMSIInformation .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES 1.0.0.0 - Initial release 2.0.0.0 - 10-4-2024 - Added file hash information, and context menu items for the installation and uninstallation of a Right-Click Option in Windows Explorer. And some other UI improvements. 2024.10.4.1 - Updated the version numbering, and a sepearator in the context menu. 2024-10.13.0 - Added an error message when the file is locked 2024-12.8.0 - Formatted Script for Publishing to PowerShell Gallery .PRIVATEDATA #> <# .SYNOPSIS This script provides a graphical user interface (GUI) for viewing and copying properties of MSI files. .DESCRIPTION The script creates a WPF-based GUI that allows users to drag and drop MSI files to view their properties such as Product Name, Manufacturer, Product Version, Product Code, and Upgrade Code. It also provides functionality to copy these properties to the clipboard and to clear the displayed information. Additionally, the script includes options to install and uninstall a context menu item for MSI files to retrieve their properties. .PARAMETER FilePath Optional parameter to specify the path of the MSI file to automatically load the information for. .NOTES #> param ( [Parameter(Mandatory = $false)] [string]$FilePath ) ############################################# ################# Variables ################# ############################################# # Script Name $Global:ScriptName = "GetMSIInformation.ps1" # Script Version [System.Version]$Global:ScriptVersion = "2024.12.8.0" # Right-Click Menu Name $Global:RightClickMenuName = "Get MSI Information" # Get the Security Principal $Global:currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) ############################################# ################# Functions ################# ############################################# #region Functions function Test-FileLock { param ( [Parameter(Mandatory = $true)] [string]$Path ) try { $FileStream = [System.IO.File]::Open("$($Path)", 'Open', 'Write') $FileStream.Close() $FileStream.Dispose() return $false } catch { return $true } } function Get-MsiProperties { param ( [Parameter(Mandatory = $true)] [IO.FileInfo[]]$Path ) # Check if the MSI file path exists if (-not (Test-Path $Path)) { throw "The file $Path does not exist." } # Create a new Windows Installer COM object $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer # Open the MSI database in read-only mode $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($Path.FullName, 0)) # Open a view on the Property table $MSIPropertyView = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, @("SELECT * FROM Property")) # Execute the view query $MSIPropertyView.GetType().InvokeMember("Execute", "InvokeMethod", $null, $MSIPropertyView, $null) # Fetch the first record from the result set $MSIRecord = $MSIPropertyView.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $MSIPropertyView, $null) # Initialize an empty System Object to store properties [System.Object]$Properties = @{} # Loop through all records in the result set while ($null -ne $MSIRecord) { # Get the property name from the first column $property = $MSIRecord.GetType().InvokeMember("StringData", "GetProperty", $null, $MSIRecord, @(1)) # Get the property value from the second column $Value = $MSIRecord.GetType().InvokeMember("StringData", "GetProperty", $null, $MSIRecord, @(2)) # Add the property name and value to the hashtable $Properties[$Property] = $Value # Fetch the next record from the result set $MSIRecord = $MSIPropertyView.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $MSIPropertyView, $null) } # Return the System Object of properties $Properties } function Enable-AllButtons { # Get all button variables $Buttons = Get-Variable -Name "btn_*" -ValueOnly -ErrorAction SilentlyContinue foreach ($Button in $Buttons) { # Enable Button $Button.IsEnabled = $true } } function Disable-AllButtons { # Get all button variables $Buttons = Get-Variable -Name "btn_*" -ValueOnly -ErrorAction SilentlyContinue foreach ($Button in $Buttons) { # Disable Button $Button.IsEnabled = $false } } function Clear-Textboxes { # Get all textbox variables $Textboxes = Get-Variable -Name "txt_*" -ValueOnly -ErrorAction SilentlyContinue foreach ($Textbox in $Textboxes) { # Disable Button $Textbox.Clear() } } # Stolen from: https://github.com/PatchMyPCTeam/CustomerTroubleshooting/blob/Release/PowerShell/Get-LocalContentHashes.ps1 Function Get-EncodedHash { [CmdletBinding()] Param( [Parameter(Position = 0)] [System.Object]$HashValue ) $hashBytes = $hashValue.Hash -split '(?<=\G..)(?=.)' | ForEach-Object { [byte]::Parse($_, 'HexNumber') } Return [Convert]::ToBase64String($hashBytes) } function Get-FileHashInformation { param ( [Parameter(Mandatory = $true)] [IO.FileInfo[]]$Path ) Write-Host "Getting File Hash Information for: [$Path]" # Initialize the hash object $Hashes = @{} # Get File Hash - MD5 $FileHashMD5 = Get-FileHash -Path $Path -Algorithm MD5 $Hashes["MD5"] = $FileHashMD5 # Get File Hash - SHA1 $FileHashSHA1 = Get-FileHash -Path $Path -Algorithm SHA1 $Hashes["SHA1"] = $FileHashSHA1 # Get File Hash - SHA256 $FileHashSHA256 = Get-FileHash -Path $Path -Algorithm SHA256 $Hashes["SHA256"] = $FileHashSHA256 # Get File Hash - SHA1 - Encoded $FileHashEncoded = Get-EncodedHash -HashValue $FileHashSHA1 $Hashes["Digest"] = $FileHashEncoded # Return the hash object $Hashes } function Set-TextboxInformation { param ( [Parameter(Mandatory = $true)] [System.Object]$MSIPropertiesInfo, [Parameter(Mandatory = $true)] [hashtable]$FileHashInfo ) # Set the MSI file properties textboxes $txt_ProductName.Text = $MSIPropertiesInfo.ProductName $txt_Manufacture.Text = $MSIPropertiesInfo.Manufacturer $txt_ProductVersion.Text = $MSIPropertiesInfo.ProductVersion $txt_ProductCode.Text = $MSIPropertiesInfo.ProductCode $txt_UpgradeCode.Text = $MSIPropertiesInfo.UpgradeCode # Set the File Hash Information textboxes $txt_MD5.Text = $FileHashInfo.MD5.Hash $txt_SHA1.Text = $FileHashInfo.SHA1.Hash $txt_SHA256.Text = $FileHashInfo.SHA256.Hash $txt_Digest.Text = $FileHashInfo.Digest } #endregion Functions ############################################# ################# Main Script ################ ############################################# # Load Assemblies Add-Type -AssemblyName PresentationFramework Add-Type -AssemblyName System.Windows.Forms # Build the GUI [xml]$XAMLformMSIProperties = @"