Arhn - архитектура программирования

Объединение dll в один .exe с помощью wpf

В настоящее время я работаю над проектом, в котором у нас много зависимостей. Я хотел бы скомпилировать все упомянутые dll в .exe так же, как вы сделали бы со встроенными ресурсами. Я попробовал ILMerge, но он не может обрабатывать ресурсы .xaml .

Итак, мой вопрос: есть ли способ объединить проект WPF с несколькими зависимостями в один .exe?

22.06.2009

  • Вы ищете только бесплатные приложения или можете заплатить за них несколько долларов? 22.06.2009
  • Если приложение достаточно хорошее и может работать с WPF, мы, вероятно, рассмотрим и коммерческие приложения. 22.06.2009

Ответы:


1

реактор .NET имеет функцию слияния сборок, и это не очень дорого.

22.06.2009
  • Может ли он также обрабатывать проекты WPF? Выглядит многообещающе. 22.06.2009
  • Кому-нибудь удалось использовать Reactor для решения проблем слияния WPF? В отличие от SmartAssembly, за 180 долларов это действительно доступный продукт для бедного условно-бесплатного разработчика вроде меня. 31.07.2010
  • Ну, я попробовал, и это работает! Однако я все еще хочу посмотреть, смогу ли я заставить все работать, не выкладывая почти 200 долларов. 31.07.2010
  • Я попробовал это на сборках Net4.0 WPF, и это не работает для меня - все мое объединенное приложение отображает информацию о том, что слияние было выполнено незарегистрированной версией .Net Reactor. 17.08.2010
  • Спасибо за эту ссылку, очень хорошая программа! 16.08.2013

  • 2

    http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

    Это сработало для меня как шарм :) и совершенно бесплатно.

    Добавление кода на случай, если блог когда-нибудь исчезнет.

    1) Добавьте это в свой файл .csproj:

    <Target Name="AfterResolveReferences">
      <ItemGroup>
        <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
          <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
        </EmbeddedResource>
      </ItemGroup>
    </Target>
    

    2) Сделайте так, чтобы ваш Main Program.cs выглядел так:

    [STAThreadAttribute]
    public static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
        App.Main();
    }
    

    3) Добавьте метод OnResolveAssembly:

    private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
    {
        Assembly executingAssembly = Assembly.GetExecutingAssembly();
        AssemblyName assemblyName = new AssemblyName(args.Name);
    
        var path = assemblyName.Name + ".dll";
        if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
    
        using (Stream stream = executingAssembly.GetManifestResourceStream(path))
        {
            if (stream == null) return null;
    
            var assemblyRawBytes = new byte[stream.Length];
            stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
            return Assembly.Load(assemblyRawBytes);
        }
    }
    
    14.02.2011
  • Хорошее решение. Были некоторые проблемы при загрузке dll с использованием .LoadFrom(), потому что, если другой контекст, но в остальном хороший и чистый способ автоматизации слияния непосредственно в VS. 10.05.2011
  • Является ли это решение специфичным для MSBuild? 06.07.2011
  • Это определенно лучший ответ 03.11.2014
  • Удивительно легко! Короче говоря, всего три шага: 1. отредактируйте свой .csproject и вставьте предоставленные строки 2. создайте файл .cs, вставьте предоставленные строки и автоматически разрешите отсутствующие сборки 3. измените объект запуска на только что созданный файл. 07.09.2016
  • Такое элегантное решение без внешних зависимостей. Любить это! Единственное, что я сделал по-другому, это переопределил App.OnStartup, чтобы перехватить AssemblyResolve, поэтому мне не нужен новый объект запуска. 11.08.2017
  • В моем случае App.OnStartup() было слишком поздно для регистрации OnAssemblyResolve, потому что к моменту вызова OnStartup мое приложение ссылалось на какие-то дополнительные сборки. Вместо этого я поместил регистрацию OnAssemblyResolve в конструктор App(), и это сработало как волшебство. 12.03.2018
  • Чего мне здесь не хватает, так это того, как преобразовать зависимости в EmbeddedResource. После добавления зависимостей с помощью NuGet я добавил их одну за другой в качестве ссылок на новую папку в своем решении, и таким образом они были встроены в выходной исполняемый файл. Тем не менее, они копируются в выходную папку. Я просто игнорирую их во время развертывания. Есть ли (лучшее) решение для этого? 01.08.2018
  • Большое спасибо! Пришлось немного адаптировать его, но это намного лучше, чем покупать или платить за какое-то другое решение в моем случае. 26.06.2019
  • Я использовал это в течение нескольких лет. Только сегодня было четыре компьютера, на которых это не удалось. Есть идеи, почему? 02.08.2019
  • Я не могу заставить это работать. Я пытаюсь включить свой файл Resources.fr в .exe. 19.09.2020

  • 3

    Используйте Costura.Fody — он доступен как Nuget Pkg для лучшего и простого способа встраивания ресурсов в вашу сборку.

    Install-Package Costura.Fody
    

    После добавления его в проект он автоматически встроит все добавленные ссылки в вашу основную сборку.

    09.10.2015
  • Обратите внимание, что этот пакет сейчас находится в режиме обслуживания, и разработчики рекомендуют использовать альтернативы. Однако, по моему опыту, этот пакет все еще отлично работает. 23.02.2021

  • 4

    {smartassembly} — один из таких продуктов. Он может запутать или внедрить ваши DLL.

    Попробуйте это: http://www.smartassembly.com/

    Вы также можете внести множество улучшений в свое приложение, чтобы оно работало быстрее.

    И да. Вы можете использовать его для WPF.

    Обновление от 06.08.2015: ILRepack 2.0.0 (альтернатива ILMerge с открытым исходным кодом) теперь поддерживает слияние большинства случаев WPF: https://twitter.com/Gluckies/status/607680149157462016

    24.06.2009
  • Я попробовал этот продукт, и он работал правильно там, где ILMerge не работал. Это было очень легко использовать. 29.01.2010
  • Выглядит впечатляюще, но не позволяет указать, какие файлы вы хотите собрать (ограничивает вас обнаруженными файлами). В результате все библиотеки DLL ресурсов (с переведенными строками) остаются в покое, и у вас остается один исполняемый файл с дополнительными библиотеками DLL. Я думал, что точкой слияния будет один экзешник и 0 dll. 17.08.2010

  • 5

    Как указано на веб-сайте ILMerge, рассматривайте эти DLL как ресурсы из Джеффри Рихтер здесь :

    Многие приложения состоят из файла EXE, который зависит от множества файлов DLL. При развертывании этого приложения должны быть развернуты все файлы. Однако есть метод, который можно использовать для развертывания только одного EXE-файла. Во-первых, определите все DLL-файлы, от которых зависит ваш EXE-файл и которые не входят в состав самой Microsoft .NET Framework. Затем добавьте эти библиотеки DLL в свой проект Visual Studio. Для каждого добавляемого DLL-файла отобразите его свойства и измените «Действие сборки» на «Встроенный ресурс». Это заставляет компилятор C# встраивать файлы DLL в ваш EXE-файл, и вы можете развернуть этот один EXE-файл. Во время выполнения CLR не сможет найти зависимые сборки DLL, что является проблемой. Чтобы исправить это, при инициализации приложения зарегистрируйте метод обратного вызова с событием ResolveAssembly AppDomain. Код должен выглядеть примерно так:

    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
    
       String resourceName = "AssemblyLoadingAndReflection." +
    
          new AssemblyName(args.Name).Name + ".dll";
    
       using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {
    
          Byte[] assemblyData = new Byte[stream.Length];
    
          stream.Read(assemblyData, 0, assemblyData.Length);
    
          return Assembly.Load(assemblyData);
    
       }
    
    }; 
    

    Теперь, когда поток в первый раз вызывает метод, который ссылается на тип в зависимом файле DLL, будет вызвано событие AssemblyResolve, а код обратного вызова, показанный выше, найдет нужный встроенный ресурс DLL и загрузит его, вызвав перегрузку метода Load сборки. который принимает Byte[] в качестве аргумента.

    11.08.2011

    6

    Вот измененная версия процитированного кода от Matthieu, которая не требует знания пространства имен для извлечения кода. Для WPF поместите это в код события запуска приложения.

    AppDomain.CurrentDomain.AssemblyResolve += (s, args) =>
    {
        // Note: Requires a using statement for System.Reflection and System.Diagnostics.
        Assembly assembly = Assembly.GetExecutingAssembly();
        List<string> embeddedResources = new List<string>(assembly.GetManifestResourceNames());
        string assemblyName = new AssemblyName(args.Name).Name;
        string fileName = string.Format("{0}.dll", assemblyName);
        string resourceName = embeddedResources.Where(ern => ern.EndsWith(fileName)).FirstOrDefault();
        if (!string.IsNullOrWhiteSpace(resourceName))
        {
            using (var stream = assembly.GetManifestResourceStream(resourceName))
            {
                Byte[] assemblyData = new Byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                var test = Assembly.Load(assemblyData);
                string namespace_ = test.GetTypes().Where(t => t.Name == assemblyName).Select(t => t.Namespace).FirstOrDefault();
    #if DEBUG
                Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", fileName, namespace_));
    #endif
                return Assembly.Load(assemblyData);
            }
        }
    
        return null;
    }; 
    

    Чтобы сделать их доступными во время компиляции, я создаю папку с именем ExternalDLLs, копирую туда библиотеки DLL и устанавливаю для них значение EmbeddedResource, как указано выше. Чтобы использовать их в своем коде, вам все равно нужно установить ссылку на них, но установите для параметра Copy local значение False. Чтобы код компилировался чисто и без ошибок, вам также необходимо установить с помощью statments в вашем коде пространства имен dll.

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

    private void getEmbeddedResourceNamespaces()
    {
        // Note: Requires a using statement for System.Reflection and System.Diagnostics.
        Assembly assembly = Assembly.GetExecutingAssembly();
        List<string> embeddedResourceNames = new List<string>(assembly.GetManifestResourceNames());
        foreach (string resourceName in embeddedResourceNames)
        {
            using (var stream = assembly.GetManifestResourceStream(resourceName))
            {
                Byte[] assemblyData = new Byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                try
                {
                    var test = Assembly.Load(assemblyData);
                    foreach (Type type in test.GetTypes())
                    {
                        Debug.WriteLine(string.Format("\tNamespace for '{0}' is '{1}'", type.Name, type.Namespace));
                    }
                }
                catch 
                {
                }
            }
        }
    }
    
    19.04.2013

    7

    Попробуйте .Netz ( http://madebits.com/netz/ ) - это бесплатно (как в пиве) и делает некоторые приятные вещи, если ваша цель - exe.

    12.07.2011
  • NetZ — отличный вариант для управления пакетными командами (так что вы можете легко встроить его в свою визуальную студию на событии после сборки). Однако в WPF у меня это не работает: Мой проблемный случай 26.09.2013

  • 8
    1. добавьте это в файл .csprofj:

    >

    <Target Name="AfterResolveReferences">
      <ItemGroup>
        <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
          <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
        </EmbeddedResource>
      </ItemGroup>
    </Target>
    
    1. щелкните правой кнопкой мыши проект/свойства/приложение/объект запуска/выберите Sinhro.Program

    2. добавьте это в свой файл program.cs:

      используя System.Reflection; с помощью System.IO; с помощью System.Globalization;

      [STAThreadAttribute]
      static void Main()
      {
          AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
          ...
      
      
      private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
      {
          Assembly executingAssembly = Assembly.GetExecutingAssembly();
          AssemblyName assemblyName = new AssemblyName(args.Name);
          string path = assemblyName.Name + ".dll";
          if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
          {
              path = String.Format(@"{0}\{1}", assemblyName.CultureInfo, path);
          }
          using (Stream stream = executingAssembly.GetManifestResourceStream(path))
          {
              if (stream == null)
                  return null;
              byte[] assemblyRawBytes = new byte[stream.Length];
              stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
              return Assembly.Load(assemblyRawBytes);
          }
      }   
      

    источник: http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application

    22.07.2015
  • Можно ли это даже применить к приложению WPF. Я попытался выгрузить, но ни один из файлов .cs не открывается. Должен ли я поместить код С# в файл statupUri cs и часть XAML в App.config? 04.03.2017

  • 9

    Поскольку все остальные решения написаны на C#, а это мне понадобилось для VB.NET, это включает разъяснение того, куда вставить изменение конфигурации, необходимые импорты и способ добавления обработчика вместо += синтаксиса C#.

    Для любого приложения WPF, а не для каждого проекта, необходимо добавить следующее, чтобы код скомпилировался в один EXE. Он по-прежнему будет включать DLL в выходную папку, но EXE будет содержать их все.

    1. Выгрузите проект WPF (обычно представление)
    2. Щелкните проект правой кнопкой мыши и отредактируйте его.
    3. В документе вставьте следующий код после этой строки
    <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
    

    Код для вставки

    <Target Name="AfterResolveReferences">
       <ItemGroup>
          <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
             <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
             </LogicalName>
          </EmbeddedResource>
       </ItemGroup>
    </Target>
    
    1. Закройте его, сохраните, а затем перезагрузите проект.
    2. В файле Application.xaml.vb добавьте следующий код или, если что-то уже есть в файле, добавьте в него это:
    Imports System.Reflection
    Imports System.Globalization
    Imports System.IO
    
    Class Application
    
        Public Sub New()
            AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf OnResolveAssembly
        End Sub
    
        Private Shared Function OnResolveAssembly(ByVal sender As Object, ByVal args As ResolveEventArgs) As Assembly
    
            Dim executingAssembly As Assembly = Assembly.GetExecutingAssembly()
            Dim assemblyName As AssemblyName = New AssemblyName(args.Name)
            Dim path = assemblyName.Name & ".dll"
            If assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) = False Then path = String.Format("{0}\{1}", assemblyName.CultureInfo, path)
    
            Using stream As Stream = executingAssembly.GetManifestResourceStream(path)
                If stream Is Nothing Then Return Nothing
                Dim assemblyRawBytes = New Byte(stream.Length - 1) {}
                stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length)
                Return Assembly.Load(assemblyRawBytes)
            End Using
    
        End Function
    
    End Class
    
    20.06.2019

    10

    Начиная с .NET Core 3.0 эта функциональность теперь является частью .NET:

    https://docs.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#single-file-executables

    Вы можете опубликовать как один исполняемый файл, используя dotnet:

    dotnet publish -r win-x64 -p:PublishSingleFile=true
    

    Или выполните аналогичную операцию в Visual Studio: в настройках профиля публикации выберите целевую среду выполнения (вы должны выбрать ее для публикации в виде одного файла), затем разверните раздел «Параметры выбора файла» и выберите «Создать один файл». Точные шаги могут различаться в зависимости от версии Visual Studio.

    Вы также можете указать эти значения по умолчанию в файле .csproj:

    <PropertyGroup>
      <RuntimeIdentifier>win10-x64</RuntimeIdentifier>
      <PublishSingleFile>true</PublishSingleFile>
    </PropertyGroup>
    

    Однако при использовании этого подхода у меня возникли проблемы с запуском модульных тестов, поэтому лично я просто выбираю этот вариант при публикации.

    11.06.2020
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..