我可以使用msilib或其他Python库从.msi文件中提取一个文件吗?

0 投票
3 回答
2336 浏览
提问于 2025-04-17 19:47

我想做的事情是检查一个特定的文件在MSI中是否存在,并且里面是否包含某个特定的字符串。

我现在的想法是先运行:

 db = msilib.OpenDatabase('c:\Temp\myfile.msi',1)
 query = "select * from File"
 view = db.OpenView(query)
 view.Execute(None)
 cur_record = view.Fetch()     # do this until I get the record I want
 print cur_record.GetString(3) # do stuff with this value

然后如果这个文件存在,就用

msiexec /a c:\Temp\myfile.msi /qn TARGETDIR=c:\foo

把所有文件提取出来,然后用某种解析工具来查看我的字符串是否在里面。不过我希望能有一种更简单的方法。

3 个回答

0

MSI的API本身比较复杂,所以关键在于你怎么去简化它。如果你只是偶尔需要用到这个,可能直接在资源管理器里手动浏览cab文件会更简单一些。(文件是通过文件键来存储的,而不是通过文件名)。

1

要知道,在使用Python的时候,你需要处理Windows安装程序的自动化接口。这意味着你需要自己完成所有的数据库连接、查询和处理工作。

如果你能换到C#(或者说PowerShell),你就可以利用一些在Windows安装程序XML(WiX)部署工具基础(DTF)中存在的更高级的类。

using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Package;

static void FindAndExtractFiles(string packagePath, string longFileName)
{
    using (var installPackage = new InstallPackage(packagePath, DatabaseOpenMode.ReadOnly))
    {
        if(installPackage.FindFiles(longFileName).Count() > 0 )
            installPackage.ExtractFiles();
    }
}

你也可以把这个写成ComVisible(True),然后从Python中调用它。

3

注意,正如msilib的文档所说,“目前不支持读取.cab文件”。更一般来说,这个库是用来创建.msi文件的,而不是用来读取它们的。而且在标准库中没有其他可以满足你需求的工具。

所以,有几个选择:

  1. 找一个其他的库,比如pycabinet。我对这个库不太了解,这只是我搜索到的第一个结果;你可能需要自己再搜索一下。不过它声称提供一个类似于zipfile的接口来处理CAB文件,这听起来正是你需要的部分。
  2. 使用win32com(如果你安装了pywin32)或者ctypes(如果你喜欢挑战)来与底层的COM接口和/或经典的Cabinet API进行交互(我觉得这个API现在已经不推荐使用了,但仍然可以工作)。
  3. 使用IronPython而不是CPython,这样你可以使用更简单的.NET接口。

由于我这里没有Windows系统,所以无法测试这些方法,但这里有一个用IronPython而不是C#编写的Christopher Painter的.NET解决方案的草图:

import clr
clr.AddReference('Microsoft.Deployment.WindowsInstaller')
clr.AddReference('Microsoft.Deployment.WindowsInstaller.Package')
from Microsoft.Deployment.WindowsInstaller import *
from Microsoft.Deployment.WindowsInstaller.Package import *

def FindAndExtractFiles(packagePath, longFileName):
    with InstallPackage(packagePath, DatabaseOpenMode.ReadOnly) as installPackage:
        if installPackage.FindFiles(longFileName).Count() > 0:
            installPackage.ExtractFiles()

撰写回答