/build/static/layout/Breadcrumb_cap_w.png
06/26/2019 318 views
$msiOpenDatabaseModeReadOnly = 0
$msiOpenDatabaseModeTransact = 1
$msiTransformErrorNone = 0
$msiTransformValidationNone = 0
$msiFolder = "C:\Test\"
$database1Path = $msiFolder + "ELMSDesktopApplication.msi"
$database2Path = $database1Path + "_bak"
$MSTPath = $msiFolder + "New_MST.mst"
 
#If backup doesn't already exist, make a copy to make our changes to
if (!(Test-Path -Path $database2Path))
{
Copy-Item -Path $database1Path -Destination $database2Path
}
#Remove MST if already exists
If (Test-Path $MSTPath){
Remove-Item $MSTPath
}

#open original MSI in ReadOnly mode
$windowsInstaller = New-Object -ComObject WindowsInstaller.Installer           
$database1 = $windowsInstaller.GetType().InvokeMember(
"OpenDatabase", 
"InvokeMethod", 
$Null, 
$windowsInstaller, 
@($database1Path, $msiOpenDatabaseModeReadOnly)
)  

#open 'backup' MSI in transact mode
$database2 = $windowsInstaller.GetType().InvokeMember(
"OpenDatabase", 
"InvokeMethod", 
$Null, 
$windowsInstaller, 
@($database2Path, $msiOpenDatabaseModeTransact)

#Insert a test Feature Component
$query = "INSERT INTO ``FeatureComponents`` (``Feature_``,``Component_``) VALUES ('BulkLoaderInstaller_Files','AAA_AddReg')"

$View = $database2.GetType().InvokeMember(
"OpenView",
"InvokeMethod",
$Null,
$database2,
($query)
)
$View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)
$View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($View) | Out-Null


#Insert a test  Component
$queryCom = "INSERT INTO ``Component`` (``Component``,``ComponentId``,``Directory_``,``Attributes``,``Condition``,``KeyPath``) VALUES ('AAA_AddReg','','INSTALLDIR',0,'','')"

$ViewCom = $database2.GetType().InvokeMember(
"OpenView",
"InvokeMethod",
$Null,
$database2,
($queryCom)
)
$ViewCom.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $ViewCom, $Null)
$ViewCom.GetType().InvokeMember("Close", "InvokeMethod", $Null, $ViewCom, $Null)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ViewCom) | Out-Null


#Insert a Registry
$queryReg = "INSERT INTO ``Registry`` (``Registry``,``Root``,``Key``,``Name``,``Value``,``Component_``) VALUES ('Registry1',2,'Software\Chiranjit','Installed','1','AAA_AddReg')"

$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


#Commit the changes to our backup database
$database2.GetType().InvokeMember("Commit", "InvokeMethod", $Null, $database2, $Null)
#Generate a transform (the difference between our original MSI and our Backup MSI)
$transformSuccess = $database2.GetType().InvokeMember(
"GenerateTransform", 
"InvokeMethod", 
$Null, 
$database2, 
@($database1,$MSTPath)
)  
#Create a Summary Information Stream for the MST
$transformSummarySuccess = $database2.GetType().InvokeMember(
"CreateTransformSummaryInfo", 
"InvokeMethod", 
$Null, 
$database2, 
@($database1,$MSTPath, $msiTransformErrorNone, $msiTransformValidationNone)
)  

#Release objects from memory
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($database1) | Out-Null 
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($database2) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($windowsInstaller) | Out-Null

#Delete backup database
If (Test-Path $database2Path){
Remove-Item $database2Path
}
0 Comments   [ + ] Show comments

Comments


All Answers

0

Personally, I'd be using the proven Wix toolset to generate the transform but, as a casual observation, your code doesn't seem to create the 'BulkLoaderInstaller_Files' feature...

Answered 06/27/2019 by: VBScab
Red Belt

0

Does the Registry table exist in your MSI?
Your code is working fine for me only if the Registry table is present in the source MSI.

Here is an example how to handle it for CustomAction table:

https://www.alkanesolutions.co.uk/blog/2014/10/30/powershell-windows-installer-msi-transforms-mst/

...
#use the TablePersist method to see if the CustomAction table exists
$tableExists = $database2.GetType().InvokeMember(
"TablePersistent",
"GetProperty",
$Null,
$database2,
"CustomAction"
)

#If CustomAction table does not exist
if ([int]$tableExists -ne 1)
{
#Create CustomAction table
$query = "CREATE TABLE ``CustomAction`` ( ``Action`` CHAR(72) NOT NULL, ``Type`` SHORT NOT NULL, ``Source`` CHAR(64), ``Target`` LONGCHAR PRIMARY KEY ``Action``)"
$View = $database2.GetType().InvokeMember(
"OpenView",
"InvokeMethod",
$Null,
$database2,
($query)
)
$View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)
$View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($View) | Out-Null
}
...
Answered 07/01/2019 by: rad33k
Third Degree Brown Belt

  • Hi You have modified anything, Actually for me Registry is not coming its showing an SQL error(Openview,Sql,Exceptio calling).
    • I did not change anything - I just copied your code and ran it against my MSI.
      I tested it on Win10 machine.
      Have you tried to test it with few different MSIs? Does the Registry table exist in your base MSI?