【发布时间】:2017-01-21 13:52:26
【问题描述】:
我想在安装时在我的 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);
}
它在 INSTALL 动作的最开始执行。 status 变量的值始终为 ERROR_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
Note: 1: 2263 2: Data2.cab 3: -2147287035 日志记录表明 MSI 错误 2263 Could not open stream [2]. 发生在自定义操作中。 -2147287035 的十六进制值是 FFFFFFFF80030005,很可能是 STG_E_ACCESSDENIED HRESULT - Access Denied。
我对这个错误的第一个猜测是 MSI 服务在尝试将源文件提取到记录中时无法访问它。我放宽了C:\work\Data2.cab 的权限,但没有运气。
然后我决定在MsiRecordSetStream() 调用中使用不存在文件的路径。
通过此更改 MsiRecordSetStream() 调用返回 161 错误。由于CreateDataCab() 中没有分支,它会一直执行到最后,并且MsiRecordSetStream() 成功后的所有 MSI 功能。
微星日志:
Action start 16:11:02: CreateDataCab.
MSI (s) (4C:D8) [16:11:02:596]: Invoking remote custom action. DLL: C:\windows\Installer\MSI4154.tmp, Entrypoint: JreCreateDataCab
MSI (s) (4C:54) [16:11:02:597]: Generating random cookie.
MSI (s) (4C:54) [16:11:02:605]: Created Custom Action Server with PID 34668 (0x876C).
MSI (s) (4C:6C) [16:11:02:682]: Running as a service.
MSI (s) (4C:6C) [16:11:02:684]: Hello, I'm your 64bit Impersonated custom action server.
MSI (s) (4C!74) [16:11:18:565]: Note: 1: 1101 2: C:\work\foo.cab 3: 2
MSI (s) (4C!74) [16:15:08:291]: Note: 1: 2263 2: Data2.cab 3: -2147287035
Note: 1: 1101 2: C:\work\Data2.cab 3: 2 表示发生 MSI 错误1101 Could not open file stream: [2]。这是 msi 日志中的额外错误,Note: 1: 2263 2: Data2.cab 3: -2147287035 仍然存在。
使用调试器进入CreateDataCab() 会产生以下观察结果:
-
Note: 1: 1101 2: C:\work\foo.cab 3: 2错误在MsiRecordSetStream()调用失败后立即添加到日志中,当它返回错误161时。 -
Note: 1: 2263 2: Data2.cab 3: -2147287035错误在MsiCloseHandle(hView)调用成功后立即添加到日志中。 - 将
_Streams表的内容转储到CreateDataCab()的开头和结尾表示新记录添加到表中成功。
这是我目前所拥有的。似乎发生了以下情况:
-
MsiRecordSetStream()新记录工作正常。如果指定正确的路径,系统可以将文件提取到记录中; - 向
_Streams表插入新记录的SQL请求成功; -
新增记录的
Data字段没有数据。
我很困惑在自定义操作中执行的 MSI 函数都通过了,但是 msi 日志文件中有错误。对_Streams 表的更改也部分成功:添加了新记录,但记录不包含数据。似乎有可能在运行时更新_Streams 表,但在表中存储数据时出现问题。
在构建时更新 msi 文件中的 _Streams 没有任何问题。一切正常。但是在运行时通过自定义操作执行此操作对我来说是错误的。我的 CA 函数中是否缺少某些内容?在运行时更改 _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;
}
当CreateDataCab() 被第一个CA 调用时,第一个dumpStream() 不输出任何内容,但第二个在c:/work/saved.cab 上写入一些内容。此文件显示为等于 c:/work/Data1.cab 的二进制文件,它被用作记录流的源。这表明_Streams表中的流数据可以更新就好了。数据持续存在并保留在数据库中,看不到它是在两个不同的MsiGetActiveDatabase()/MsiDatabaseClose() 范围内插入和检索的。非常好,但是Note: 1: 2263 2: Data2.cab 3: -2147287035 仍在日志中。
当为第二个 CA 调用 CreateDataCab() 时,事情就不那么好了。第一个 dumpStream() 清空 c:/work/saved.cab 文件。这意味着Data1.cab 记录仍在_Streams 表中,但流数据为空。
因为有人注意到_Streams 表很特别,所以我尝试了Binary 表。尝试修改现有记录导致Note: 1: 2259 2: 3: 4: 错误(数据库:[2] 表更新失败),即"UPDATE Binary SET Data = ? WHERE Name='Data1.cab'" SQL 查询不起作用。因此,我重新编写了 CA 代码以使用 MsiViewModify(hView, MSIMODIFY_INSERT_TEMPORARY, hRec) 并在运行安装之前从 Binary 表中删除了 Data1.cab 记录。
第一个CreateDataCab() CA 工作正常:插入正常,msi 日志中没有错误,第二个dumpStream() 正常。但是,当为第二个 CA 调用 CreateDataCab() 时,第一个 dumpStream() 转储零字节,随后的 MsiViewModify(hView, MSIMODIFY_INSERT_TEMPORARY, hRec) 失败。
_Streams 和 Binary table 的结果相同:
- 插入的记录在 CA 调用中保持不变。
- 添加记录中的流数据不会在 CA 调用中持续存在,并在第二个 CA 中设置为零长度。
在二进制表中插入新的临时记录不会导致 msi 日志中出现错误消息。
好消息是可以使用来自 CA 的流数据更改记录。不是那么坏的消息是,在Binary 表中,不可能更改现有记录中的数据。
坏消息是添加/更改流数据会损坏 db。
如果有人能质疑我的结论,我会非常高兴。
【问题讨论】:
标签: windows-installer custom-action