Компоновка, упаковка, развёртывание и администрирование приложений и типов
Задачи развёртывания в .NET Framework
.NET Framework в значительной степени решает "кошмар DLL" (ситуация, когда установка новой программы может повлиять на работоспособность уже установленных) и делает шаг к решению проблемы с распределением данных по всей системе. Совершенствование системы защиты связано с новой моделью безопасности - безопасностью доступа на уровне кода (основана на правах, которые контролируются хостом приложения).
Компоновка типов в модуль
Есть простая программа:
public sealed class Program
{
public static void Main()
{
System.Console.WriteLine("Hi");
}
}
Её можно скомпилировать командой csc.exe /out:Program.exe /t:exe /r:MSCorLib.dll Program.cs. Здесь:
- csc.exe - C Sharp Compiler
- /out:Program.exe - имя создаваемого файла
- /t[arget].exe - тип создаваемого файла (в данном случае консольное приложение Win32, также можно использовать /t:winexe для GUI или /t:appcontainerexe для Windows Store)
- /r[eference]:MSCorLib.dll - сборка для поиска внешних типов
По факту имена исполняемого файла и кода совпадают, можно опустить параметр /t. Как и параметр /r, так как библиотека MSCorLib.dll подгружается автоматически. В итоге получается стандартный PE32.
Файл параметров
Файл параметров (CSC.rsp по умолчанию или другое имя файла, определённого рядом с проектом) используется для вынесения общих параметров компилятора. Таких, например, как ссылки на все стандартные библиотеки. При компиляции конфиги в командной строке переопределяют локальные конфиги (в текущем каталоге), а локальные - глобальные (рядом с CSC.exe).
Несколько слов о метаданных
Управляемый PE-файл состоит из 4 блоков:
- Заголовок PE32(+) - стандартная информация, ожидаемая Windows.
- Заголовок CLR - небольшой блок информации, специфичной для модулей, требующих CLR (старший и младший номера версии CLR, ряд флагов, маркер MethodDef, указывающий на точку входа, размер и смещение некоторых таблиц метаданных).
- Метаданные - блок двоичных данных из нескольких таблиц (определений, ссылок и манифестов).
- IL-код
Основные таблицы определений:
- ModuleDef - Одна запись, идентифицирующая модуль (имя файла модуля с расширением и идентификатор версии в виде GUID).
- TypeDef - По одной записи для каждого типа в модуле (имя типа, базовый тип, флаги сборки и указатели на другие таблицы, описывающие члены типа).
- MethodDef - По одной записи для каждого метода в модуле (имя метода, флаги, сигнатуру и смещение в модуле, по которому находится IL-код, а также указатели на ParamDef, где хранятся данные о параметрах метода).
- FieldDef - По одной записи для каждого поля в модуле (тип поля и флаги).
- ParamDef - По одной записи для каждого параметра в модуле (тип, имя и флаги).
- PropertyDef - По одной записи для каждого свойства (имя, флаги, тип и вспомогательное поле, которое может быть пустым).
- EventDef - По одной записи для каждого события (имя и флаги).
Основные таблицы ссылок:
- AssemblyRef - По одной записи для каждой сборки, на которую ссылается модуль (имя, номер версии, региональные стандарты и маркер открытого ключа).
- ModuleRef - По одной записи для каждого PE-модуля, реализующего типы, на которые он ссылается (имя файла сборки и расширение).
- TypeRef - По одной записи для каждого типа, на который ссылается модуль (имя типы и ссылку: TypeRef, ModuleDef, ModuleRef или AssemblyRef).
- MemberRef - По одной записи для каждого члена типа, на который ссылается модуль (имя и сигнатуру члена, указатель на TypeRef).
Объединение модулей для создания сборки
Получившийся Program.exe - это не просто PE-файл с метаданными, но ещё и сборка, то есть совокупность нескольких (в данном случае одного) файлов с определениями типов и файлов ресурсов. Один из файлов выбирается для хранения манифеста - набора таблиц метаданных, которые содержат имена файлов, составляющих сборку, а также версию, региональные стандарты сборки, её издателя и так далее.
CLR работает со сборками, соответственно, сначала загружается манифест, а затем остальные файлы. Некоторые характеристики сборки:
- В сборке определены многократно используемые типы.
- Сборке назначается номер версии.
- Со сборкой может быть связана информация безопасности.
Чтобы упаковать типы, а также обеспечить их безопасность и управление версиями, нужно поместить типы в модули, объединённые в сборку.
Сборка позволяет разграничить логическое и физически понятие многократно используемых типов. При этом применяемые чаще типы можно поместить в один файл, а применяемые реже - в другой.
При попытке загрузить файл сборки CLR получает URL и проверяет наличие в локальном кэше. Если файл есть, то он загружается, если нет - CLR использует для загрузки URL. Если файл не найден, то генерируется исключение FileNotFoundException.
Сборка - единица многократного использования, управления версиями и безопасности типов. Загрузив файл с манифестом, CLR может определить, какие файлы сборки содержат типы и ресурсы, на которые ссылается приложение. Таким образом можно абстрагироваться от особенностей распределения содержимого сборки по файлам. При работе с многими типами, совместно использующими одну версию и набор параметров безопасности, по соображениям производительности рекомендуется размещать все типы в одном файле.
Для компоновки сборки нужно выбрать один PE-файл, который будет хранителем манифеста. Или это может быть отдельный PE-файл, который не содержит ничего, кроме манифеста. Таблицы метаданных манифеста, которые превращают управляемый модуль в сборку:
- AssemblyDef - Единственная запись, если модуль идентифицирует сборку (имя сборки без расширения и пути, сведения о версии, региональные стандарты, флаги, алгоритм хеширования и открытый ключ издателя).
- FileDef - По одной записи для каждого PE-файла и файла ресурсов, кроме файла манифеста (имя и расширение без пути, хеш-код и флаги).
- ManifestResourceDef - по одной записи для каждого ресурса (имя ресурса, флаги, индекс для FileDef).
- ExportedTypesDef - Записи для всех открытых типов, экспортируемых всеми PE-модулями (имя типа, индекс для FileDef, индекс для TypeDef).
Файл сборки, содержащий манифест, содержит таблицу AssemblyRef, где хранятся записи с описанием всех сборок, на которые ссылаются файлы. Это делает сборку самоописываемой.
Если компилятор генерирует исполняемый файл, то это создаёт PE-файл с таблицами метаданных манифеста. Можно также создать PE-файл без манифеста - получится DLL в формате PE. В Visual Studio отсутствует поддержка создания многофайловых сборок.
Добавление сборок в проект в среде Visual Studio
Использование утилиты Assembly Linker
Вместо компилятора можно использовать компоновщик сборок (assembly linker, AL.exe). Компоновщик генерирует сборку только из манифеста, без IL-кода.
Включение в сборку файлов ресурсов
...
Ресурсы со сведениями о версии сборки
При компоновке через компилятор сведения о сборке задаются через атрибуты в коде, а при assembly linker'ом - через параметры командной строки.
Номера версии
...
Региональные стандарты
Сборку без регионального стандарта называют сборкой с нейтральными региональными стандартами. Ресурсные файлы специфичные для региональных стандартов стоит выделять в отдельны сборки, которые называются сопутствующими.
Развёртывание простых приложений (закрытое развёртывание сборок)
Сборки с закрытым развёртыванием - сборки, которые разворачиваются в том же каталоге, что и приложение.
Простое средство администрирования (конфигурационный файл)
Идея в том, что рядом с приложением есть конфигурационный файл, с помощью которого приложение может управлять своим каталогом и его подкаталогами, но не может управлять другими каталогами.
Алгоритм поиска файлов сборки
...
