【问题标题】:Do not override the existing file when we install the .exe file我们安装 .exe 文件时不要覆盖现有文件
【发布时间】:2025-12-01 14:35:02
【问题描述】:

我已经安装了 .exe 应用程序(比如说,1.0 版)。安装应用程序后,我可以在本地路径(C:\Program Files (x86))中看到几个 DLL 和几个 .XML 配置文件。现在,在启动新版本并安装它之后,我不想在我的本地路径中单独覆盖现有的 XML 文件。有什么办法吗?

我的 EXE 是使用 WIX 安装程序创建的,该项目是在 Visual Studio 2015 中构建的。

【问题讨论】:

    标签: c# wix windows-installer


    【解决方案1】:

    您的应用程序是如何工作的?它是否会随着时间的推移将设置写入这些 XML 文件,因此您想要保留它们?如果是这样,它们存储在哪里?在每台机器或每用户位置?

    让我尝试为您描述一些您可能会面临的问题 - 您可能无法提前预见(未经测试)。

    关于文件覆盖的一些细节

    1. 文件版本控制规则:MSI 内置支持不覆盖自安装以来已修改的非版本控制文件(创建和修改日期为不同的)。这些是“File Versioning Rules”。它们有些复杂和奇怪 - 您一定要阅读链接内容 - 真正的 McCoy。因此,在更新期间不应替换修改过的非版本化文件 - 但有许多复杂性和陷阱 - 大多数人似乎都陷入其中之一。

    2. 主要升级文件恢复:也许“意外文件覆盖”最常见的并发症根本不是覆盖。它与重大升级有关 - 或者如果您愿意,可以安装您的下一个产品版本。

      • 如果您使用主要升级来安装您的下一个版本,并且您在 InstallExecuteSequence 的早期安排了 RemoveExistingProducts,那么实际上所有文件都将被卸载,然后在其原始版本中重新安装。它们似乎已被覆盖,但它们已被卸载,然后改为“全新”安装。

      • 这完全是由于一个非常奇怪的决定,即 MSI 将愉快地卸载修改过的文件,即使它们有资格不被覆盖。您需要一种方法来定义根本不卸载的文件。这将我们带到下一点:

    3. 永久和永不覆盖标志:每个组件都有两个与文件覆盖和文件保存相关的重要设置:永不覆盖标志和永久旗帜。这确实是指示文件不应被替换或卸载的好方法。不过……

      • 仅设置永不覆盖标志无助于解决上述主要升级问题,该文件仍会被卸载然后重新安装,因此它看起来已被覆盖(但已恢复)。

      • 如果您仅设置永久标志,则文件将根据文件版本规则被覆盖 - 前提是未使用 REINSTALLMODE 等功能在命令行中指定修改(请参阅下面的部分)。 (我需要再次验证这一点才能 100% 确定。有很多因素:组件只能在较旧或较新的设置中永久设置 - 或在两种设置中,主要升级可以提前或延迟卸载,REINSTALLMODE 有很多口味等...每个变量都可以更改覆盖行为)

      • 换句话说,建议同时设置永久和永不覆盖标志以保护设置文件。

    4. REINSTALLMODE:REINSTALLMODE 是File Overwrite / Versioning Rules (symantec) 的“修改器”或禁用器。可以在安装期间将REINSTALLMODE property 设置为等于amus - 这将(除其他外)在安装期间强制覆盖所有文件,而不管上述文件版本规则。

      • amusREINSTALLMODE参数说明:

        • a - 强制重新安装所有文件,无论版本如何
        • m - 重写所有进入 HKLM 或 HKCU 的注册表项
        • u - 重写所有进入 HKCU 或 HKEY_USERS 的注册表项
        • s - 重新安装快捷方式和图标
      • 其中一天,我将写出有关 REINSTALLMODE 及其实现的问题。它在 MSI 中是一个极具破坏性的概念,永远不应该被使用。问题是这是一个通用命令行,可以传递给任何 MSI 包并适用于整个设置(为什么不能至少是每个功能?)。如果指定了amus,我想让我的包中止。让我快速记下一些问题:

        • 降级:可以在系统范围内降级共享文件。这些文件可以来自合并模块,因此人们可能没有意识到他们以系统文件夹为目标。 Windows 10 现在可以很好地保护其操作系统文件,但仍有其他文件可以在没有操作系统保护的情况下降级。
        • 版本不一致:可能会导致版本资产不一致,因为旧包可以在新包之后安装,并且只能降级某些共享文件。
        • 设置清除:可以降级或清除非版本控制文件(和注册表设置)中的设置。
        • 重启提示:由于尝试不必要地替换相同版本的正在使用的文件,可能会导致请求的重启次数显着增加。有一些功能可以避免这种情况,但它们通常没有正确使用。 Restart Manager, more on restart manager.
        • 然后还有更多...:还有几个非常具体的问题。总有一天我会把它们都写下来。我认为。也许不吧? :-)。
          • Windows 文件保护 (WFP):也许我可以补充一点,某些旧合并模块包含现在受操作系统保护的文件,并尝试替换它们用于 可能会触发运行时错误。这现在由 Windows 10 “静默”处理。
          • Registryamusmu 开关无论 a 开关如何,都会彻底重置组件(而不是自定义操作代码)写入的注册表数据(至少我上次尝试的情况是这样)。对我的MSI anti-pattern list 赞不绝口(请参阅“潜在的反模式”部分)。
          • 未知数GACWinSxS。我什至不知道 GAC 是如何运作的?并排设计是不可降级的吗?有人会想。除非您直接在程序集文件夹内四处寻找 - 希望这会触发运行时错误 - 我没有尝试过。事实上,我认为不会。 See this issuepublisher configuration files(查看“线程”中的所有推文)。

    说实话,这有点疯狂,但这就是技术的运作方式。所以,让我们停止抱怨并尝试解决问题。有什么办法可以解决这个问题?

    处理设置文件保存问题的一些选项

    总的来说,我想你有几个选择:

    1. 使用小升级来升级您的应用程序。然后,您的文件永远不会被卸载和重新安装,因此会像进行重大升级一样被还原。

      • 次要升级不会卸载现有安装 - 它会直接升级它。适用常规文件版本控制规则(修改后的非版本控制文件不会被替换) - 除非使用 REINSTALLMODE 来修改此逻辑。

      • 然而,很少有人只通过小的升级就能成功——他们是too restrictive and limited。我想说的几乎没有。

    2. 在 InstallExecuteSequence 早期使用 RemoveExistingProducts 进行重大升级,并为托管您的设置文件的组件启用永久且永不覆盖。使用重大升级时,主要选择是提前卸载旧版本还是延迟卸载旧版本,这种选择具有巨大的影响。

      • 这部分需要清理一下。很快就会发生......2019 年 12 月:我们回来了。多年后。今天发生了一些小的清理工作——其他的不多。我将继续承诺会更多地清理它。空洞的承诺使世界转转。如果这一切看起来都在抱怨,please read the major benefits of MSI(快速列表)。 MSI 对以前的部署技术进行了至关重要的改进,但还存在一些重大挑战。

      • 提前卸载意味着您​​所做的任何组件引用错误都不会导致任何问题。如果你愿意,他们会更“宽容”。很多人最终都使用这种方法,因为它最“宽容”了组件引用错误和整体“健壮性”。

      • 提前卸载也意味着在升级过程中可以卸载并重新安装修改过的文件,导致它们看起来被覆盖,但它们被还原(如上所述)。

      • 解决上述问题的方法是将文件的托管组件设置为永久永不覆盖

      • 提前卸载意味着可以打破复杂的组件创建规则,您的升级仍然可以工作。破坏组件创建规则是很常见的。如果您使用这种方法,您应该将升级期间要保留的文件设置为永久文件,并且永远不要覆盖。这应该可以防止修改文件被卸载并安装新文件的问题 - 产生文件已被覆盖的印象。

    3. 在 InstallExecuteSequence 后期使用 RemoveExistingProducts 进行重大升级

      • 后期卸载主要升级安装不会卸载和重新安装旧设置和新设置中存在于同一位置的文件 - 即使有问题的文件未设置为永久且永不覆盖(允许适当的也可以卸载文件 - 不添加任何自定义逻辑)。

      • 这种升级基本上是作为“补丁”安装的 - 保持版本之间不变的文件不变,然后根据上述文件版本控制规则升级其他文件。

      • 它们将根据正常的文件版本控制规则被覆盖(前提是您没有将 REINSTALLMODE 设置为等于 amus - 在这种情况下,您降级所有内容并清除设置)。

      • 上一个答案:How to keep a config file when major upgrade in wix v3.8?

    4. 设置文件的自定义备份机制:许多最终实施复杂的自定义操作,备份设置文件并在安装运行后将它们放回原位。 p>

      • 我不喜欢这种方法,因为它可能会导致自定义操作失败,但许多人使用此选项。这通常是应用程序版本 2 中添加的一项功能,因为相关文件在版本 1 的设置中并未设置为永久且“永不覆盖”。

      • 获取 sequencingimpersonation(您在其中运行的用户/系统上下文)和 调节 适合此类自定义操作是 一点都不简单。

      • 作为所涉及的复杂性的具体示例,这里是以前的问题/答案,其中已实现此类功能并且存在确定确切发生的问题的问题:Wix Tools update uses old custom actions

        • 复杂性:为了使备份和恢复操作在不同的安装模式(全新安装、升级、维修、修改等)

        • Condition Debugger Light (practical tip!):为了解决复杂的条件,我喜欢在实际测试中非常具体条件而不是过多的分析(是的,无论如何你都会失败 - 不知何故)。

          • 这是我使用的总体方法:VBScript condition tester。它本质上是一种来自受条件自定义操作的对话框方法 - 它在现实生活中显示自定义操作何时运行而无需任何日志记录或复杂调试。

          • 只需在不同模式下运行安装程序,看看是否看到对话框:installuninstallmodify, repairpatchingmajor upgradesuspended-resume...

    5. 从部署中删除设置文件:这是我最喜欢的选项,虽然听起来有点奇怪。任何可以使您的设置“笨拙”且不那么复杂的东西都会使其在实际使用中更加可靠。

      • 安装后要更改的文件根本不应该安装,而是由应用程序生成或从模板文件复制到要更改的位置。

        • 在这些情况下,任何部署问题都不会干扰您宝贵的设置文件 - 除非您执行奇怪的自定义操作(您不应该这样做)。

        • 您的安装程序不知道这些设置文件的存在,因此它们将始终保持独立。这通常是您想要的。

        • 您可以使用自己的清理功能在卸载时删除它们,但我的建议是不要管它们:它们是用户数据。

      • 我在这个答案中写了与每个用户和设置文件部署相关的一般问题:Create folder and file on Current user profile, from Admin Profile。请至少快速浏览一下,看看您是否可以通过消除复杂性而不是接受它来改进您的部署。

        • 基本上有多种方法可以在设置中部署设置和用户文件(Active Setupself-repair 等...)。 p>

        • 然后有模板文件,您可以“实例化”并避免设置(如上所述没有干扰)。

        • 在某些情况下,您可以使用应用程序内部默认值而不是设置文件 - 消除所有部署注意事项。

        • 最后,您可以从网络中检索设置(数据库),而不是使用旧式设置文件。

    实用建议:如果可以,请使用数据库进行设置或某种形式的云方法从设置中获取设置文件。部署你的 仅限二进制!以及在启动时进行设置文件或注册表设置所需的模板。这将极大地简化您的设置。


    链接:

    【讨论】:

      【解决方案2】:

      您可以做的一件事是防止安装程序卸载最新版本。您可以为配置文件添加NeverOverwrite属性。

      我找到了这个解决方案here

      【讨论】:

        【解决方案3】:

        假设您正在进行重大升级,有两种通用解决方案:

        如果您的升级计划“延迟”,例如 afterInstallExecute,则升级“安装在”旧产品之上,因此应用了文件覆盖规则。对于数据文件,这意味着如果文件在安装后已更新(修改日期 > 创建日期),则不会被覆盖:

        Neither File Has a Version

        换句话说,升级将起作用,您根本不需要做任何事情。

        如果您的升级安排在“早期”,例如 afterInitinalize,则实际上是卸载后安装,因此您将在卸载时丢失文件。在这些情况下,通常最好在升级之前(在 RemoveExistingProducts 之前)使用自定义操作将文件复制到安全位置,然后在升级中恢复它。或者让应用程序将实际数据保存在与安装文件不同的副本文件中,因此安装文件是“模板”而不是实际的实时文件。

        【讨论】: