如何添加仅在卸载时(通过MSI)发生的WiX自定义操作?


160

我想修改MSI安装程序(通过WiX创建)以在卸载时删除整个目录。

我了解WiX中的RemoveFileRemoveFolder选项,但这些选项不够强大,无法递归删除安装后创建内容的整个文件夹。

我注意到了类似的堆栈溢出问题,即在卸载WiX时删除文件,但是我想知道是否可以更简单地通过调用批处理脚本来删除文件夹来完成。

这是我第一次使用WiX,但我仍然习惯于自定义操作。将在卸载时运行批处理脚本的自定义操作的基本示例是什么?

Answers:


188

编辑:也许看看下面立即给出的答案。


长期以来,这个话题一直让人头疼。我终于弄明白了。在线上有一些解决方案,但没有一个真正有效。当然也没有文档。因此,在下表中,建议使用几个属性,以及它们在各种安装方案中的值:

替代文字

因此,在我的情况下,我希望仅在卸载时运行的CA-不能升级,不能修复或修改。根据上表我不得不使用

<Custom Action='CA_ID' Before='other_CA_ID'>
        (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>

而且有效!


25
该图表中的值是否正确?为什么需要添加REMOVE =“ ALL”?NOT UPGRADINGPRODUCTCODE仅适用于卸载(根据图表),因此(NOT UPGRADINGPRODUCTCODE)AND(REMOVE =“ ALL”)也仅适用于卸载。REMOVE =“ ALL”似乎不必要。
托德·罗普

2
我同意@ToddRopog-该示例和真值表似乎不同意。真的对吗?
Tim Long

19
真值表有些错误。不UPGRADINGPRODUCTCODE的第一次安装是也是如此
尼尔


1
请确认:“已安装”和“安装”是不同的东西,只有Windows Installer设置“已安装”。我认为“安装”无效。
Micha Wiedenmann

139

yaluna的答案存在多个问题,属性名称也区分大小写,Installed拼写正确(INSTALLED不起作用)。上表应该是这样的:

在此处输入图片说明

同样假设完全修复和卸载属性的实际值可能是:

在此处输入图片说明

WiX的表达式语法文件说:

在这些表达式中,可以使用属性名称(请记住,它们是区分大小写的)。

这些属性记录在Windows Installer指南中(例如已安装

编辑:对第一张桌子的小修正;显然,仅REMOVE存在也会发生“卸载” True


3
REMOVE也似乎已设置为Change
szx 2014年

2
“升级”列是在旧版本的卸载过程中还是在新版本的安装过程中?
尼克·沃利

1
@NickWhaley:我已经有一段时间没有研究它了,但是我相信只有当安装的版本大于已经安装的版本时,“升级”选项才会出现。
ahmd0

1
@ ahmd0,当然可以。但是,在RemoveExistingProducts中发生的嵌套安装具有完全不同的属性集。就是您的“升级”列中的内容。其余升级与“安装”列相同。
尼克·惠利

1
@NickWhaley:在执行先前版本的卸载程序的过程中,“ REMOVE”选项对于“主要升级”适用,即1.0.0到2.0.0,而不是1.0.0到1.1.0。要在新版本的重大升级过程中运行自定义操作,您需要引用在升级MSI表中为该版本升级定义的ActionProperty。symantec.com/zh-CN/connect/articles/msi-upgrade-overview msdn.microsoft.com/en-us/library/aa372379%28v=vs.85%29.aspx
Chaoix 2015年

48

您可以通过自定义操作来执行此操作。您可以在下面的自定义操作中添加参考<InstallExecuteSequence>

<InstallExecuteSequence>
...
  <Custom Action="FileCleaner" After='InstallFinalize'>
          Installed AND NOT UPGRADINGPRODUCTCODE</Custom>

然后,您还必须在以下位置定义您的操作<Product>

<Product> 
...
  <CustomAction Id='FileCleaner' BinaryKey='FileCleanerEXE' 
                ExeCommand='' Return='asyncNoWait'  />

其中FileCleanerEXE是二进制文件(在我的情况下是一个执行自定义操作的c ++程序),该文件也定义在以下位置<Product>

<Product> 
...
  <Binary Id="FileCleanerEXE" SourceFile="path\to\fileCleaner.exe" />

真正的诀窍是“ Installed AND NOT UPGRADINGPRODUCTCODE自定义操作”中的条件,而您的操作将在每次升级时运行(因为升级实际上是卸载,然后重新安装)。如果要删除文件,可能在升级过程中不需要。

附带说明:我建议您使用C ++程序而不是批处理脚本来解决该问题,因为它提供了强大的功能和控制力-您可以防止“ cmd提示”窗口在刷新时闪烁您的安装程序运行。


3
25票赞成,但没有被接受的答案。欢迎来到安装者世界!:)
Christopher Painter

4
这实际上是行不通的。当您要执行安装在您自己的安装文件夹中的fileCleaner.exe时,这将是鸡与鸡蛋的问题:CustomAction将执行“ After ='InstallFinalize'”。此时,所有文件将从“安装”文件夹中删除。也是fileCleaner.exe。因此,您将无法通过CustomAction执行它。这个答案是完全错误的。我想知道42次投票!
西蒙

40

批处理脚本的最大问题是在用户单击“取消”时处理回滚(或在安装过程中出现问题)。解决此情况的正确方法是创建一个CustomAction,该操作将临时行添加到RemoveFiles表中。这样,Windows Installer即可为您处理回退案例。当您看到解决方案时,它变得异常简单。

无论如何,要使操作仅在卸载期间执行,请添加带有以下内容的Condition元素:

REMOVE ~= "ALL"

〜=表示不区分大小写(尽管我认为ALL始终是大写)。有关更多信息,请参见有关条件语法MSI SDK文档

PS:从来没有出现过我坐下来想过:“哦,批处理文件将是安装软件包中的一个很好的解决方案。实际上,找到其中包含批处理文件的安装软件包只会鼓励我退回产品以获得退款。


我正要使用批处理脚本,然后查看PS部分。感谢您的救助:)删除〜=“ ALL”为我工作。
ArNumb

12

这是我制作的一组属性,使用起来比内置的东西更直观。条件基于ahmd0上面提供的真值表。

<!-- truth table for installer varables (install vs uninstall vs repair vs upgrade) https://stackoverflow.com/a/17608049/1721136 -->
 <SetProperty Id="_INSTALL"   After="FindRelatedProducts" Value="1"><![CDATA[Installed="" AND PREVIOUSVERSIONSINSTALLED=""]]></SetProperty>
 <SetProperty Id="_UNINSTALL" After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED="" AND REMOVE="ALL"]]></SetProperty>
 <SetProperty Id="_CHANGE"    After="FindRelatedProducts" Value="1"><![CDATA[Installed<>"" AND REINSTALL="" AND PREVIOUSVERSIONSINSTALLED<>"" AND REMOVE=""]]></SetProperty>
 <SetProperty Id="_REPAIR"    After="FindRelatedProducts" Value="1"><![CDATA[REINSTALL<>""]]></SetProperty>
 <SetProperty Id="_UPGRADE"   After="FindRelatedProducts" Value="1"><![CDATA[PREVIOUSVERSIONSINSTALLED<>"" ]]></SetProperty>

这里是一些示例用法:

  <Custom Action="CaptureExistingLocalSettingsValues" After="InstallInitialize">NOT _UNINSTALL</Custom>
  <Custom Action="GetConfigXmlToPersistFromCmdLineArgs" After="InstallInitialize">_INSTALL OR _UPGRADE</Custom>
  <Custom Action="ForgetProperties" Before="InstallFinalize">_UNINSTALL OR _UPGRADE</Custom>
  <Custom Action="SetInstallCustomConfigSettingsArgs" Before="InstallCustomConfigSettings">NOT _UNINSTALL</Custom>
  <Custom Action="InstallCustomConfigSettings" Before="InstallFinalize">NOT _UNINSTALL</Custom>

问题:


这是一个很好的解决方案。切记还要考虑PATCH和MSIPATCHREMOVE条件。
加雷特·贾克斯

在真值表中,您是要使用PREVIOUSVERSIONSINSTALTAL而不是ahmd0所使用的UPGRADINGPRODUCTCODE吗?我没有在MSI属性参考页(docs.microsoft.com/zh-cn/windows/win32/msi/property-reference)上看到对PREVIOUSVERSIONSINSTALLED的任何参考。
帕特里克

属性的某些谓词没有考虑ahmd0表中的所有行(已安装,REINSTALL,UPGRADINGPRODUCTCODE和REMOVE)。你能解释为什么吗?
帕特里克(Patrick)

0

我使用了用C ++ DLL单独编码的“自定义操作”,并使用DLL在卸载时使用以下语法调用了相应的函数:

<CustomAction Id="Uninstall" BinaryKey="Dll_Name" 
              DllEntry="Function_Name" Execute="deferred" />

使用上面的代码块,我能够在卸载时运行C ++ DLL中定义的任何函数。仅供参考,我的卸载功能包含有关清除当前用户数据和注册表项的代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.