首页 > 解决方案 > Powershell - 如何使用“WindowsInstaller.Installer”在 MSI 中插入属性值 - REBOOT = Force

问题描述

我正在尝试在 Powershell 中完成此操作,我发现它很难做到。这是我发现做类似事情的最接近的代码。我还发现了这个类似的例子,但在 VBScript 中。我已经将所有这些加上几个小时的谷歌搜索编译成这个:

$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

但是,当我运行它时,我得到以下信息:

使用“5”参数调用“InvokeMember”的异常:“Execute,Params”在 C:\XXX\Example.PS1:15 char:5 $View.GetType().InvokeMember('Execute', 'InvokeMethod', $null, $V ...

CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
FullyQualifiedErrorId : COMException

而且我不知道为什么,有人知道为什么这不会运行吗?我的真正问题是我看不到问题的根源,所有 COMExceptions 似乎都没有返回任何真实信息,这使得弄清楚 COM 调用失败的原因非常困难,有没有更好的方法那个也是?

标签: powershellinsertcomwixwindows-installer

解决方案


读/写[int]$msiOpenDatabaseMode = 0这会以只读方式打开 MSI 数据库 - 非常常见的故障。这需要纠正,但也可能存在其他故障。我只是基于下面示例的现有脚本。

提醒:请记住,某些属性不应被创作到属性表中。我现在唯一能想到的是: FASTOEM ADDLOCAL - 还有其他的。从技术上讲,REBOOT 应该没问题 - 我认为 - 但是请阅读下一点 - 这不是一个好习惯。

重启警告ScheduleReboot当 MSI 在静默模式下运行时,MSI 标准操作会触发系统自发重启。我刚刚验证了这种方法会发生同样的事情(REBOOT = Force在属性表中)。要静默安装 MSI,请从提升的cmd.exe命令提示符运行此命令:msiexec /i Setup.msi /qn.

  • 重启的伦理(不少于)。
  • 另一个问题:安装模式(每次操作后都要求您重新启动 - 修改、修复、升级、卸载等...)。

DTF:我还想提一下为与 MSI API 交互而编写的部署工具基础 .NET 类。使用此 dll,您不必处理 COM 互操作。这些 DLL 随 WiX 工具包一起安装。以下是 dll 系列的简要概述


Powershell 不是我的菜——你必须是埃及人才能喜欢那些象形文字(尽可能强大)——但也许你可以试试下面的脚本(更新$MsiFilePath)。对高线噪音说不!

  • 此脚本假定属性表中没有预先存在的REBOOT条目 - 运行两次将触发错误。
  • 如果您遇到问题 - 并且您已经运行了许多测试 - 尝试使用您的测试 MSI 的新副本或完全切换到另一个 MSI。
    • 您可以通过自动化测试进入一些奇怪的“肮脏状态”。
    • 当文件损坏时,避免将头撞到脚本上(只是为了说明这一点)。

实际脚本:

$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

推荐阅读