PowerShell Automation for Verifying MST

Occasional Contributor

Just finished putting together a script to Apply an MST to an MSI then read out the property table.  This is for an automation process to verify a submited MSI and MST meet our packaging standards.


Need a little sanity check as working with COM objects is not a strong point for me.

Really want to make sure I am closing the files correctly after applying the transform then querying the database.  I wasn't able to delete the temp files running in Powershell ISE until I did the ReleaseComObject.  I didn't have to do that when working with the straight MSI and just pull the properties from it so I hope this is not corrupting any files.  

# Apply MST to an MSI
# Based on Code from: https://hinchley.net/articles/update-cab-file-and-msi-transform-via-command-line/
$SourceMSI = "C:\Temp\MSI\MSI-x64.msi"
$SourceMST = "C:\Temp\MSI\MSI.mst"

$TempMSI = "$SourceMSI.tmp"
$TempMST = "$SourceMST.tmp"

Copy-Item $SourceMSI $TempMSI -Force
Copy-Item $SourceMST $TempMST -Force

$WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
#Open the database in Direct read/write without Transaction (2)
$MSIDatabase1 = $WindowsInstaller.GetType().InvokeMember('OpenDatabase' , 'InvokeMethod' , $Null, $WindowsInstaller, @($TempMSI, 2))

#$MSIDatabase1.applytransform($TempMST, 0)

$MSIDatabase1.GetType().InvokeMember('ApplyTransform' , 'InvokeMethod' , $Null , $MSIDatabase1 , @($TempMST, 0))

            $Query = ("SELECT Property,Value FROM Property")

            #Opens a data view to the MSI based on the query created.
            $View = $MSIDatabase1.GetType().InvokeMember('OpenView', 'InvokeMethod', $null, $MSIDatabase1, ($Query))
            $null = $View.GetType().InvokeMember('Execute', 'InvokeMethod', $null, $View, $null)

                        $hash = @{}
            # Add File information (Note this adds the full File information Porperties so can call with <Var>.File |Select *

                WHILE ($Record = $View.GetType().InvokeMember('Fetch', 'InvokeMethod', $null, $View, $null)) 
	            $name = $Record.GetType().InvokeMember('StringData', 'GetProperty', $null, $Record, 1)
	            $value = $hashMSIValue = $Record.GetType().InvokeMember('StringData', 'GetProperty', $null, $Record, 2)

# Push Hash table into a PSCustom object
                $msiProperties = [pscustomobject]$hash

# I'm not sure If I have everything required to close out here properly from applying the transform.
            $null = $MSIDatabase1.GetType().InvokeMember('Commit' , 'InvokeMethod' , $Null , $MSIDatabase1 , $Null)
            $null = $view.GetType().InvokeMember('Close', 'InvokeMethod', $null, $view, $null)
# Really important part to be able to release the opened files and delete
            $null = [Runtime.Interopservices.Marshal]::ReleaseComObject($view)
            $null = [Runtime.Interopservices.Marshal]::ReleaseComObject($MSIDatabase1)
            $null = [Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller)



0 Replies