Объединение dll в один .exe с wpf

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

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

Ответ 1

. NET-реактор имеет свойство слияния сборок и его не очень дорого.

Ответ 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) Сделайте основной 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);
    }
}

Ответ 3

Costura Fody - это инструмент с открытым исходным кодом, предназначенный для обработки объединяющихся сборок wpf.

https://github.com/Fody/Costura#how-it-works

Ответ 4

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

Попробуйте следующее: http://www.smartassembly.com/

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

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

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

Ответ 5

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

Многие приложения состоят из EXE файла, который зависит от многих DLL файлы. При развертывании этого приложения все файлы должны быть развертывается. Тем не менее, существует метод, который можно использовать для развертывания просто один EXE файл. Сначала определите все файлы DLL, которые EXE файл зависит от того, который не поставляется как часть Microsoft.NET Сама по себе. Затем добавьте эти DLL в проект Visual Studio. Для каждого добавляемого DLL файла отобразите его свойства и измените его "Построить действие" на "Встроенный ресурс". Это приводит к тому, что компилятор С# вставьте DLL файл в ваш EXE файл, и вы можете развернуть этот файл EXE файл. Во время выполнения CLR не сможет найти зависимую DLL, что является проблемой. Чтобы исправить это, когда ваше приложение инициализирует, регистрирует метод обратного вызова с AppDomains Событие ResolveAssembly. Код должен выглядеть примерно так:

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 и загрузите его, вызвав перегрузку метода Loads Loads, который принимает байт [] в качестве аргумента.

Ответ 6

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

Install-Package Costura.Fody

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

Ответ 7

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

Ответ 8

Вот уточненная версия цитированного кода от 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, как указано выше. Чтобы использовать их в коде, вам все равно нужно установить ссылку на них, но установите Копировать локально в False. Чтобы получить код для компиляции без ошибок, вам также необходимо установить с помощью статусов в вашем коде пространства имен 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 
            {
            }
        }
    }
}

Ответ 9

  • добавьте это в файл .csprofj:
<Р →
<Target Name="AfterResolveReferences">
  <ItemGroup>
    <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
      <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
    </EmbeddedResource>
  </ItemGroup>
</Target>
  1. щелкните правой кнопкой мыши project/properties/application/starup object/выберите 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

Ответ 10

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

Для любого приложения 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