【问题标题】:How to keep a config file when major upgrade in wix v3.8?在 wix v3.8 中进行重大升级时如何保留配置文件?
【发布时间】:2013-04-24 07:39:44
【问题描述】:

我想在 msi 安装程序进行重大升级时保留一个配置文件。对于配置文件,我在安装时进行了更改。代码如下:

<Component Id="MODIFYCONFIG" Guid="6A1D7762-B707-4084-A01F-6F936CC159CE" Win64="yes">
    <File Id="Application.config" Name="Application.config" Vital="yes" KeyPath="yes" Source="Resource\Application.config"></File>
    <util:XmlFile Id="SetValueIP" Action="setValue" Permanent="yes" File="[#Application.config]"
         ElementPath="/configuration/applicationSettings/Application.Properties.Settings/setting[\[]@name='IpAddress'[\]]/value"  Value="[IPADDRESS]" Sequence="1"/>
    <util:XmlFile Id="SetValuePort" Action="setValue" Permanent="yes" File="[#Application.config]"
         ElementPath="/configuration/applicationSettings/Application.Properties.Settings/setting[\[]@name='IpPort'[\]]/value"  Value="[PORT]" Sequence="2"/>
    <Condition>Not Installed</Condition>
  </Component>
  <Component Id="KEEPCONFIG" Guid="F7F173AA-C2FD-4017-BFBC-B81852A671E7" Win64="yes">
    <RemoveFile Id="ApplicationConfig" Name="Application.config" On="uninstall"/>
    <Condition>(REMOVE=ALL) AND (NOT UPGRADINGPRODUCTCODE)</Condition>
  </Component>

但是当发生重大升级时,文件不会被保留。如何保留修改后的文件?

【问题讨论】:

    标签: wix upgrade preserve


    【解决方案1】:

    这为我解决了...配置文件通过次要/主要升级保留,并在卸载时完全删除。

    参考:http://blogs.msdn.com/b/astebner/archive/2008/10/19/9006538.aspx

    编辑:来自链接页面的汇总信息...

    1. 每个配置文件都应有自己的组件,其中配置文件被标记为组件的关键路径。 Windows Installer 将使用未版本化的文件替换逻辑。
    2. 在“InstallFiles”操作之后添加“RemoveExistingProducts”操作。在删除旧 MSI 之前安装所有组件的新版本。当它按此顺序完成时,组件的引用计数将增加到 2,但配置文件不会被替换,除非它们未修改(因为未版本化的文件替换逻辑)。当旧的 MSI 被移除时,引用计数会减回到 1,但文件不会被移除,因为引用计数不为 0。

    【讨论】:

    • 是的,这很有效,是最好的选择。您需要遵守所有组件规则,并且在重大升级后可能会丢失引用或文件。您还可以通过将其组件设置为永久来保留配置文件,然后使用条件为仅在不属于主要升级的卸载时运行的自定义操作,以便在执行真正的卸载时进行清理。 UPGRADINGPRODUCTCODE 条件可用于检测是否正在执行主要升级。仅将其留作参考,以供可能遇到相同问题的人参考。
    • 该链接非常有价值,但我认为最好总结一下帖子以防万一它消失。
    • 第 2 部分似乎是错误的。在“InstallFiles”之后添加“RemoveExistingProducts”操作时收到错误消息:某些操作介于 InstallInitialize 和 RemoveExistingProducts 之间。
    • 这不适用于 MajorUpgrade,因为那时您不能自己添加 RemoveExistingProducts。
    • @markmnl 您无法添加 RemoveExistingProducts,但您可以在 MajorUpgrade 元素上设置 Schedule 属性,该属性将执行相同的工作。
    【解决方案2】:

    升级时有 3 个选项:

    1. 使配置文件组件永久化。这不会卸载它,您可以升级它,但删除它会非常困难。
    2. 使用Remember property pattern 将IP 和PORT 的配置设置存储在注册表中。
    3. 作为安装的一部分,将配置文件写入临时文件名,然后使用 CopyFile 命令创建目标文件。升级时使用 FileSearch 检查文件,如果存在则不要复制。这里唯一的问题是,如果配置文件已更改,您将无法获得更新的部分。

    最好的选择是记住我的属性,因为它的问题最少。

    【讨论】:

    • 您可以通过设置空白 GUID 将组件与引用计数分离。然后文件只是转储到磁盘上,卸载时将保留在那里。然后,您可以在卸载时使用自定义操作来清理它,或者在卸载时保留安装的文件。在默认情况下,重新安装将不会重新安装文件 - 各种陷阱。你需要知道你想要什么行为。
    【解决方案3】:

    我花了一段时间,但这是我自己解决的方法。这可能是caveman_dick 的第三个选项的变体。

    1) 将新操作添加到 UISequence 以备份您当前的配置文件。您可以使用自定义操作和 ComponentSearch 的魔力来实际定位文件。

    2) 稍后在 ExecuteSequence 中恢复文件。

    <Binary Id="CustomActions.CA.dll" SourceFile="..\CustomActions\bin\$(var.Configuration)\CustomActions.CA.dll" />
    <CustomAction Id="BackupConfigFile"
             Return="check"
             BinaryKey="CustomActions.CA.dll"
             DllEntry="BackupFile" />
    
    <CustomAction Id="RestoreConfigFile"
         Return="check"
         Execute="deferred"
         Impersonate="no"
         BinaryKey="CustomActions.CA.dll"
         DllEntry="RestoreFile" />
    
    <CustomAction Id="PropertyDelegator" 
                  Property="RestoreConfigFile" 
                  Value="MYTARGET=[MYTARGET];FILENAME_TO_BACKUP=[FILENAME_TO_BACKUP]" />
    
    <Property Id="FILENAME_TO_BACKUP" Value="test.exe.config" />
    
    <Property Id="PREVIOUS_PATH">
      <ComponentSearch Id="evSearch" Guid="{010447A6-3330-41BB-8A7A-70D08ADB35E4}" />
    </Property>
    

    这是我写的快速 CustomAction.cs:

    [CustomAction]
    public static ActionResult BackupFile(Session session)
    {
        try
        {
            // check out if the previous installation has our file included
            // and if it does,
            // then make copy of it.
            var previousInstallationPath = session["PREVIOUS_PATH"];
            var fileToBackup = session["FILENAME_TO_BACKUP"];
    
            if (!string.IsNullOrEmpty(previousInstallationPath) && !string.IsNullOrEmpty(fileToBackup))
            {
                var absolutePath = Path.Combine(previousInstallationPath, fileToBackup);
                if (File.Exists(absolutePath))
                {
                    var destinationPath = Path.Combine(Path.GetTempPath(),
                        string.Concat(fileToBackup, _MODIFIER));
    
                    File.Copy(absolutePath, destinationPath);
                }
            }
        }
        catch (Exception e)
        {
            session.Log("Couldn't backup previous file: {0}", e);
        }
        return ActionResult.Success;
    }
    
    [CustomAction]
    public static ActionResult RestoreFile(Session session)
    {
        try
        {
            // check if our CustomAction made backup of file,
            // and if it indeed exists in temp path, then
            // we basically copy it back.
            var currentInstallationPath = session.CustomActionData["MYTARGET"];
            var fileToRestore = session.CustomActionData["FILENAME_TO_BACKUP"];
            var fileOriginalContentPath = Path.Combine(Path.GetTempPath(),
                string.Concat(fileToRestore, _MODIFIER));
    
            if (File.Exists(fileOriginalContentPath))
            {
                var destinationFile = Path.Combine(currentInstallationPath, fileToRestore);
                if (File.Exists(destinationFile))
                    File.Delete(destinationFile);
    
                File.Move(fileOriginalContentPath, destinationFile);
            }
        }
        catch (Exception e)
        {
            session.Log("Couldn't restore previous file: {0}", e);
        }
        return ActionResult.Success;
    }
    

    实际定义序列:

    <InstallUISequence>
      <Custom Action="BackupConfigFile" After="AppSearch"></Custom>
    </InstallUISequence>
    
    <InstallExecuteSequence>
      <Custom Action="PropertyDelegator" Before="RestoreConfigFile" />
      <Custom Action="RestoreConfigFile" After="InstallFiles"></Custom>
    </InstallExecuteSequence>
    

    尚未对其进行彻底测试,但目前似乎可以完成这项工作。警告:临时文件夹可能会改变?!

    另外有一个是我从网上找的,但没有测试过。

                <!-- Support Upgrading the Product -->
    
                <Upgrade Id="{B0FB80ED-249E-4946-87A2-08A5BCA36E7E}">
    
                      <UpgradeVersion Minimum="$(var.Version)"
    OnlyDetect="yes" Property="NEWERVERSIONDETECTED" />
    
                      <UpgradeVersion Minimum="0.0.0"
    Maximum="$(var.Version)" IncludeMinimum="yes" 
    
                                              IncludeMaximum="no"
    Property="OLDERVERSIONBEINGUPGRADED" />
    
                </Upgrade>
    
                <Property Id="OLDERVERSIONBEINGUPGRADED" Secure="yes" />
    
    
    
                <!-- Action to save and Restore the Config-File on reinstall
    -->
    
                <!-- We're using CAQuietExec to prevent DOS-Boxes from
    popping up -->
    
                <CustomAction Id="SetQtCmdLineCopy" Property="QtExecCmdLine"
    Value="&quot;[SystemFolder]cmd.exe&quot; /c copy
    &quot;[INSTALLDIR]MyApp.exe.config&quot;
    &quot;[INSTALLDIR]config.bak&quot;" />
    
                <CustomAction Id="QtCmdCopy" BinaryKey="WixCA"
    DllEntry="CAQuietExec" Execute="immediate" />
    
                <CustomAction Id="SetQtCmdLineRestore"
    Property="QtCmdRestore" Value="&quot;[SystemFolder]cmd.exe&quot; /c move
    /Y &quot;[INSTALLDIR]config.bak&quot;
    &quot;[INSTALLDIR]MyApp.exe.config&quot;" />
    
                <CustomAction Id="QtCmdRestore" Execute="commit"
    BinaryKey="WixCA" DllEntry="CAQuietExec" />
    
    
    
                <!-- These actions will run only for a major upgrade -->
    
                <InstallExecuteSequence>
    
                      <Custom Action="SetQtCmdLineCopy"
    After="InstallInitialize"> NOT (OLDERVERSIONBEINGUPGRADED = "")</Custom>
    
                      <Custom Action="QtCmdCopy"
    After="SetQtCmdLineCopy">NOT (OLDERVERSIONBEINGUPGRADED = "")</Custom>
    
                      <Custom Action="SetQtCmdLineRestore"
    Before="InstallFinalize">NOT (OLDERVERSIONBEINGUPGRADED = "")</Custom>
    
                      <Custom Action="QtCmdRestore"
    After="SetQtCmdLineRestore">NOT (OLDERVERSIONBEINGUPGRADED =
    "")</Custom>
    
                </InstallExecuteSequence>
    

    【讨论】:

      【解决方案4】:

      还有另一个选项,但它可能不适用于您的场景 - 这完全取决于谁是最初运行您的安装程序...

      例如,如果您的应用是通过网络下载的,那么我们通常会使用 Caveman_dick 的记住属性模式。

      但是,我们有几套产品始终由我们自己的安装人员安装,他们访问客户站点。在这种情况下,根本不要在安装程序中包含配置文件!

      简单地说 - 如果安装程序不知道某个文件,那么它就不会卸载它!

      在这种情况下,您可以选择让您的安装团队创建和配置配置文件,或者您的应用在配置文件不存在时创建它,然后向用户询问值。

      如前所述,在某些情况下这不是一个选项,但它对我们来说很好。

      【讨论】:

        【解决方案5】:

        在 MajorUpgrade 中添加 Schedule="afterInstallExecuteAgain"

        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallExecuteAgain" />
        

        对我有用

        【讨论】:

          猜你喜欢
          • 2011-02-15
          • 2014-05-10
          • 1970-01-01
          • 2010-10-04
          • 2018-12-27
          • 2015-08-09
          • 2018-03-26
          • 2014-05-16
          • 1970-01-01
          相关资源
          最近更新 更多