Generate a Windows Installer transform file (MST) using PowerShell

Related Posts
23 Comments

I’m glad to see I wasn’t crazy in the way that I did this! I just wish I found this post sooner 🙂

Yeah, partly my fault.  I must dedicate more time to SEO on this blog and you would have found it sooner! 🙂  Hey, I’ve just released an App-V Conversion tool here: http://www.alkanesolutions.co.uk/2014/12/29/virtualisr-app-v-4-6-app-v-5-automated-sequencing/.  If you’re interesting on writing a blog entry on it let me know and I’ll throw you some free licenses!

Sure! Throw it my way. It may take a couple of weeks for me to get to it. I’m rebuilding my homelab….AGAIN! 🙂

Hi Im trying to insert into registry table with this script it does not work any ideas?

Hi Rob.  I’d need more information than that!  What error are you getting?

Hi

The script works well when inserting into other tables. EG “INSERT INTO FeatureComponents (Feature_, Component_) VALUES (‘Program’,’AuditKeys’)”

But when issuing this as the query.

“INSERT INTO Registry (Registry, Root, Key, Name, Value, Component_) VALUES (‘SampleReg’, 2, ‘Software\AlkaneTest’, ‘testName’, ‘testValue’, ‘AuditRegKeys’)”

I get this.

Exception calling “InvokeMember” with “5” argument(s): “OpenView,Sql”
At line:61 char:1
+ $View = $database2.GetType().InvokeMember(
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : COMException

COM object that has been separated from its underlying RCW cannot be used.
At line:69 char:1
+ $View.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $View, …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidComObjectException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.InvalidComObjectException

COM object that has been separated from its underlying RCW cannot be used.
At line:70 char:1
+ $View.GetType().InvokeMember(“Close”, “InvokeMethod”, $Null, $View, $ …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidComObjectException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.InvalidComObjectExceptio

…try enclosing your table names and column names with back-ticks like in the examples…..CREATE TABLE `CustomAction` ( `Action` etc.

I think you’ll find that some columns require this (like the Registry.Key column).  That’s probably where your error is.  It’s probably a best practise to enclose table names and column names like this at all times.

Yes I have tried this.

“INSERT INTO `Registry` `Registry`, `Root`, `Key`, `Name`, `Value`, `Component_`) VALUES (‘SampleReg’, 2, ‘Software\AlkaneTest’, ‘testName’, ‘testValue’, ‘AuditRegKeys’)”

its still not having it same error as before. Any other suggestions?

$ValuesList += “INSERT INTO `Registry` (`Registry`, `Root`, `Key`, `Name`, `Value`, `Component_`) VALUES (‘SampleReg’, 2, ‘Software\AlkaneTest’, ‘testName’, ‘testValue’, ‘AuditRegKeys’)”

rob

its not showing the escape characters

Ankita

Have you get any solution on the same?

Yeah I’ve just realised it’s not showing the backticks in these comments.  Drop the script to kae.travis@alkanesolutions.co.uk and I’ll have a quick look.  Otherwise you may get a quicker response asking over at ITNinja…

Issue resolved via usage of double backticks. See tip in blog post.

Hi,

Your post has help me so much updating my automation scripts from vbs to Powershell!

I have one issue when trying to add a custom action into my mst as the vbscript needs to be added to the binary table and everything I have tried using CreateRecord and SetStream has failed. I have searched everywhere on google and can’t find an answer for powershell.

Hoping you can point me in the wright direction.

Thanks,

Jim

Hi Jim. Thanks for the comment. Drop me your script in an email and I’ll try and take a look if I get a second spare. Thanks.

Hi,
Many thanks for your script, I have tried and it worked wonders.
But I have a small issue now.
I am trying to create registry entries in the msi database. I am getting the sql error. Below is my code snippet.
$queryReg = “INSERT INTO “Registry“ (“Registry“,“Root“,“Key“,“Name“,“Value“,“Component_“) VALUES (‘Branding’,’2′,’Software\test’,’testname’,’1234′,’Branding”
$ViewReg = $database2.GetType().InvokeMember(“OpenView”,”InvokeMethod”,$Null,$database2,($queryReg))
$ViewReg.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $ViewReg, $Null) 
$ViewReg.GetType().InvokeMember(“Close”, “InvokeMethod”, $Null, $ViewReg, $Null)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ViewReg) | Out-Null
Error Message:
Exception calling “InvokeMember” with “5” argument(s): “OpenView,Sql”
At C:\Scripts\New folder\MSI\Untitled5.ps1:71 char:1
+ $ViewReg = $database2.GetType().InvokeMember(“OpenView”,”InvokeMethod”,$Null,$da …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : COMException

You cannot call a method on a null-valued expression.
At C:\Scripts\New folder\MSI\Untitled5.ps1:72 char:1
+ $ViewReg.GetType().InvokeMember(“Execute”, “InvokeMethod”, $Null, $ViewReg, $Nul …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull

Hi.

Firstly check the tip in this post for double back ticks.

Secondly check your SQL statement against the golden rules mentioned here: http://www.alkanesolutions.co.uk/2012/11/30/tutorial-1-introduction-to-msi-scripting-and-golden-rules/

Your copy and paste doesn’t look to have rendered correctly since your quotes are incorrect and you’re missing a closing bracket.

(‘Branding’,’2′,’Software\test’,’testname’,’1234′,’Branding”

should be

(‘Branding’,’2’,’Software\test’,’testname’,’1234’,’Branding’)”

Be VERY careful of the quotes and back ticks.  They don’t paste very well in these comments.

 

Purnima Jain

How to add new component for windows installer using powershell script?

How to add new component for windows installer using powershell script?

randomusername

Thanks, this is great! I ran into an issue with this, though: I’m generating a MST to remove some rows in the `CustomAction` and `InstallExecuteSequence` tables to prevent the software from starting automatically upon installation. I ran SQL statements to delete those rows, but it also deleted any row referenced by the the deleted rows.

Orca had no issue deleting only the desired rows, so I used the excellent API Monitor v2 by Rohitab to inspect what it was actually doing. It turns out that rather than copying the entire MSI to create a comparison database, it created a new database into which it merged the original database.

Duplicating this process in Powershell resulted in a MST with only the desired changes.

I’m using the WIX toolset library to avoid calling COM functions directly, but here’s what I came up with (not sure if the code will format properly…

“`
$originalDB = [WixToolset.Dtf.WindowsInstaller.Database]::new($msiReferenceFilePath, [WixToolset.Dtf.WindowsInstaller.DatabaseOpenMode]::ReadOnly)
$updatedDB = [WixToolset.Dtf.WindowsInstaller.Database]::new($msiComparisonFilePath, [WixToolset.Dtf.WindowsInstaller.DatabaseOpenMode]::Create)

$updatedDB.Merge($originalDB)

$mstQueries = @(
‘Delete From `InstallExecuteSequence` where `Action` = ”runAfterFinish”’
‘Delete From `InstallExecuteSequence` where `Action` = ”runAfterFinishPerMachine”’
‘Delete From `InstallExecuteSequence` where `Action` = ”scheduleUninstallPerUser”’
‘Delete From `CustomAction` where `Action` = ”runAfterFinishPerMachine”’
‘Delete From `CustomAction` where `Action` = ”runAfterFinish”’
‘Delete From `CustomAction` where `Action` = ”scheduleUninstallPerUser”’
‘Update `Property` set `Value` = ”1” where `Property` = ”ALLUSERS”’
‘Update `Property` set `Value` = ”0” where `Property` = ”ACCEPTEULA”’
‘Update `Property` set `Value` = ”0” where `Property` = ”MSIINSTALLPERUSER”’
‘Delete From `Property` where `Property` = ”SecureCustomProperties”’
)

foreach ($queryText in $mstQueries) {
$updatedDB.Execute($queryText)
}

$transformSuccess = $updatedDB.GenerateTransform($originalDB, $mstPath)

$updatedDB.CreateTransformSummaryInfo($originalDB, $mstPath, [WixToolset.Dtf.WindowsInstaller.TransformErrors]::None, [WixToolset.Dtf.WindowsInstaller.TransformValidations]::None)

$originalDB.Close()
$updatedDB.Close()
“`

Great work, and thanks for sharing!

Leave a Reply
Send us a message

Send your message any time you want.

Our usual reply time: 2 Business Hours
Send us a message

Have a question? Send us a message.

Our usual reply time: 2 Business Hours