如何在wix v3.8中进行主要升级时保留配置文件?

问题描述:

我想在msi安装程序进行重大升级时保留配置文件。对于配置文件,我在安装时进行了修改。代码如下:如何在wix v3.8中进行主要升级时保留配置文件?

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

但是,当发生重大升级时,文件不会保留。我怎样才能保留修改后的文件?

升级时有3种选择:

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

最好的选择是记住我的财产,因为这有最少的问题。

+1

通过设置空白GUID,可以将引导计数与组件分离。然后该文件被转储到磁盘上并在卸载时保留。然后,您可以使用卸载时的自定义操作进行清理,或者在卸载时保留安装的文件。重新安装将不会在默认情况下重新安装文件 - 各种陷阱。你需要知道你想要什么样的行为。 – 2014-03-04 08:49:51

我花了一段时间,但这是我自己解决它的方式。这可能是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> 

这解决了它对我来说...配置文件保留与轻微/主要升级,并在卸载时完全删除。

编号:http://blogs.msdn.com/b/astebner/archive/2008/10/19/9006538.aspx

编辑:从链接的页面总结信息...

  1. 每个配置文件必须有它自己的组件,其配置文件被标记为组件的的keyPath 。 Windows安装程序将使用Unversioned文件替换逻辑。
  2. 在“InstallFiles”操作之后添加“RemoveExistingProducts”操作。删除旧的MSI之前安装了所有组件的新版本。当按照这个顺序完成时,组件的引用计数会递增到2,但是配置文件不会被替换,除非它们未被修改(由于未受版本控制的文件替换逻辑)。当删除旧的MSI时,引用计数将递减到1,但由于引用计数不为0,因此文件不会被删除。
+0

是的,这是有效的,是最好的选择。您需要遵守所有组件规则,并在主要升级后引用或文件可能会丢失。您还可以通过将配置文件设置为永久性来保留配置文件,然后使用自定义操作进行调整以仅在卸载时运行,而不是在执行真正的卸载时进行主要升级以清除的一部分。条件UPGRADINGPRODUCTCODE可用于检测是否正在执行主要升级。只要将其留给任何可能面临同样问题的人参考。 – 2014-03-04 08:25:23

+0

这个链接非常有价值,但我认为总结帖子以防万一它消失是件好事。 – knightpfhor 2014-05-06 04:38:00

+0

第2部分似乎是错误的。在“InstallFiles”之后添加“RemoveExistingProducts”操作时收到错误消息:某些操作位于InstallInitialize和RemoveExistingProducts之间。 – Simon 2016-03-24 13:58:23

还有另一个选项,但它可能不适用于您的情况 - 这一切都取决于谁是最初运行您的安装...

如果你的应用程序被下载在网上例如,那么我们通常caveman_dick去的记忆特性模式。

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

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

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

如上所述,在某些情况下,这不是一个选项,但它对我们的工作正常。