Как Windows Cll DLL может быть объединена в C# приложение exe?

У меня есть Windows C# программа, которая использует C++ DLL для ввода / вывода данных. Моя цель - развернуть приложение как один EXE-файл.

Какие шаги для создания такого исполняемого файла?

9 ответов

Решение

Развертывание управляемого и неуправляемого кода в одной сборке 4 февраля 2007 г.

Разработчики.NET любят развертывание XCOPY. И они любят отдельные компоненты сборки. По крайней мере, я всегда чувствую себя немного неловко, если мне нужно использовать какой-то компонент и мне нужно запомнить список файлов, которые также должны быть включены в основную сборку этого компонента. Поэтому, когда мне недавно пришлось разработать компонент управляемого кода и дополнить его некоторым неуправляемым кодом из DLL C (спасибо Маркусу Хигу за помощь в этом!), Я подумал о том, как упростить развертывание двух DLL., Если бы это были всего две сборки, я бы использовал ILmerge, чтобы собрать их в один файл. Но это не работает для компонентов смешанного кода с управляемыми и неуправляемыми библиотеками DLL.

Итак, вот что я придумал для решения:

Я включаю любые библиотеки DLL, которые я хочу развернуть, с основной сборкой моего компонента в качестве встроенных ресурсов. Затем я установил конструктор класса для извлечения этих DLL, как показано ниже. Класс ctor вызывается только один раз в каждом AppDomain, поэтому я думаю, что это незначительные накладные расходы.

namespace MyLib
{
    public class MyClass
    {
        static MyClass()
        {
            ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll");
            ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll");
        }

        ...

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

Код для извлечения библиотек DLL в свои файлы прост:

public static class ResourceExtractor
{
    public static void ExtractResourceToFile(string resourceName, string filename)
    {
        if (!System.IO.File.Exists(filename))
            using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create))
                {
                    byte[] b = new byte[s.Length];
                    s.Read(b, 0, b.Length);
                    fs.Write(b, 0, b.Length);
                }
    }
}

Работа с управляемой сборкой кода такая же, как обычно - почти. Вы ссылаетесь на него (здесь: ManagedService.dll) в основном проекте вашего компонента (здесь: MyLib), но для свойства Copy Local установлено значение false. Кроме того, вы связываете сборку как существующий элемент и устанавливаете для действия сборки значение "Встроенный ресурс".

Для неуправляемого кода (здесь: UnmanagedService.dll) вы просто связываете в DLL как существующий элемент и устанавливаете для действия сборки значение "Встроенный ресурс". Для доступа к его функциям используйте атрибут DllImport как обычно, например

[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);

Это оно! Как только вы создаете первый экземпляр класса со статическим ctor, встроенные библиотеки DLL извлекаются в собственные файлы и готовы к использованию, как если бы вы развернули их как отдельные файлы. Пока у вас есть права на запись для каталога выполнения, это должно работать для вас нормально. По крайней мере, для прототипного кода, я думаю, этот способ развертывания одной сборки довольно удобен.

Наслаждайтесь!

http://weblogs.asp.net/ralfw/archive/2007/02/04/single-assembly-deployment-of-managed-and-unmanaged-code.aspx

Используйте Fody.Costura nuget

  1. Откройте свое решение -> Проект -> Управление пакетами Nuget
  2. Поиск Fody.Costura
  3. Скомпилируйте ваш проект.

Это оно!

Источник: http://www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/

Попробуйте boxedapp; это позволяет загружать все библиотеки DLL из памяти. Кроме того, кажется, что вы можете даже встроить.net время выполнения. Хорошо для создания действительно автономных приложений...

Просто щелкните правой кнопкой мыши свой проект в Visual Studio, выберите "Свойства проекта" -> "Ресурсы" -> "Добавить ресурс" -> "Добавить существующий файл"… и включите приведенный ниже код в файл App.xaml.cs или аналогичный.

public App()
{
    AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");

    dllName = dllName.Replace(".", "_");

    if (dllName.EndsWith("_resources")) return null;

    System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());

    byte[] bytes = (byte[])rm.GetObject(dllName);

    return System.Reflection.Assembly.Load(bytes);
}

Вот мой оригинальный пост в блоге: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/

Вы пробовали ILMerge? http://research.microsoft.com/~mbarnett/ILMerge.aspx

ILMerge - это утилита, которая может использоваться для объединения нескольких сборок.NET в одну сборку. Он доступен для свободного использования на странице "Инструменты и утилиты" в Центре разработчиков Microsoft .NET Framework.

Если вы создаете C++ DLL с /clr флаг (полностью или частично C++/CLI), тогда он должен работать:

ilmerge /out:Composite.exe MyMainApp.exe Utility.dll

Однако он не будет работать с обычной (родной) Windows DLL.

Если вы хотите упаковать уже существующее приложение (включая его dll и другие ресурсы, независимо от того, на каком языке оно написано) в один .exe, вы можете использовать для этой цели Но он будет «обнаружен» для «запуска вредоносного кода от хакера», потому что он распаковывает сам себя:

Appacker и созданные им пакеты могут быть обнаружены некоторыми антивирусными программами как вредоносное ПО. Это из-за хакерского способа, который я использовал для упаковки файлов: упакованное приложение читает свой собственный исполняемый файл и извлекает из него другие файлы, которые антивирусы считают чертовски подозрительными. Это ложное срабатывание, но оно все еще мешает использованию этого приложения.
-SerGreen на Appacker от SerGreen .GitHub

Чтобы использовать его, вы можете просто открыть его, щелкнуть все предупреждения о вирусах (и попросить Защитника Windows не удалять его!), затем выбрать каталог, который должен быть запакован, и исполняемый файл, который нужно запустить после распаковки.
При желании вы можете изменить поведение распаковки приложения (распаковщик окон/без окон, распаковка целевого каталога, если он будет переупакован или изменения в распакованных файлах будут проигнорированы, ...)

Thinstall является одним из решений. Для собственного приложения Windows я бы предложил встроить DLL в виде объекта двоичного ресурса, а затем извлечь его во время выполнения, прежде чем оно понадобится.

Smart Assembly может сделать это и многое другое. Если ваша dll имеет неуправляемый код, она не позволит вам объединить dll в одну сборку, вместо этого она может встраивать необходимые зависимости в качестве ресурсов в ваш основной exe-файл. Его оборотная сторона, это не бесплатно.

Вы можете сделать это вручную, внедрив dll в свои ресурсы, а затем полагаясь на сборку AppDomain ResolveHandler, Когда дело доходит до смешанного режима DLL, я нашел много вариантов и вкусов ResolveHandler у меня подход не работать (все которые читают байты dll в память и читают из нее). Все они работали на управляемые библиотеки. Вот что сработало для меня:

static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        string assemblyName = new AssemblyName(args.Name).Name;
        if (assemblyName.EndsWith(".resources"))
            return null;

        string dllName = assemblyName + ".dll";
        string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);

        using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
        {
            byte[] data = new byte[stream.Length];
            s.Read(data, 0, data.Length);

            //or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);

            File.WriteAllBytes(dllFullPath, data);
        }

        return Assembly.LoadFrom(dllFullPath);
    };
}

Ключевым моментом здесь является запись байтов в файл и загрузка из него. Чтобы избежать проблемы с курицей и яйцом, необходимо убедиться, что вы объявили обработчик перед доступом к сборке, и чтобы у вас не было доступа к элементам сборки (или к созданию экземпляров всего, что имеет отношение к сборке) внутри загрузочной (разрешающей сборку) детали. Также позаботьтесь о том, чтобы GetMyApplicationSpecificPath() не является временным каталогом, поскольку временные файлы могут пытаться стереть другие программы или вы сами (не то, что он будет удален, когда ваша программа обращается к dll, но, по крайней мере, это неприятно. AppData - это хорошее расположение). Также обратите внимание, что вы должны записывать байты каждый раз, вы не можете загрузить их из местоположения, просто потому что dll уже находится там.

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

PostBuild от Xenocode может упаковать как управляемые, так и неуправляемые в один exe-файл.

Другие вопросы по тегам