Windows installer 从自定义操作在msi中插入cab文件

Windows installer 从自定义操作在msi中插入cab文件,windows-installer,custom-action,Windows Installer,Custom Action,我想在安装时在msi包中插入cab文件。这可能吗? 有立即的自定义操作: UINT __stdcall CreateDataCab(MSIHANDLE hInstall) { MSIHANDLE hRec = MsiCreateRecord(1); UINT status = MsiRecordSetStream(hRec, 1, T("C:\\work\\Data2.cab")); MSIHANDLE hDb = MsiGetActiveDatabase(hInsta

我想在安装时在msi包中插入cab文件。这可能吗? 有立即的自定义操作:

UINT __stdcall CreateDataCab(MSIHANDLE hInstall) {
    MSIHANDLE hRec = MsiCreateRecord(1);
    UINT status = MsiRecordSetStream(hRec, 1, T("C:\\work\\Data2.cab"));

    MSIHANDLE hDb = MsiGetActiveDatabase(hInstall);
    MSIHANDLE hView = 0;
    status = MsiDatabaseOpenView(hDb, _T("INSERT INTO `_Streams` (`Name`, `Data`) VALUES ('Data2.cab', ?)"), &hView);
    status = MsiViewExecute(hView, hRec);
    status = MsiViewClose(hView);
    status = MsiCloseHandle(hView);
    status = MsiCloseHandle(hRec);
    status = MsiCloseHandle(hDb);
}
它在安装操作的最开始执行。
status
变量的值始终为
ERROR\u SUCCESS
。看来一切都很好。但是,在msi日志中,我可以看到触发的错误:

Action start 14:29:27: INSTALL.
MSI (s) (4C:14) [14:29:27:049]: Running ExecuteSequence
MSI (s) (4C:14) [14:29:27:049]: Doing action: SetSilentInstall
Action start 14:29:27: SetSilentInstall.
MSI (s) (4C:14) [14:29:27:051]: PROPERTY CHANGE: Adding SILENT property. Its value is '/s'.
Action ended 14:29:27: SetSilentInstall. Return value 1.
MSI (s) (4C:14) [14:29:27:052]: Doing action: CreateDataCab
Action start 14:29:27: CreateDataCab.
MSI (s) (4C:1C) [14:29:27:072]: Invoking remote custom action. DLL: C:\windows\Installer\MSI3EC2.tmp, Entrypoint: CreateDataCab
MSI (s) (4C:B4) [14:29:27:074]: Generating random cookie.
MSI (s) (4C:B4) [14:29:27:079]: Created Custom Action Server with PID 32604 (0x7F5C).
MSI (s) (4C:14) [14:29:27:147]: Running as a service.
MSI (s) (4C:14) [14:29:27:151]: Hello, I'm your 64bit Impersonated custom action server.
MSI (s) (4C!50) [14:32:22:069]: Note: 1: 2263 2: Data2.cab 3: -2147287035 
注意:1:2263 2:Data2.cab 3:-2147287035
日志记录表明MSI错误
2263无法打开流[2]。
在自定义操作中发生。
-2147287035
的十六进制值为
FFFFFFFF80030005
,很可能是
STG_E_ACCESSDENIED HRESULT-拒绝访问
。 我对这个错误的第一个猜测是,MSI服务在试图将源文件提取到记录中时无法访问它。我放宽了
C:\work\Data2.cab的权限,但运气不好。
然后我决定在
msicrecordsetstream()调用中为不存在的文件使用路径。
通过此更改
MsiRecordSetStream()
调用返回
161
错误。由于
CreateDataCab()
中没有分支,因此它一直执行到最后,并且在
msicrecordsetstream()
成功后,所有MSI函数都将被执行。 msi日志:

注意:1:1101 2:C:\work\Data2.cab 3:2
表示出现MSI错误
1101无法打开文件流:[2]
。这是msi日志中的额外错误,
注意:1:2263 2:Data2.cab 3:-2147287035
仍然存在

使用调试器在
CreateDataCab()
中单步执行会导致以下观察结果:

  • 注意:1:1101 2:C:\work\foo.cab 3:2
    错误在返回错误时立即添加到日志中
  • 注意:1:2263 2:Data2.cab 3:-2147287035
    错误在成功调用
    MsiCloseHandle(hView)
    后立即添加到日志中
  • CreateDataCab()
    的开头和结尾转储
    \u Streams
    表的内容表示新记录已成功添加到表中
  • 这就是我现在拥有的。似乎发生了以下情况:

  • msicrecordsetstream()
    用于新记录的操作正常。如果指定了正确的路径,系统可以将文件提取到记录中
  • \u Streams
    表插入新记录的SQL请求成功
  • 数据
    新增记录字段不含数据
  • 我很困惑,自定义操作中执行的MSI函数都通过了,但MSI日志文件中有错误。对
    \u Streams
    表的更改也部分成功:添加了新记录,但该记录不包含数据。似乎有可能在运行时更新
    \u Streams
    表,但在表中存储数据时出现问题

    我在构建时更新msi文件中的
    \u Streams
    没有问题。一切正常。但在运行时从自定义操作执行此操作对我来说是不正确的。我的CA功能中是否缺少某些内容?在运行时更改
    \u Streams
    是否合法

    UPD:我对这个问题做了进一步的调查。我修改了我的msi,从两个CA调用了两次
    CreateDataCab()
    。还修改了
    CreateDataCab()
    本身。伪代码如下:

    CreateDataCab() {
        dumpStream();
    
        openDb;
        _Streams["Data1.cab"].Data = "c:/work/Data1.cab";
        commitDb;
        closeDb;
    
        dumpStream();
    }
    
    dumpStream() {
        openDb;
        if ("Data1.cab" in _Streams) {
            save _Streams["Data1.cab"].Data to "c:/work/saved.cab";
        }
        closeDb;
    }
    
    当为第一个CA调用
    CreateDataCab()
    时,第一个
    dumpStream()
    不会输出任何内容,但第二个on会将某些内容写入
    c:/work/saved.cab
    。此文件显示为二进制文件,等于用作记录流源的
    c:/work/Data1.cab
    。这表明流数据可以在
    \u Streams
    表中很好地更新。数据持续存在并保留在数据库中,以便看不到它是在两个不同的
    MsiGetActiveDatabase()/MsiDatabaseClose()
    作用域中插入和检索的。非常好,但是
    注意:1:2263 2:Data2.cab 3:-2147287035
    仍在日志中

    当为第二个CA调用
    CreateDataCab()
    时,情况就不那么好了。第一个
    dumpStream()
    清空
    c:/work/saved.cab
    文件。这意味着
    Data1.cab
    记录仍在
    \u Streams
    表中,但流数据为空

    正如大家所注意到的,
    \u Streams
    表是特殊的,我用
    二进制
    表进行了实验。试图修改现有记录导致
    注意:1:22592:3:4:
    错误(数据库:[2]表更新失败),即
    “更新二进制集数据=?其中Name='Data1.cab'”
    SQL查询不起作用。因此,在运行安装之前,我重新编写了CA代码以使用
    MsiViewModify(hView,MSIMODIFY\u INSERT\u TEMPORARY,hRec)
    ,并从
    Binary
    表中删除了
    Data1.cab
    记录

    第一个
    CreateDataCab()
    CA工作正常:插入OK,msi日志中没有错误,第二个
    dumpStream()
    OK。但是,当为第二个CA调用
    CreateDataCab()
    时,第一个
    dumpStream()
    转储零字节,随后的
    msiewmodify(hView,MSIMODIFY\u INSERT\u TEMPORARY,hRec)
    失败

    _流和二进制表的结果相同:

  • 插入的记录在CA调用中保持不变
  • 添加的记录中的流数据不会跨CA调用持久化,并在第二个CA中设置零长度
  • 在二进制表中插入新的临时记录不会导致msi日志中出现错误消息。 好消息是,可以使用来自CA的流数据更改记录。不太坏的消息是,在
    Binary
    表中,不可能更改现有记录中的数据。 坏消息是,添加/更改流数据会损坏数据库


    如果有人能质疑我的结论,我会非常高兴。

    在运行时,数据库是只读的。您必须使用而不是仅仅插入,我不确定它是否适用于
    \u Streams
    表。(我一般也有
    CreateDataCab() {
        dumpStream();
    
        openDb;
        _Streams["Data1.cab"].Data = "c:/work/Data1.cab";
        commitDb;
        closeDb;
    
        dumpStream();
    }
    
    dumpStream() {
        openDb;
        if ("Data1.cab" in _Streams) {
            save _Streams["Data1.cab"].Data to "c:/work/saved.cab";
        }
        closeDb;
    }