I'm am trying to accomplish this in Powershell, I've found it to very difficult to do. This is the closest bit of code I've found that does something similar. I've also found this example which is similar but in VBScript. I've compiled all this plus hours of googling into this:
$com_object = New-Object -com WindowsInstaller.Installer
[int]$msiOpenDatabaseMode = 0
$database = $com_object.GetType().InvokeMember(
"OpenDatabase",
"InvokeMethod",
$Null,
$com_object,
@("C:\XXX.msi", $msiOpenDatabaseMode)
)
$View = $database.GetType().InvokeMember('OpenView', 'InvokeMethod', $null, $database,
("INSERT INTO Property (Property, Value) VALUES ('REBOOT', 'Force')"))
$View.GetType().InvokeMember('Execute', 'InvokeMethod', $null, $View, $null) | Out-Null
$View.GetType().InvokeMember('Close', 'InvokeMethod', $null, $View, $null) | Out-Null
However I get the following when I run it:
Exception calling "InvokeMember" with "5" argument(s): "Execute,Params" At C:\XXX\Example.PS1:15 char:5 $View.GetType().InvokeMember('Execute', 'InvokeMethod', $null, $V ...
CategoryInfo : NotSpecified: (:) [], MethodInvocationException FullyQualifiedErrorId : COMException
And I can't figure out why, does anyone have a idea why this won't run? My real issue with this is that I can't see the root of the issue, all the COMExceptions don't seem to return any real information, it makes figuring out why COM invocations are failing extremely difficult, is there a better way of doing that too?
Read / Write:
[int]$msiOpenDatabaseMode = 0
this opens the MSI database read-only - very common glitch. This needs to be corrected, but there could be other glitches as well. I just based myself on an existing script for the sample below.Heads-up: Keep in mind that some properties should not be authored into the Property table. The only ones I can think of right now are: FASTOEM, ADDLOCAL - there are others. REBOOT should be OK technically - I think - however read the next point - it is not good practice.
Warning on Reboot: The
ScheduleReboot
MSI standard action triggers a spontaneous reboot of the system when an MSI is run in silent mode. I just verified that the same thing happens with this approach (REBOOT = Force
in property table). To install MSI silently, run this command from an elevatedcmd.exe
command prompt:msiexec /i Setup.msi /qn
.
- Ethics of rebooting (no less).
- Another issue: installation modes (you are asked to reboot after every operation - modify, repair, upgrade, uninstall, etc...).
DTF: I also want to mention the Deployment Tools Foundation .NET classes written to interact with the MSI API. Using this dll you don't have to deal with COM Interop. These DLLs come installed with the WiX toolkit. Here is a brief overview of the dll family.
Powershell is not my thing - you would have to be Egyptian to like those hieroglyphs (as powerful as they might be) - but maybe you can try the script below (update $MsiFilePath
). Say no to high line-noise!
REBOOT
entry in the property table - running twice will trigger an error.Actual script:
$Installer = new-object -comobject WindowsInstaller.Installer
$MSIOpenDatabaseModeTransact = 1
$MsiFilePath = "C:\Users\UserName\Desktop\MyTest.msi"
$MsiDBCom = $Installer.GetType().InvokeMember(
"OpenDatabase",
"InvokeMethod",
$Null,
$Installer,
@($MsiFilePath, $MSIOpenDatabaseModeTransact)
)
$Query1 = "INSERT INTO ``Property`` (``Property``,``Value``) VALUES ('REBOOT','Force')"
$Insert = $MsiDBCom.GetType().InvokeMember(
"OpenView",
"InvokeMethod",
$Null,
$MsiDBCom,
($Query1)
)
$Insert.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $Insert, $Null)
$Insert.GetType().InvokeMember("Close", "InvokeMethod", $Null, $Insert, $Null)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Insert) | Out-Null
$MsiDBCom.GetType().InvokeMember("Commit", "InvokeMethod", $Null, $MsiDBCom, $Null)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($MsiDBCom) | Out-Null