175

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

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

我注意到类似的 Stack Overflow 问题Removing files when uninstalling WiX,但我想知道这是否可以更简单地使用调用批处理脚本来删除文件夹来完成。

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

4

6 回答 6

195

EDIT: Perhaps look at the answer currently immediately below.


This topic has been a headache for long time. I finally figured it out. There are some solutions online, but none of them really works. And of course there is no documentation. So in the chart below there are several properties that are suggested to use and the values they have for various installation scenarios:

alt text

So in my case I wanted a CA that will run only on uninstalls - not upgrades, not repairs or modifies. According to the table above I had to use

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

And it worked!

于 2009-04-08T20:34:43.577 回答
157

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

在此处输入图像描述

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

在此处输入图像描述

WiX表达式语法文档说:

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

Windows Installer Guide 中记录了这些属性(例如Installed

编辑:对第一张表的小修正;显然,“卸载”也可能发生REMOVETrue.

于 2013-07-12T05:36:25.227 回答
51

您可以使用自定义操作来执行此操作。您可以在下面为您的自定义操作添加引用<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 提示”窗口在闪烁时闪烁您的安装程序运行。

于 2008-11-26T18:44:32.170 回答
42

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

无论如何,要仅在卸载期间执行操作,请添加一个 Condition 元素:

REMOVE ~= "ALL"

~= 表示比较不区分大小写(即使我认为 ALL 总是大写)。有关详细信息,请参阅有关条件语法的 MSI SDK 文档

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

于 2008-11-26T19:29:19.863 回答
15

这是我制作的一组属性,使用起来比内置的东西更直观。这些条件基于上面由 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>

问题:

于 2018-07-06T17:31:25.657 回答
0

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

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

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

于 2014-04-25T12:51:30.270 回答