从 PHP 读取 Windows 安装程序(MSI 文件)属性

Read Windows Installer (MSI file) attributes from PHP

我有一个 Windows MSI 文件,我需要以编程方式从中读取版本号。我唯一能看到这个版本的地方是在文件详细信息的 Subject 中:

如果我能以某种方式阅读 Subject 的全部内容,那就没问题了,但是有什么方法可以从 PHP 中获取吗? PHP 在 IIS Web 服务器中是 运行,如果这有帮助的话;-)

stat对此没有帮助。

我考虑过对文件进行校验和,并且可以做到,但我确实需要真实版本。

首先要做两件事:

  1. 我从未从 PHP 访问过 COM,但下面是一些使用 MSI API - COM automation. There are also Win32 functions.

    从 MSI 文件获取信息的 VBScript 示例
  2. 您引用的那个字段不是版本字段,而是来自 MSI 的“摘要流”的文本字段 - MSI 文件的一个特殊部分,包含各种“元信息”。 Summary Information Stream(全名)。

这是获取 MSI 文件的真实版本的方法。这存储在 MSI 文件中的 属性 "ProductVersion" 中。至少有两种不同的方法来检索它 - 通过将 MSI 文件作为会话打开或仅通过 SQL 查询访问 属性 table:

通过Session对象访问版本:

Const msiUILevelNone = 2
Dim installer : Set installer = CreateObject("WindowsInstaller.Installer")
installer.UILevel = msiUILevelNone

Set s = installer.OpenPackage("C:\MySetup.msi",1)

MsgBox CStr(s.ProductProperty("ProductVersion"))

通过 SQL 再次访问 MSI 数据库 (属性 table):

Dim installer : Set installer = CreateObject("WindowsInstaller.Installer")

' Open MSI database in read-only mode (0)
Set db = installer.OpenDatabase("C:\MySetup.msi", 0)
Set view = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'")
view.Execute

Set record = view.Fetch

MsgBox CStr(record.StringData(1))

然后是访问 SummaryStream 的问题——从它的外观来看,这是你真正要问的——这是一个简单的冒烟测试,其中包含一些关于你可以检索哪些属性的提示——注意摘要stream - 损坏可能有多种方式(我不记得细节,但以只读方式访问应该是安全的):

Dim installer : Set installer = CreateObject("WindowsInstaller.Installer")

' Open MSI database in read-only mode (0)
Set db = installer.OpenDatabase("C:\MySetup.msi", 0)
MsgBox CStr(db.SummaryInformation.Property(3))

' 1 = "Codepage"
' 2 = "Title"
' 3 = "Subject"
' 4 = "Author"
' 5 = "Keywords"
' 6 = "Comments"
' 7 = "Template"
' 8 = "LastAuthor"
' 9 = "Revision"
' 11 = "Printed"
' 12 = "Created"
' 13 = "Saved"
' 14 = "Pages"
' 15 = "Words"
' 16 = "Characters"
' 18 = "Application"
' 19 = "Security"

链接:

目前我无法为此找到原生 PHP 解决方案,所以我通过调用 Powershell 脚本暂时解决了这个问题,因为它似乎更容易做到。

我现在有这个 PHP 代码: $version = exec("powershell.exe -file GetMsiVersion.ps1 MyFile.msi);

有了我上面的图片,那么 $version 将包含 1.5.9,所以我什至不需要解释来自 Subject.[=16= 的数据]

GetMsiVersion.ps1 Powershell 脚本有这个代码:

function Get-Property ($Object, $PropertyName, [object[]]$ArgumentList) {
    return $Object.GetType().InvokeMember($PropertyName, 'Public, Instance, GetProperty', $null, $Object, $ArgumentList)
}

function Invoke-Method ($Object, $MethodName, $ArgumentList) {
    return $Object.GetType().InvokeMember($MethodName, 'Public, Instance, InvokeMethod', $null, $Object, $ArgumentList)
}

$Path = $args[0]
$msiOpenDatabaseModeReadOnly = 0
$Installer = New-Object -ComObject WindowsInstaller.Installer
$Database = Invoke-Method $Installer OpenDatabase  @($Path, $msiOpenDatabaseModeReadOnly)
$View = Invoke-Method $Database OpenView  @("SELECT Value FROM Property WHERE Property='ProductVersion'")
Invoke-Method $View Execute
$Record = Invoke-Method $View Fetch
if ($Record) {
    Write-Output (Get-Property $Record StringData 1)
}

Invoke-Method $View Close @()

我现在会接受这是最好的解决方案,但是,我希望这可以从 PHP 本地实现,因为我认为这是一个更好、更干净的解决方案 - 到那时我会接受它作为最佳答案(或者至少 不接受 我自己的临时解决方案)。

做一个 exec 有点邪恶 ;-)