Как сохранить файл конфигурации при значительном обновлении в wix v3.8?

Я хочу сохранить файл конфигурации, когда установщик 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 ответа

Это решило это для меня... файл конфигурации сохранен с незначительным / главным обновлением и полностью удален при удалении.

Ссылка: http://blogs.msdn.com/b/astebner/archive/2008/10/19/9006538.aspx

РЕДАКТИРОВАТЬ: Обобщенная информация со связанной страницы...

  1. Каждый файл конфигурации должен иметь свой собственный компонент, где файл конфигурации помечается как ключевой путь компонента. Неверная логика замены файлов будет использоваться установщиком Windows.
  2. Добавьте действие "RemoveExistingProducts" после действия "InstallFiles". Новые версии всех компонентов устанавливаются перед удалением старого MSI. Когда это будет сделано в этой последовательности, счетчики ссылок будут увеличены до 2, но файлы конфигурации не будут заменены, если они не изменены (из-за неверсионной логики замены файлов). При удалении старого MSI счетчик ссылок будет уменьшен до 1, но файлы не будут удалены, поскольку счетчик ссылок не равен 0.

У вас есть 3 варианта при обновлении:

  1. Сделайте компонент файла конфигурации постоянным. Это не удалит его, и вы сможете обновить его, но удалить его будет очень сложно.
  2. Используйте шаблон свойства Remember для сохранения настроек реестра для IP и PORT в реестре.
  3. В процессе установки запишите файл конфигурации во временное имя файла, а затем используйте команду CopyFile для создания файла назначения. При обновлении проверьте файл, используя FileSearch, и, если он существует, не копируйте. Проблема здесь только в том, что если файл конфигурации изменился, вы не получите обновленные разделы.

Лучший вариант - помнить меня, так как здесь меньше всего проблем.

Это заняло у меня некоторое время, но вот как я решил это сам. Вероятно, это вариант третьего варианта 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>

Добавлять Schedule="afterInstallExecuteAgain" в MajorUpgrade

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

Это работает для меня

Есть и другой вариант, но он может не подходить к вашему сценарию - все зависит от того, кто изначально запускает ваш установщик...

Если ваше приложение загружается, например, через Интернет, то мы обычно используем шаблон свойств caveman_dick запомнить.

Однако у нас есть несколько наборов продуктов, которые всегда устанавливаются нашими собственными специалистами по установке, которые посещают сайт клиентов. В этом случае просто не включайте файл конфигурации в установщик!

Проще говоря - если установщик не знает о файле, он не будет удален!

В этом случае у вас есть возможность вашей команды установки создать и настроить файл конфигурации, или ваше приложение создает его, когда он не существует, и запрашивает у пользователя значения.

Как уже говорилось, это не будет вариантом в некоторых сценариях, но он отлично работает для наших.

Другие вопросы по тегам