Как правило, при написании .NET программ используются не только классы из .NET BCL, но и сторонние библиотеки. Во время выполнения программы все используемые библиотеки должны быть найдены. Для этого зависимые dll кладут в одну папку с exe файлом.Однако существуют программы, использующие сторонние библиотеки, но при этом состоящие из одного единственного файла. Все утилиты от SysInternals, а также любимый мной LINQPad представляют из себя один файл в котором содержится все, что требуется для работы. Пользоваться такими утилитами одно удовольствие — они сразу готовы к использованию, их удобно передавать и хранить.
В статье рассказывается, как создавать такие автономные программы из одного файла. Разобран пример как со сжатием зашить библиотеку AutoMapper в программу и как ее потом достать и использовать.
Код к статье
Исходный код к статье — скачать
Код программы использует стороннюю библиотеку AutoMapper. Чтобы убедиться в работоспособности библиотеки после ее зашития в ресурсы, в программе вызывается код из семплов к библиотеке. Этот код здесь не приведен, ибо это статья не об AutoMapper. Но сама библиотека интересная и полезная — рекомендую посмотреть, что же она делает в коде.
Подход
Чтобы .NET код мог работать, в использующий его AppDomain нужно загрузить сборку, содержащую типы, которые используются в коде. Если тип находится в сборке, которая еще не была загружена в AppDomain, CLR производит поиск этой сборки по ее полному имени. Поиск происходит в нескольких местах, где именно — зависит от настроек AppDomain. Для настольных приложений это обычно GAC и текущая папка.
Если CLR не удалось найти сборку, вызывается событие AppDomain.AssemblyResolve. Событие дает возможность загрузить требуемую сборку вручную. Поэтому для реализации автономной программы, состоящей из одного exe файл��, достаточно зашить все зависимые сборки в ресурсы и в обработчике AssemblyResolve подгружать их.
Подробности про механизм нахождения сборок можно найти в книге Essential .NET, Volume 1: The Common Language Runtime (Don Box, Chris Sells) — 8 глава, AppDomains and the Assembly Resolver секция.
Архивация сборок
Удобно класть в ресурсы сборки в архивированном виде. Архивация уменьшает размер итоговой программы приблизительно в 2 раза. Скорость запуска увеличивается, но эти доли секунды мало кто заметит. А вот уменьшение размера файла позволит его быстрее качать по сети.
В .NET библиотеке есть архивирующий поток — DeflateStream. По сути он производит zip архивацию, только с патентами не связан. Сжатие сборки зависимой библиотеки происходит следующим образом:
var fileInputName = @"OneExeProgram\Libraries\AutoMapper 1.0 RTW\AutoMapper.dll"; |
Использование сборки из ресурсов
Итак, у нас есть работающий проект, использующий сторонние библиотеки. Хочется, чтобы exe файл проекта был автономен и не требовал наличия зависимых dll в своем каталоге.
Полученную ранее архивированную сборку добавляем в ресурсы проекта через Project Properties-Resources-Files. Студия при добавлении ресурса генерирует код, который позволяет использовать добавленный ресурс через Resources класс.
Регистрируем обработчик AssemblyResolve (до использования классов зависимой библиотеки):
AppDomain.CurrentDomain.AssemblyResolve += AppDomain_AssemblyResolve; |
private static Assembly AppDomain_AssemblyResolve( object sender, ResolveEventArgs args ) |
Для нескольких зависимых библиотек стоит ввести соглашение, согласно которому имя ресурса и искомой сборки совпадают. Тогда можно с помощью отражения автоматически искать требуемую библиотеку в ресурсах.
По умолчанию зависимые библиотеки, добавляемые через References, копируются в выходную директорию проекта. Чтобы AssemblyResolve сработал, нужно либо скопировать выходной exe файл в другую директорию, либо запретить копировать зависимые библиотеки в конечную директорию через References-AutoMapper-Properties-Copy Local=false.
Заключение
Включение зависимых сборок в ресурсы самой программы позволяет ей работать автономно. Для запуска требуется один лишь exe файл. Это важно для служебных утилит, сразу готовых к использованию.
Фактически такие автономные программы не требуют установки и их удобно передавать по сети или хранить на флешке. Архивирование сборок позволяет уменьшить размер программы и больше таких программ разместить на флешке / быстрее выкачивать из сети.