Wix自定义卸载操作 - 如何在MSI删除文件之前运行

6

我有一个自定义操作,可以将文件添加到安装目录中。当程序被卸载时,另一个自定义操作会尝试删除这些文件,从而可以删除安装目录。

问题在于,我的自定义卸载操作在标准安装文件被删除后运行,因此安装目录仍然存在,尽管它是空的。

配置看起来类似于这样:

<CustomAction Id="AddFilesAction" BinaryKey="installerActions" DllEntry="AddFiles" Execute="deferred" Return="check" Impersonate="no" />
<CustomAction Id="CleanupAction" BinaryKey="installerActions" DllEntry="Cleanup" Execute="deferred" Return="check" Impersonate="no" />

<InstallExecuteSequence>
  <Custom Action="CleanupAction" Before="InstallFiles">Installed</Custom>
  <Custom Action="AddFilesAction" After="InstallFiles">NOT Installed</Custom>
</InstallExecuteSequence>

我可以让CleanupAction在msi开始删除安装文件之前运行,这样自定义文件就已经被删除了,msi可以删除主安装目录吗?

3个回答

10
问题在于我的自定义卸载操作在删除标准安装文件之后运行。这是因为您将其安排在InstallFiles之前,而InstallFiles标准的InstallExecuteSequence中位于RemoveFiles之后。您还可以在 Orca 或 InstEd 等编辑器中打开 MSI 文件,并查看 InstallExecuteSequence 表。按照Sequence列排序即可查看执行顺序。

我能让清理操作在安装程序开始删除安装文件之前运行吗

当然可以,只需在RemoveFiles之前安排它即可:
<Custom Action="CleanupAction" Before="RemoveFiles">
    (REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE)
</Custom>

编辑:在 Stein Åsmul 提醒我之后,我还改进了自定义操作条件。请查看他的回答以获取详细解释。


如果您还不知道,WiX已经支持删除应用程序生成的文件,这可能能够替代您的自定义操作。它以 RemoveFileutil:RemoveFolderEx 元素的形式出现。

如果这些元素不能满足您的需求,因此您仍然需要自定义操作,我建议在运行时(在 immediate 自定义操作中)向 RemoveFile 表添加要删除的文件的临时记录。这使您可以利用MSI引擎进行实际的文件删除,即如果用户决定取消卸载或发生错误,则会自动回滚。我过去也这样做过(在发明 RemoveFolderEx 之前),因此如果您需要更多信息,请提出另一个问题。


太好了。谢谢你。添加临时记录听起来很有趣,如果我无法弄清楚,我会再提一个问题。谢谢。 - František Žiačik
顺便提一下,我试图用RemoveFolderEx替换自定义清理操作,但它的效果与我的清理操作在RemoveFiles之后运行时相同。这个文件夹(实际上是安装文件夹中的子文件夹)被删除了,但卸载完成后根安装文件夹(现在为空)仍然在原地。不确定为什么会这样。 - František Žiačik
1
@FrantišekŽiačik 请尝试向位于根安装文件夹中的组件添加<RemoveFolder Id="RemoveRootDir" On="uninstall"/>元素。 - zett42
是的,这个可以工作,尽管在我看来它看起来有点不太正规。 - František Žiačik

7
短答案:您的条件和排序似乎有误。请在RemoveFiles之前安排您的清理自定义操作,并可能设置更好的条件,使操作仅在所需时运行(而不是在意外的安装模式下运行)。下面我建议使用(REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE)。如果您使用此条件,请进行彻底测试。该条件将在下面解释。
快速示例:
<InstallExecuteSequence>
  <Custom Action="CleanupAction" 
          Before="RemoveFiles">(REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE)</Custom>
</InstallExecuteSequence>

请确保阅读下面的详细信息。您可能还想收紧复制文件操作的条件 - 否则它也将在主要升级时运行 - 这可能是您想要的,也可能不是。
自定义操作替代方案: 如果可能,请避免使用自定义操作 - 概述一些自定义操作问题 - 它们是严重的。自定义操作是部署失败的主要原因。您确定需要它们吗?通常有其他方法可以使用内置的MSI功能或WiX特定构造实现您在自定义操作中实现的内容。常见示例包括:安装服务、删除文件、更新XML文件或INI文件等... 但有时确实需要自定义操作- 显然。Zett42已经很好地介绍了替代方案,因此我不会在此重复 - 请查看他/她的答案。
RemoveFiles:这里还有进一步的问题 - 我将尝试在下面描述这些问题 - 但是当标准操作RemoveFiles运行时,文件将被卸载。换句话说,在InstallExecuteSequence中需要安排清理自定义操作在此标准操作之前运行。
条件:您的清理自定义操作的条件为Installed,这将使自定义操作在修改修复小型升级补丁中运行,以及在卸载主要升级启动的卸载中运行。这很可能不是您想要的。要指定仅在卸载时运行,最常见的条件是REMOVE~="ALL"。这将使清理在手动启动的卸载和主要升级启动的卸载上发生(我认为这不是您想要的)。您可以尝试(REMOVE~="ALL") AND (NOT UPGRADINGPRODUCTCODE)(仅在常规卸载时运行 - 不在主要升级卸载时运行)。

提示:即使是经验丰富的WiX/MSI用户,条件也很容易搞错。以下是一些可能有所帮助的资源:


一些进一步的链接(供参考)


感谢您的另一个观点。我们使用自定义操作有两个原因。首先,客户必须在安装过程中选择一个ZIP文件,该文件必须解压缩到安装目录中。其次,我们需要更新Java属性文件(而不是INI文件)。 - František Žiačik
错误的自定义操作条件被成功捕获。 - zett42
顺便提一下,我已经固定了产品ID(例如Id="SOME-GUID"而不是Id="*"),因为我需要能够在脚本中执行自动卸载,如msiexec /x {SOME-GUID}。但是这种方式似乎不允许我进行升级。 - František Žiačik
2
您需要更改产品GUID才能使用包的主要升级。您只使用次要升级吗?相对容易地查询机器以确定安装了哪些MSI软件包(以查找产品GUID),即使远程访问,如果您的AD设置正确。这里是有关从远程计算机检索升级代码的简介 - Stein Åsmul

0

我不建议在InstallInitialize和InstallFinalize之间安排您的操作。在初始化之前放置文件,在完成之后清理文件。请注意,InstallFinalize后您将失去属性值,您需要考虑到这一点。


1
InstallFinalize之后或InstallInitialize之前运行的自定义操作不会以提升权限运行,因此在尝试执行特权操作时可能会出现错误消息 - 除非整个安装从具有管理员权限的命令提示符(或等效机制)启动。以提升的权限运行意味着标准用户在安装期间(在InstallInitialize和InstallFinalize之间)以临时管理员权限运行,而使用“真实”管理员权限启动设置意味着整个过程都以管理员权限运行。企业部署使用提升的权限。 - Stein Åsmul
1
违反Windows Installer最佳实践的做法是在“事务”之外更改系统,该事务在InstallInitializeInstallFinalize之间定义。所有对系统的更改都应在此序列中执行,并且如果安装失败,则应正确回滚更改。坦白地说,我不知道为什么可以在InstallFinalize之后插入自定义操作 - 你不能在那里插入“延迟模式自定义操作”,但可以插入“立即模式自定义操作”。 - Stein Åsmul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接