MSI 需要替换正在使用的文件计划删除,无法替换,然后文件丢失

问题描述

我有一个需要自行升级的应用程序,因此它运行一个 MSI,在该应用程序仍在运行时安装该应用程序。多年来一直成功运行,尽管 .exe、.dll 和其他东西正在使用中,但安装程序可以正常工作。但是我现在正在安装 CEF 浏览器组件,这给我带来了麻烦。

这是安装程序日志中的示例:

    *** the file is scheduled for removal
    MSI (s) (9C:7C) [11:44:06:372]: Executing op: FileRemove(,FileName=cef.pak,ComponentId={AEDD9CDD-C5CB-4260-A5D5-A7C13011CC97})
    MSI (s) (9C:7C) [11:44:06:374]: Verifying accessibility of file: cef.pak
    MSI (s) (9C:7C) [11:44:06:374]: Verifying accessibility of file: cef.pak
    MSI (s) (9C:7C) [11:44:06:374]: Using source file security for destination.
    MSI (s) (9C:7C) [11:44:06:375]: Note: 1: 2318 2: C:\Config.Msi\144c57.rbf 
    MSI (s) (9C:7C) [11:44:06:390]: Note: 1: 2329 2: 32 3: C:\xxxx\cef.pak 
    MSI (s) (9C:7C) [11:44:06:390]: Verifying accessibility of file: cef.pak
    MSI (s) (9C:7C) [11:44:06:393]: Executing op: FileRemove(,FileName=cef_100_percent.pak,ComponentId={AEDD9CDD-C5CB-4260-A5D5-A7C13011CC97})
    Info 1903. Scheduling reboot operation: Deleting file C:\xxxx\cef.pak. Must reboot to complete operation.

    *** The file is not overwritten and it sees the files as the same
    MSI (s) (9C:30) [11:44:09:050]: Executing op: Filecopy(SourceName=cef.pak,SourceCabKey=cef.pak,DestName=cef.pak,Attributes=512,FileSize=1961473,PerTick=65536,VerifyMedia=1,CheckCRC=0,InstallMode=58982400,HashOptions=0,HashPart1=885556701,HashPart2=2067920947,HashPart3=-336625845,HashPart4=-1380829775,)
    MSI (s) (9C:30) [11:44:09:056]: File: C:\xxxxcef.pak;   Won't Overwrite;    Won't patch;    Existing file is unversioned and unmodified - hash matches source file

因此安装程序尝试删除文件,知道它正在使用中并在重新启动后安排删除。但随后它查看是否应该安装该文件,查看它已经存在(它还没有被删除)并决定不做任何事情。所以重启后文件消失了,应用程序失败。

我尝试将安装程序设置为始终将文件替换为:

<Property Id='REINSTALLMODE' Value='amus'/>

在 Windows 7 上,这会产生所需的行为,文件被视为正在使用中,新文件被复制到临时位置,并计划在重新启动和删除重命名

    Line 1525: MSI (s) (CC:64) [12:19:41:201]: The file represented by File table key 'cef.pak' has no eligible binary patches
    Line 2618: MSI (s) (CC:64) [12:19:41:775]: Executing op: FileRemove(,ComponentId={AEDD9CDD-C5CB-4260-A5D5-A7C13011CC97})
    Line 2619: MSI (s) (CC:64) [12:19:41:776]: Verifying accessibility of file: cef.pak
    Line 2620: MSI (s) (CC:64) [12:19:41:776]: Verifying accessibility of file: cef.pak
    Line 2622: MSI (s) (CC:64) [12:19:41:779]: Note: 1: 2329 2: 32 3: C:\xxx\cef.pak 
    Line 2623: MSI (s) (CC:64) [12:19:41:779]: Verifying accessibility of file: cef.pak
    Line 2625: Info 1903. Scheduling reboot operation: Deleting file C:\xxx\cef.pak. Must reboot to complete operation.
    Line 3382: MSI (s) (CC:68) [12:19:42:632]: The file represented by File table key 'cef.pak' has no eligible binary patches
    Line 3801: MSI (s) (CC:68) [12:19:42:933]: Executing op: Filecopy(SourceName=cef.pak,InstallMode=62914560,)
    Line 3801: MSI (s) (CC:68) [12:19:42:933]: Executing op: Filecopy(SourceName=cef.pak,)
    Line 3802: MSI (s) (CC:68) [12:19:42:939]: File: C:\xxx\cef.pak;    Overwrite;  Won't patch;    REINSTALLMODE specifies all files to be overwritten
    Line 3803: MSI (s) (CC:68) [12:19:42:939]: Source for file 'cef.pak' is compressed
    Line 3805: MSI (s) (CC:68) [12:19:43:713]: Verifying accessibility of file: cef.pak
    Line 3806: MSI (s) (CC:68) [12:19:43:713]: Verifying accessibility of file: cef.pak
    Line 3808: MSI (s) (CC:68) [12:19:43:717]: Note: 1: 2329 2: 32 3: C:\xxx\cef.pak 
    Line 3809: MSI (s) (CC:68) [12:19:43:717]: Verifying accessibility of file: cef.pak
    Line 3810: Info 1603. The file C:\xxx\cef.pak is being held in use. Close that application and retry.
    Line 3811: Info 1903. Scheduling reboot operation: Deleting file C:\xxx\cef.pak. Must reboot to complete operation.
    Line 3812: Info 1902. Scheduling reboot operation: Renaming file C:\xxx\TBMBFB2.tmp to C:\xxx\cef.pak. Must reboot to complete operation.
    

但在 Windows 10 中不会发生这种情况,它会尝试立即替换文件而不使用临时文件,因此失败。

cef.pak
    Line 1895: MSI (s) (88:8C) [12:07:12:133]: The file represented by File table key 'cef.pak' has no eligible binary patches
    Line 3011: MSI (s) (88:8C) [12:07:13:761]: Executing op: FileRemove(,ComponentId={AEDD9CDD-C5CB-4260-A5D5-A7C13011CC97})
    Line 3012: MSI (s) (88:8C) [12:07:13:763]: Verifying accessibility of file: cef.pak
    Line 3013: MSI (s) (88:8C) [12:07:13:764]: Verifying accessibility of file: cef.pak
    Line 3016: MSI (s) (88:8C) [12:07:13:782]: Note: 1: 2329 2: 32 3: c:\xxx\cef.pak 
    Line 3017: MSI (s) (88:8C) [12:07:13:782]: Verifying accessibility of file: cef.pak
    Line 3019: Info 1903. Scheduling reboot operation: Deleting file c:\xxx\cef.pak. Must reboot to complete operation.
    Line 3747: MSI (s) (88:8C) [12:07:16:042]: The file represented by File table key 'cef.pak' has no eligible binary patches
    Line 4322: MSI (s) (88:8C) [12:07:16:577]: Executing op: Filecopy(SourceName=cef.pak,)
    Line 4322: MSI (s) (88:8C) [12:07:16:577]: Executing op: Filecopy(SourceName=cef.pak,)
    Line 4323: MSI (s) (88:8C) [12:07:16:584]: File: c:\xxx\cef.pak;    Overwrite;  Won't patch;    REINSTALLMODE specifies all files to be overwritten
    Line 4324: MSI (s) (88:8C) [12:07:16:584]: Source for file 'cef.pak' is compressed
    Line 4325: MSI (s) (88:8C) [12:07:16:585]: Re-applying security from existing file.
    Line 4326: MSI (s) (88:8C) [12:07:21:645]: Verifying accessibility of file: cef.pak
    Line 4327: MSI (s) (88:8C) [12:07:21:647]: Using source file security for destination.
    Line 4328: MSI (s) (88:8C) [12:07:21:647]: Note: 1: 2318 2: C:\Config.Msi\a5880.rbf 
    Line 4329: MSI (s) (88:8C) [12:07:21:651]: Note: 1: 1310 2: 0 3: c:\xxx\cef.pak 
    Line 4330: Info 1603. The file c:\xxx\cef.pak is being held in use. Close that application and retry.
    Line 4331: MSI (s) (88:8C) [12:07:21:652]: Product: XXX-- Error 1310. Error writing to file: c:\xxx\cef.pak.  System error 0.  Verify that you have access to that directory.
    Line 4332: 
    Line 4333: Error 1310. Error writing to file: c:\xxx\cef.pak.  System error 0.  Verify that you have access to that directory.
    Line 4334: MSI (s) (88:8C) [12:07:21:683]: Note: 1: 2265 2:  3: -2147287035 
    Line 4335: MSI (s) (88:8C) [12:07:21:684]: User policy value 'disableRollback' is 0
    Line 4336: MSI (s) (88:8C) [12:07:21:684]: Machine policy value 'disableRollback' is 0
    Line 4337: Action ended 12:07:21: InstallFinalize. Return value 3.
    Line 4347: MSI (s) (88:8C) [12:07:21:713]: Executing op: Filecopy(SourceName=C:\Config.Msi\a5880.rbf,DestName=c:\xxx\cef.pak,Attributes=32,FileSize=0,PerTick=0,VerifyMedia=0,ElevateFlags=3,InstallMode=4194304,)
    Line 4348: MSI (s) (88:8C) [12:07:21:713]: File: c:\xxx\cef.pak;    Overwrite;  Won't patch;    REINSTALLMODE specifies all files to be overwritten
    Line 4349: MSI (s) (88:8C) [12:07:21:715]: Re-applying security from existing file.
    Line 4350: MSI (s) (88:8C) [12:07:21:718]: Note: 1: 2318 2:  
    Line 4351: MSI (s) (88:8C) [12:07:21:720]: Verifying accessibility of file: cef.pak
    Line 4352: MSI (s) (88:8C) [12:07:21:722]: Using source file security for destination.
    Line 4353: MSI (s) (88:8C) [12:07:21:722]: Note: 1: 2318 2: C:\Config.Msi\a5881.rbf 
    Line 4354: MSI (s) (88:8C) [12:07:21:735]: File will have security applied from OpCode.
    Line 4355: MSI (s) (88:8C) [12:07:21:737]: Note: 1: 1310 2: 32 3: c:\xxx\cef.pak 
    Line 4356: Info 1603. The file c:\xxx\cef.pak is being held in use. Close that application and retry.
    Line 4357: MSI (s) (88:8C) [12:07:21:737]: Error in rollback skipped.   Return: 3
    Line 4358: Info 1310. Error writing to file: c:\xxx\cef.pak.  System error 32.  Verify that you have access to that directory.

有没有办法让其他类型的文件(例如 .pak 文件)具有这种行为?

解决方法

CEF 浏览器:我不熟悉这个组件。它是否在进程内运行? (作为一个 dll) - 或者它是否有一个 API 调用来关闭自己?您能否尝试为您的主要可执行文件实现下面描述的 Windows 重启管理器 "RegisterApplicationRestart" 功能?或者,如果有 API,您可以在组件的自定义操作中调用关闭?

数据文件:此外,这是一个您根本不应该安装的数据文件,而是应该让组件在启动时自行创建?如果是这种情况,您可以从安装程序中删除该文件并完全避免它与您的安装程序纠缠在一起吗?这对于未版本控制的文件尤其可取 - 由于 MSI file overwrite rules / file versioning rules 可能难以更新。


Restart Manager:Windows Installer 支持所谓的 Restart Manager feature。这个想法是让应用程序在安装过程中正常关闭 - 释放所有文件和资源锁定 - 然后让 Windows Installer 在所有文件更新后再次重新启动它。这意味着应用程序本身将在被告知关闭和重新启动时自行处理:"shut yourself down and restart when told"(保存所有数据和理想状态)。只有应用程序本身才能知道如何正确执行此操作?

技术支持:这是一篇关于如何在您的应用程序中实现对 Restart Manager feature 的支持的技术文章 - 来自领先部署工具的制造商 Advanced Installer .这是一篇不错的文章:https://www.advancedinstaller.com/user-guide/qa-vista-restart-manager.html

HRESULT WINAPI RegisterApplicationRestart(PCWSTR pwzCommandline,DWORD dwFlags);

整个想法基本上是更喜欢重新启动应用程序而不是重新启动操作系统 - 并且通常还避免重新启动。为此:1) 您的应用程序使用为其最终重启指定的命令行调用 RegisterApplicationRestart() - 它“注册”以进行重启管理。 2) 您的应用程序会监视 WM_QUERYENDSESSION messages 并在被告知这样做时以适当的方式优雅地关闭保存数据。 3) 然后重新启动管理器可以在安装完成后重新启动应用程序(可以禁用重新启动)。


链接: