Встраивание сборок в другую сборку - PullRequest
40 голосов
/ 21 октября 2008

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

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


Я также немного озадачен, почему сборки .NET представляют собой .dll файлы. Разве этот формат не существовал до .NET? Являются ли все библиотеки .NET DLL, но не все библиотеки DLL .NET? Почему они используют одинаковый формат и / или расширение файла?

Ответы [ 8 ]

55 голосов
/ 09 марта 2009

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

В качестве альтернативы ilmerge, вы можете встроить одну или несколько сборок в качестве ресурсов в ваш exe или DLL. Затем во время выполнения, когда загружаются сборки, вы можете программно извлечь встроенную сборку, загрузить и запустить ее. Звучит сложно, но есть только небольшой пример кода.

Чтобы сделать это, вставьте сборку так же, как и любой другой ресурс (изображение, файл перевода, данные и т. Д.). Затем установите AssemblyResolver, который вызывается во время выполнения. Это должно быть установлено в статическом конструкторе класса запуска. Код очень прост.

    static NameOfStartupClassHere()
    {
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver);
    }

    static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args)
    {
        Assembly a1 = Assembly.GetExecutingAssembly();
        Stream s = a1.GetManifestResourceStream(args.Name);
        byte[] block = new byte[s.Length];
        s.Read(block, 0, block.Length);
        Assembly a2 = Assembly.Load(block);
        return a2;
    }

Свойство Name в параметре ResolveEventArgs - это имя сборки, которую необходимо разрешить. Это имя относится к ресурсу, а не к имени файла. Если вы встраиваете файл с именем «MyAssembly.dll» и называете встроенный ресурс «Foo», то здесь вам нужно имя «Foo». Но это может сбить с толку, поэтому я предлагаю использовать имя файла сборки для имени ресурса. Если вы правильно внедрили и назвали свою сборку, вы можете просто вызвать GetManifestResourceStream () с именем сборки и загрузить сборку таким образом. Очень просто.

Это работает с несколькими сборками, так же хорошо, как с одной встроенной сборкой.

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

В остальной части кода приложения вы используете типы из сборки как обычно.

Когда вы собираете приложение, вам нужно добавить ссылку на соответствующую сборку, как вы это обычно делаете. Если вы используете инструменты командной строки, используйте параметр / r в csc.exe; если вы используете Visual Studio, вам потребуется «Добавить ссылку ...» во всплывающем меню проекта.

Во время выполнения проверка и проверка версии сборки работает как обычно.

Разница только в распределении. Когда вы развертываете или распространяете свое приложение, вам не нужно распространять DLL для встроенной (и ссылочной) сборки. Просто разверните основную сборку; нет необходимости распространять другие сборки, потому что они встроены в основную DLL или EXE.

35 голосов
/ 21 октября 2008

Посмотрите на ILMerge для объединения сборок.

Я также немного озадачен тем, почему сборки .NET являются файлами .dll. Разве этот формат не существовал до .NET?

Да.

Являются ли все библиотеки .NET для сборок,

Как DLL, так и EXE обычно - но также может быть сетевым модулем.

но не все библиотеки DLL являются сборками .NET?

Correct.

Почему они используют одинаковый формат и / или расширение файла?

Почему это должно быть иначе - это служит той же цели!

7 голосов
/ 21 октября 2008

Вы можете встроить сборку (или фактически любой файл) в качестве ресурса (а затем использовать класс ResourceManager для доступа к ним), но если вы просто хотите объединить сборки, вы лучше использовать такой инструмент, как ILMerge .

Файлы EXE и DLL представляют собой переносимые исполняемые файлы Windows , которые достаточно универсальны для размещения будущих типов кода, включая любой код .NET (они также могут выполняться в DOS, но отображать только сообщение о том, что они ' не должен работать в DOS). Они включают инструкции для запуска среды выполнения .NET, если она еще не запущена. Также возможно, чтобы одна сборка охватывала несколько файлов, хотя это вряд ли когда-либо происходит.

5 голосов
/ 14 мая 2009

Примечание. ILMerge не работает со встроенными ресурсами, такими как XAML, поэтому приложения WPF и т. Д. Должны будут использовать метод Cheeso.

4 голосов
/ 21 октября 2008

Существует также утилита mkbundle, предлагаемая проектом Mono

1 голос
/ 10 декабря 2012

Почему они используют один и тот же формат и / или расширение файла?

Почему это должно быть иначе - это служит той же цели!

Мои 2 ¢ пояснения: DLL - это Dynamic Link Library. И старый стиль .dll (C-код) и .net стиль .dll по определению являются библиотеками "динамического соединения". Так что .dll является подходящим описанием для обоих.

0 голосов
/ 14 ноября 2018

Я бы предложил вам попробовать Costura.Fody . Только не забудьте установить пакет Fody до Costura.Fody (чтобы получить новейшую версию Fody!)

0 голосов
/ 15 января 2016

Что касается ответа Cheeso о встраивании сборок в качестве ресурсов и их динамической загрузке с использованием перегрузки Load (byte []) с помощью обработчика событий AssemblyResolve, вам необходимо изменить распознаватель, чтобы проверить домен AppDomain для существующего экземпляра сборки загрузить и вернуть существующий экземпляр сборки, если он уже загружен.

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

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

https://msdn.microsoft.com/en-us/library/dd153782%28v=vs.110%29.aspx

Пара существенных пунктов по ссылке:

"Другие сборки не могут привязываться к сборкам, загруженным без контекста, если вы не обработаете событие AppDomain.AssemblyResolve"

«Загрузка нескольких сборок с одним и тем же идентификатором без контекста может вызвать проблемы с идентификацией типа, аналогичные тем, которые возникают при загрузке сборок с одним и тем же идентификатором в нескольких контекстах. См. Избегайте загрузки сборки в несколько контекстов.»

...