Generating MST transforms for an MSI database

This blog entry provides an example of generating MST transforms for an MSI database using VBScript.  It follows on from the previous blog post which provided a tutorial on applying transforms to an MSI database using VBScript.

It forms part 13 of an 17-part series that explores how to use VBScript to manipulate MSI relational databases using the Windows Installer API.  Throughout this series of tutorials, we identify the common issues that we encounter and the best practises that we use to overcome them.

Now we’re going to attempt to generate a transform from changes which we make to an MSI. An example of this could be adding certain properties for a customer, or possible an audit key in the registry table.

When we’re generating an MST from a script, here’s the idea:

  • We take a copy of the original base MSI, and create a new temporary MSI
  • We apply any changes we make to this temporary MSI
  • We find the difference between the temporary MSI and the base (original) MSI
  • We generates a transform from the differences between the base MSI and the temporary MSI.

So, here goes. We’re going to insert the following entry into the Registry table:

generate_xfm

'create 2 constants - one for when we want to just query the MSI (read) and one for when we want to make changes (write)

Const msiOpenDatabaseModeReadOnly = 0
Const msiOpenDatabaseModeTransact = 1

'create a file system object, so that we can copy the original MSI
Dim fso : Set fso = CreateObject("Scripting.FileSystemObject")

'get a reference to the original msi
Dim originalMSI : Set originalMSI = fso.GetFile(WScript.arguments(0))

'create a full path to our 'proposed' generated transform
Dim transformWithChanges: transformWithChanges = Left(originalMSI.Path, InStrRev(originalMSI.Path, ".") - 1) & "_Changes.MST"

'get random filename or a temporary MSI
temporaryMSI = originalMSI.ParentFolder & "\" & fso.GetTempName

'make a copy of the original MSI, and name it with our temporary name above
originalMSI.Copy (temporaryMSI)   'Make a backup of the MSI to work on

'set the attribute of the temp file - default to 'Normal'
fso.GetFile(temporaryMSI).Attributes = 0

'we can now use our temp file above as a base comparison

Set oInstaller = CreateObject("WindowsInstaller.Installer")

'open the temporary MSI in transact mode
Set oTempDatabase = oInstaller.OpenDatabase(temporaryMSI, msiOpenDatabaseModeTransact)

'insert our new value
Dim sql : sql = "INSERT INTO `Registry` (`Registry`,`Root`,`Key`,`Name`,`Value`,`Component_`) VALUES ('SampleTransformReg',2,'Software\AlkaneTest','testTransformName','testTransformValue','alkaneComponent')"

Dim regView : Set regView = oTempDatabase.OpenView(sql)

'execute the query
regView.Execute

'Now get a reference to the original, un-changed MSI.  Open it in Read-only mode
Set oOriginalDatabase = oInstaller.OpenDatabase(originalMSI.Path, msiOpenDatabaseModeReadOnly)

'get the differences between our original database (oOriginalDatabase) and our temp database (oTempDatabase), and generate transform to our new transform filename (transformWithChanges)
oTempDatabase.GenerateTransform oOriginalDatabase, transformWithChanges

'create transform summary information stream.  This was a bug which I found was missing from the SDK scripts
oTempDatabase.CreateTransformSummaryInfo oOriginalDatabase, transformWithChanges, 0, 0

regView.Close
Set regView = Nothing
Set oTempDatabase = Nothing
Set oOriginalDatabase = Nothing
Set oInstaller = Nothing

'delete our temporary database
Set File = fso.GetFile(temporaryMSI)
File.Delete

Wscript.Echo transformWithChanges & " Created!"

The comments throughout the example above explain what each section does. What I would say, though, is:

  • Always remember to call the CreateTransformSummaryInfo method! The Windows Installer SDK examples don’t include this. Omitting this may mean your transform wont generate/apply without error.
  • Ensure you close every view object, otherwise the temporary file will not delete at the end.
  • If no changes are made to the base MSI and you attempt to generate a transform, there will be an error because the difference between the base MSI and the new one will be nothing.

Thanks for reading about generating MST transforms for an MSI database.  Next you can find out how to use SQL from with a custom action using VBScript.