Avatar ·

Как встроить DLL-библиотеки в скомпилированный EXE-файл C#?

Всем привет! Я пишу небольшое приложение на C# (Windows Forms), которое использует стороннюю библиотеку — например, Newtonsoft.Json или iTextSharp. Когда я собираю проект в Visual Studio, в папке Debug/Release появляется мой .exe-файл и куча .dll-файлов. Пользователям это неудобно: им нужно следить, чтобы все библиотеки лежали рядом с exe-шником, иначе программа не запустится. Я хочу сделать так, чтобы все DLL-ки встраивались прямо внутрь одного .exe-файла. То есть пользователь получает один файл, запускает его — и всё работает без дополнительных манипуляций. Пробовал гуглить, наткнулся на ILMerge, но он устаревший и не поддерживает .NET Core / .NET 5+. Также слышал про Costura.Fody, но не уверен, как его правильно настроить. Есть ли современный способ сделать это в .NET 6/7/8? Или может быть можно как-то вручную подгружать DLL через AssemblyResolve? Хотелось бы увидеть рабочий пример, желательно с кодом. Вот пример моей структуры проекта (консольное приложение .NET 6): ``` using Newtonsoft.Json; var data = new { Name = "Test", Value = 42 }; string json = JsonConvert.SerializeObject(data); Console.WriteLine(json); ``` После сборки в папке появляются: MyApp.exe, Newtonsoft.Json.dll. Хочу, чтобы остался только MyApp.exe. Как это сделать правильно и надёжно? Заранее спасибо! <<>>

Решение: встраивание DLL с помощью Costura.Fody (современный способ)

Самый простой и надёжный способ для .NET Framework, .NET Core и .NET 5+ — использовать пакет Costura.Fody. Он автоматически встраивает все зависимые сборки (DLL) в ваш основной EXE-файл во время сборки. Всё, что вам нужно — установить пакет и добавить одну строку в конфигурацию.

Пошаговая инструкция

  1. Установите Costura.Fody через NuGet Package Manager или консоль:
    Install-Package Costura.Fody
    Или через .NET CLI:
    dotnet add package Costura.Fody
  2. Добавьте файл FodyWeavers.xml в корень вашего проекта (если его нет, он создаётся автоматически после установки пакета). Откройте его и убедитесь, что там есть строка:
    <Weavers>
        <Costura />
    </Weavers>
  3. Соберите проект — теперь все DLL будут встроены в EXE. Проверьте: удалите все DLL из папки сборки и запустите .exe — он должен работать.

Настройка: исключение определённых сборок

Если вы хотите исключить какие-то DLL из встраивания (например, нативные библиотеки), добавьте атрибуты в FodyWeavers.xml:

<Costura>
    <ExcludeAssemblies>
        SomeNativeLib
    </ExcludeAssemblies>
</Costura>

Как это работает под капотом?

Costura модифицирует ваш EXE на этапе сборки (через MSBuild task). Он добавляет все DLL как встроенные ресурсы (Embedded Resource). При запуске программы срабатывает обработчик события AppDomain.CurrentDomain.AssemblyResolve, который загружает сборки из этих ресурсов. Вам не нужно писать ни строчки кода — всё автоматизировано.

Пример с вашим кодом

После установки Costura.Fody ваш проект будет выглядеть так же, как раньше. Единственный файл, который появится — FodyWeavers.xml. При сборке в папке bin\Release\net6.0 останется только MyApp.exe (и, возможно, .pdb для отладки). Запустите его — Newtonsoft.Json будет загружен изнутри EXE.

Важные замечания

  • Производительность: при первом запуске может быть небольшая задержка (доли секунды), так как DLL распаковываются в память. Для большинства приложений это незаметно.
  • Нативные DLL: Costura не умеет встраивать нативные библиотеки (C++ DLL). Для них придётся использовать отдельные решения (например, распаковка во временную папку через AssemblyResolve).
  • Лицензирование: некоторые библиотеки запрещают встраивание — проверьте лицензию.
  • Размер: EXE станет больше на суммарный размер всех DLL. Это нормально.

Альтернатива: ручной AssemblyResolve

Если Costura по каким-то причинам не подходит, можно написать свой обработчик. Вот минимальный пример:

using System;
using System.IO;
using System.Reflection;

class Program
{
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            // Извлекаем имя сборки
            string assemblyName = new AssemblyName(args.Name).Name;
            // Ищем ресурс с именем "ИмяПроекта.DLLs." + assemblyName + ".dll"
            string resourceName = "MyApp.DLLs." + assemblyName + ".dll";
            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                if (stream == null) return null;
                byte[] assemblyData = new byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                return Assembly.Load(assemblyData);
            }
        };

        // Ваш код
        var data = new { Name = "Test", Value = 42 };
        string json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
        Console.WriteLine(json);
    }
}

Для этого способа нужно добавить DLL как встроенные ресурсы (Build Action = Embedded Resource) в папку DLLs внутри проекта. Это более трудоёмко, но даёт полный контроль.

Итог

Рекомендую использовать Costura.Fody — это стандарт де-факто для встраивания DLL в .NET. Установка занимает 1 минуту, а результат — чистый и переносимый EXE-файл.

<<>>

Альтернативный подход: публикация как single-file приложение (.NET Core 3.0+)

Если вы используете .NET Core 3.0, .NET 5, 6, 7 или 8, у вас есть встроенная возможность создать single-file приложение без сторонних инструментов. Это не встраивание DLL в EXE, а упаковка всего приложения (включая рантайм и библиотеки) в один файл.

Как это сделать

  1. Откройте файл проекта (.csproj) и добавьте или измените свойство PublishSingleFile:
    <PropertyGroup>
        <PublishSingleFile>true</PublishSingleFile>
        <SelfContained>false</SelfContained>  <!-- или true, если хотите включить рантайм -->
        <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    </PropertyGroup>
  2. Опубликуйте приложение командой:
    dotnet publish -c Release -r win-x64 --self-contained false
    Флаг --self-contained false означает, что .NET рантайм не включается (пользователь должен его установить). Если поставить true, получится один EXE, который работает без установленного .NET.

Отличия от Costura.Fody

  • Плюсы: не требует дополнительных пакетов, официальная поддержка Microsoft, работает с нативными библиотеками (если они есть).
  • Минусы: EXE — это не настоящий «встроенный» файл, а скорее архив. При запуске DLL распаковываются во временную папку (обычно %TEMP%\.net\). Это может вызвать проблемы с антивирусами или правами доступа. Кроме того, single-file публикация может быть медленнее при первом запуске.

Когда выбирать этот способ

Если вам не нужна поддержка старых .NET Framework (только .NET Core 3+), и вы готовы мириться с распаковкой во временную папку — используйте встроенный single-file. Для максимальной совместимости и простоты лучше взять Costura.Fody.