Forum Discussion
J Mymryk
Nov 05, 2021Copper Contributor
PowerShell Automation for Verifying MST
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 *
$hash.Add('File',$TempMSI)
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)
$hash.Add($name,$value)
}
# 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)
[GC]::Collect()
No RepliesBe the first to reply