15

当 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>

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

4

5 回答 5

13

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

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

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

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

升级时有 3 个选项:

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

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

于 2013-04-24T12:07:59.637 回答
2

我花了一段时间,但这是我自己解决的方法。这可能是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>
于 2013-10-22T16:13:15.377 回答
1

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

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

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

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

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

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

于 2014-08-20T09:40:09.193 回答
1

添加Schedule="afterInstallExecuteAgain"MajorUpgrade

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

它对我有用

于 2019-04-10T12:08:14.993 回答