Как сохранить файл конфигурации при основном обновлении в версии 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>

Но когда происходит серьезное обновление, файл не сохраняется. Как сохранить измененный файл?

Ответ 1

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

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

EDIT: суммированная информация со связанной страницы...

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

Ответ 2

У вас есть 3 варианта:

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

Лучшим вариантом является свойство помнить меня, так как это имеет наименьшие проблемы.

Ответ 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>

не тестировали его полностью, но, похоже, сейчас делают эту работу. Caveat: папка Temp может измениться?!

В качестве альтернативы есть тот, который я нашел из Интернета, но не тестировал его.

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

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

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

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