我想在安装时在我的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函数都会执行。
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;
}
当为第一个CA调用CreateDataCab()
时,第一个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)
并从Data1.cab
表中删除了Binary
记录。
第一个CreateDataCab()
CA工作正常:插入OK,msi日志中没有错误,第二个dumpStream()
确定。但是,当为第二个CA调用CreateDataCab()
时,第一个dumpStream()
转储零字节和后续MsiViewModify(hView, MSIMODIFY_INSERT_TEMPORARY, hRec)
失败。
_Streams和Binary表的结果完全相同:
在二进制表中插入新的临时记录会导致msi日志中没有错误消息。
好消息是可以使用来自CA的流数据来更改记录。不是坏消息是,在Binary
表中,不可能更改现有记录中的数据。
坏消息是添加/更改流数据会破坏db。
如果有人可以质疑我的结论,我会很高兴。
答案 0 :(得分:1)
在运行时,数据库是只读的。您必须使用INSERT ... TEMPORARY而不仅仅是INSERT,我不确定它是否适用于_Streams
表。 (我通常也更幸运使用基于记录的API而不是基于SQL的API,即打开表的视图,构建记录,然后在这种情况下MsiViewModify(hView,MSIMODIFY_INSERT_TEMPORARY,hRec) );但我不认为这是问题所在。)
那就是说,这个用例对我来说听起来非常不寻常,所以我会非常谨慎。因为记录是临时的,所以每当MSI运行时你都必须这样做 - 而不仅仅是第一次安装。请务必测试您要支持的所有方案,包括卸载,修复和任何升级方法。特别是如果你可以提供小的升级补丁。