Могу ли я заставить проект установщика использовать файл .config из встроенного решения вместо исходного? - PullRequest
9 голосов
/ 26 июля 2011

Я использую решение для этого вопроса , чтобы применить изменения конфигурации к App.config в проекте Winforms.У меня также есть проект установщика для проекта, который создает устанавливаемый файл * .msi.Проблема в том, что файл конфигурации, включенный в установщики, является исходным, не преобразованным файлом конфигурации.Таким образом, мы не получим строки производственного соединения в производственном установщике, даже если в файле конфигурации для собранного проекта winforms применены все правильные преобразования.

Есть ли способ заставить проект установщика использовать выводпроекта сборки?

Ответы [ 7 ]

8 голосов
/ 02 августа 2011

Прежде всего: невозможно заставить проект установки указывать на другой файл app.config с помощью опции Primary output.Так что мое решение будет работать вокруг .Я надеюсь, что вы найдете это полезным в вашей ситуации.

Обзор:

Основная идея:

  • Удалить принудительное app.configиз проекта установки;
  • Добавьте файл, указывающий на app.config, вручную;
  • Используйте MSBuild, чтобы попасть в файл vdproj, и измените его в соответствии с реальным выводомпреобразованный app.config.

Некоторые недостатки:

  • Проект установки обновляется только в том случае, если проект развертывает сборку. аааа ... не реальный недостаток!
  • Вам нужен MSBuild 4.0 ... это тоже можно обойти!
  • Нужна специальная задача под названием FileUpdate ... он с открытым исходным кодом и имеет установщик.

Давайте работать:

1) Перейдите в проект установки и выберитеОсновной объект вывода, щелкните правой кнопкой мыши и перейдите в Свойства.Там вы найдете Exclude Filter ... добавить фильтр для *.config, поэтому он удалит жестко запрограммированный файл app.config.

2) Щелкните правой кнопкой мыши ваш проект установки в обозревателе решений ->Добавить -> Файл ... выбрать любой файл, заканчивающийся .config.

3) Загрузить Проект MSBuild Community Tasks , я рекомендую установщик msi.

4) Выгрузите ваш проект (csproj) и замените код из другого вопроса следующим:

Код:

  <UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
  <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
  <Target Name="AfterCompile" Condition="exists('app.$(Configuration).config')">
    <!-- Generate transformed app config in the intermediate directory -->
    <TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
    <!-- Force build process to use the transformed configuration file from now on. -->
    <ItemGroup>
      <AppConfigWithTargetPath Remove="app.config" />
      <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
        <TargetPath>$(TargetFileName).config</TargetPath>
      </AppConfigWithTargetPath>
    </ItemGroup>
    <PropertyGroup>
      <SetupProjectPath>$(MSBuildProjectDirectory)\$(IntermediateOutputPath)$(TargetFileName).config</SetupProjectPath>
    </PropertyGroup>
    <!-- Change the following so that this Task can find your vdproj file -->
    <FileUpdate Files="$(MSBuildProjectDirectory)\..\Setup1\Setup1.vdproj"
                    Regex="(.SourcePath. = .8:).*\.config(.)"
                    ReplacementText="$1$(SetupProjectPath.Replace(`\`,`\\`))$2" />
    <FileUpdate Files="$(MSBuildProjectDirectory)\..\Setup1\Setup1.vdproj"
                    Regex="(.TargetName. = .8:).*\.config(.)"
                    ReplacementText="$1$(TargetFileName).config$2" />
  </Target>

5) Предыдущий код должен бытьизменилось, чтобы он мог найти ваш файл vdproj.Я поместил комментарий в коде, указав, где вам нужно внести изменения.

Теперь, каждый раз, когда вы строите свой основной проект, MSBuild будет изменять проект установки, так что он использует правильный app.configфайл.У него могут быть недостатки, но это решение можно отполировать и стать лучше.Если вам нужно оставить комментарий, и я постараюсь ответить как можно скорее.

Используемые ресурсы

Требуется MSBuild 4.0, потому что мне нужно использовать функцию замены String,заменить один "\" на двойной "\" в пути.См. Функции свойств MSBuild для получения подробной информации об использовании функции в MSBuild.

Я узнал о FileUpdate Task в этом другом вопросе.Официальный проект MSBuild Project Tasks Project .

Эти две темы были важны для моих выводов:

Попытка включить файлы конфигурации, специфичные для app.config, вПроект установки

Проблемы с проектом настройки - я толстый?

6 голосов
/ 18 октября 2011

Другое решение, которое я нашел, это не использовать преобразования, а просто иметь отдельный файл конфигурации, например, app.Release.config. Затем добавьте эту строку в ваш файл csproj.

  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <AppConfig>App.Release.config</AppConfig>
  </PropertyGroup>

Это заставит проект развертывания использовать правильный файл конфигурации при упаковке.

4 голосов
/ 20 июня 2017

Я соединил лучшие из следующих ответов, чтобы получить полностью рабочее решение без использования любых внешних инструментов вообще:

1.Настройка преобразования App.Config

Источник: https://stackoverflow.com/a/5109530

Вкратце:

Вручную добавьте дополнительные файлы .config для каждой конфигурации сборки и отредактируйтеНеобработанный файл проекта для включения их аналогичен следующему:

<Content Include="App.config" />
<Content Include="App.Debug.config" >
  <DependentUpon>App.config</DependentUpon>
</Content>
<Content Include="App.Release.config" >
  <DependentUpon>App.config</DependentUpon>
</Content>

Затем включите следующий XML в конец файла проекта, непосредственно перед закрывающим тегом </project>:

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="AfterCompile" Condition="exists('app.$(Configuration).config')">
  <TransformXml Source="app.config" Destination="$(IntermediateOutputPath)$(TargetFileName).config" Transform="app.$(Configuration).config" />
  <ItemGroup>
    <AppConfigWithTargetPath Remove="app.config" />
    <AppConfigWithTargetPath Include="$(IntermediateOutputPath)$(TargetFileName).config">
      <TargetPath>$(TargetFileName).config</TargetPath>
    </AppConfigWithTargetPath>
  </ItemGroup>
</Target>

Наконец, отредактируйте дополнительные файлы .config, включив в них соответствующие преобразования для каждой конфигурации сборки:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <!-- transformations here-->
</configuration>

2.Включите соответствующий .config в проект установки

Сначала добавьте команду в событие postbuild вашего основного проекта, чтобы переместить соответствующий преобразованный файл .config в нейтральное место (например, основной bin\).каталог):

copy /y "$(TargetDir)$(TargetFileName).config" "$(ProjectDir)bin\$(TargetFileName).config"

(Источник: https://stackoverflow.com/a/26521986)

Откройте проект установки и щелкните узел «Первичный вывод ...», чтобы открыть окно свойств.добавьте ExludeFilter "*.config", чтобы исключить файл по умолчанию (без преобразования) .config.

(Источник: https://stackoverflow.com/a/6908477)

Наконец добавьте преобразованный файл .config (из события postbuild)) в проект установки (Добавить> Файл).

Готово.

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

4 голосов
/ 23 октября 2014

Я сделал это по-другому, без каких-либо внешних инструментов:

Я добавил событие после сборки, которое скопировало целевые файлы в «нейтральный» каталог (корень папки / bin в проекте), а затем добавил этот файл в .vdproj. Проект развертывания теперь выбирает, какой бы ни была последняя сборочная версия:

Команда пост-сборки:

copy /y "$(TargetDir)$(TargetFileName).config" "$(ProjectDir)bin\$(TargetFileName).config"

Это сработало для того, что мне было нужно без каких-либо внешних инструментов, и прекрасно работает с преобразованиями SlowCheetah.

1 голос
/ 22 апреля 2014

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

Предварительные требования

Я использовал утилиту cct.exe для явного преобразования файла.Вы можете скачать здесь http://ctt.codeplex.com/

Я использовал пользовательский установщик в проекте установки для захвата событий установки.

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

1)Добавьте нужные файлы конфигурации в свой проект и измените свой файл .csproj, например,

<Content Include="app.uat.config">
  <DependentUpon>app.config</DependentUpon>
</Content>
<Content Include="app.training.config">
  <DependentUpon>app.config</DependentUpon>
</Content>
<Content Include="app.live.config">
  <DependentUpon>app.config</DependentUpon>
</Content> 

Я добавил их в качестве содержимого, чтобы их можно было скопировать в выходной каталог.

2)Добавьте cct.exe к загруженному проекту.

3) Добавьте пользовательский установщик к своему проекту, который должен выглядеть следующим образом

  [RunInstaller(true)]
  public partial class CustomInstaller : System.Configuration.Install.Installer
  {
    string currentLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    string[] transformationfiles = Directory.GetFiles(Path.GetDirectoryNam(Assembly.GetExecutingAssembly().Location), "app.*.config");

    public CustomInstaller()
    {
        InitializeComponent();
        // Attach the 'Committed' event.
        this.Committed += new InstallEventHandler(MyInstaller_Committed);
        this.AfterInstall += new InstallEventHandler(CustomInstaller_AfterInstall);

    }


    void CustomInstaller_AfterInstall(object sender, InstallEventArgs e)
    {
        try
        {

            Directory.SetCurrentDirectory(currentLocation);
            var environment = Context.Parameters["Environment"];
            var currentconfig = transformationfiles.Where(x => x.Contains(environment)).First();

            if (currentconfig != null)
            {
                FileInfo finfo = new FileInfo(currentconfig);
                if (finfo != null)
                {
                    var commands = string.Format(@"/C  ctt.exe s:yourexename.exe.config t:{0} d:yourexename.exe.config ", finfo.Name);

                    using (System.Diagnostics.Process execute = new System.Diagnostics.Process())
                    {
                        execute.StartInfo.FileName = "cmd.exe";
                        execute.StartInfo.RedirectStandardError = true;
                        execute.StartInfo.RedirectStandardInput = true;
                        execute.StartInfo.RedirectStandardOutput = true;
                        execute.StartInfo.UseShellExecute = false;
                        execute.StartInfo.CreateNoWindow = true;
                        execute.StartInfo.Arguments = commands;
                        execute.Start();
                    }

                }
            }
        }
        catch
        {
            // Do nothing... 
        }


    }

    // Event handler for 'Committed' event.
    private void MyInstaller_Committed(object sender, InstallEventArgs e)
    {
        XmlDocument doc = new XmlDocument();
        var execonfigPath = currentLocation + @"\yourexe.exe.config";
        var file = File.OpenText(execonfigPath);
        var xml = file.ReadToEnd();
        file.Close();
        doc.LoadXml(FormatXmlString(xml));
        doc.Save(execonfigPath);

        foreach (var filename in transformationfiles)
            File.Delete(filename);

    }



    private static string FormatXmlString(string xmlString)
    {
        System.Xml.Linq.XElement element = System.Xml.Linq.XElement.Parse(xmlString);
        return element.ToString();
    }

}

Здесь я использую два обработчика событий CustomInstaller_AfterInstall , в которые загружаю правильный файл конфигурации и преобразую.В MyInstaller_Committed Я удаляю файлы преобразования, которые нам не нужны, на клиентской машине после применения.Я также делаю отступ для преобразованного файла, потому что cct просто преобразовывает элементы, выровненные безобразно.

4) Откройте ваш проект установки и добавьте выходной файл проекта, чтобы программа установки могла копировать файлы конфигурации, такие как app.uat.config, app.live.config и т. д. на клиентский компьютер.

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

 string[] transformationfiles = Directory.GetFiles(Path.GetDirectoryNam

 (Assembly.GetExecutingAssembly().Location), "app.*.config");

. Для этого я добавил диалоговое окно пользовательского интерфейса при настройке.проект, чтобы получить текущий конфиг.Диалог дает пользователю возможность выбрать среду, такую ​​как «Live», «UAT», «Test» и т. Д.Теперь передайте выбранную среду своему пользовательскому установщику и отфильтруйте их.

Configure UI to allow user to select option

enter image description here

enter image description here

Будет длинная статья, если я объясню, как добавить диалог, как установитьи т.д., пожалуйста, гуглите их.Но идея состоит в том, чтобы преобразовать выбранную пользователем среду.Преимущество этого подхода в том, что вы можете использовать один и тот же установочный файл для любой среды.

Вот сводка:

Добавление файлов конфигурации

Добавление файла cct exe

Добавить пользовательский установщик

Применить преобразование к exe.config в после события установки

Удалить файлы преобразования с компьютера клиента

Изменить проект установки таким образом, чтобы

set up should copy all config files(project output content) and cct.exe into output directory

configure UI dialog with radio buttons (Test,Live,UAT..)

pass the selected value to custom installer

Решение может выглядеть длинным, но у него нет выбора, потому что MSI всегда копирует app.config и не заботится о событиях сборки проекта и преобразованиях.slowcheetah работает только с clickonce not setup project

1 голос
/ 01 мая 2013

Исходя из ответа Алека, вот аналогичный элемент, который вы можете использовать вместе с преобразованиями и при этом получить их полное преимущество:

  <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <Content Include="$(OutputPath)$(AssemblyName).dll.config">
      <InProject>false</InProject>
      <Link>$(AssemblyName).dll.config</Link>
    </Content>
  </ItemGroup>

Таким образом, вы можете использовать преобразования SlowCheetah или встроенные для преобразования файла .config, а затем перейти в проект развертывания Visual Studio (или другой) и включить Содержимое из затронутого проекта в свой Add- > Выход проекта ... страница легко, с минимальными изменениями.

0 голосов
/ 14 августа 2017

Вопрос старый, но следующее может все же помочь многим людям.

Я бы просто использовал Wix WiFile.exe для замены соответствующего файла в msi таким образом (для примера мы называем ваш msi yourPackage.msi ):

Шаг 1. Из командной строки запустите: WiFile.exe "yourPackage.msi" / x "app.exe.config." Вышеприведенное извлечет «неправильный» файл app.exe.config из msi и поместит его в тот же каталог, что и ваш msi ;

Шаг 2. Поместите новый (prod) файл конфигурации (должен иметь то же имя, что и извлеченный файл: app.exe.config) в то же место, что и ваш msi; Это означает, что вы перезаписываете файл app.exe.config, который был только что извлечен на шаге 1 выше, новым (рабочий файл конфигурации) ;

Шаг 3. Из командной строки запустите: WiFile.exe "yourPackage.msi" / u "app.exe.config."

ЭТО ВСЕ!

Вышеуказанное можно сделать за несколько секунд. Вы можете автоматизировать задание, если хотите, например, запустив его как пакетный или другой.

После выполнения шага 3, описанного выше, ваш msi будет содержать новый файл конфигурации, который теперь будет установлен на ваших клиентах при запуске установки.

...