Гл а в а 2 . Ком поновка ,
уп а ко вка , разверты ван и е
и адм и н и стр и рова н и е
- п риложе н и и и ти пов
П режде чем перейти к г лава м , описывающим разработку программ для
Microsoft .NET Framework, давайте обсудим вопросы создания, упаковки и развертывания
приложений и их типов. В этой главе акцент сделан на основах
создания компонентов, предназначенных исключительно для ваших приложений.
В г лаве 3 рассказано о ряде более сложных, но очень важных концепций,
в том числе способах создания и применения сборок, содержащих компоненты,
предназначенные для использования совместно с другими приложениями.
В этой и следующей главах также показано, как администратор может влиять
на исполнение приложения и его типов.
Современные приложения состоят из типов, которые создаются самими разработчиками
или компанией Microsoft. Помимо этого, процветает целая отрасль
поставщиков компонентов, которые используются клиентами, чтобы сократить
время на разработку проектов. Типы, реализованные при помощи языка, ориентированного
на общеязыковую исполняющую среду ( CLR), способны легко
работать друг с другом, базовый класс такого типа можно даже написать на
другом языке программирования.
В этой главе объясняется, как эти типы создаются и упаковываются в файлы,
предназначенные для развертывания. В процессе изложения дается краткий
исторический обзор некоторых проблем, решенных с приходом .NET
Framework.
Задач и разверты вания
в . N ET Framework
Все годы своего существования операционная система Windows •славилась􀃘
нестабильностью и чрезмерной сложностью. Такая репутация, заслуженная или
нет, сложилась по ряду причин. Во-первых, все приложения используют динамически
подключаемые библиотеки ( Dynamic Link Library, DLL), созданные
Microsoft и другими производителями. Поскольку приложение исполняет код,
нписанный разными производителями, ни один разработчик какой-либо части
Задач и развертывания в . N ET Framework 35
программы не может быть на 1 00 % уверен в том, что точно знает, как другие
собираются применять созданный им код. В теории такая ситуация чревата любыми
неполадками, но на практике взаимодействие кодов от разных производителей
редко становится источником проблем, так как перед развертыванием
приложения тестируют и отлаживают.
Однако пользователи часто сталкиваются с проблемами, когда производитель
решает обновить поставленную им программу и предоставляет новые файлы.
Предполагается, что новые файлы обеспечивают qобратную совместимость􀃘
с прежним программным обеспечением, но кто за это поручится? Одному производителю,
выпускающему обновление своей программы, фактически не под
силу заново протестировать и от ладить все существующие приложения, чтобы
убедиться, что изменения при обновлении не влекут за собой межелательных
последствий.
Уверен, что каждый читающий эту книгу сталкивался с той или иной разновидностью
проблемы, когда после установки нового приложения нарушалась
работа одной (или нескольких) из установленных ранее программ. Эта проблема
получила название <<ад DLL􀃘. Подобная уязвимость вселяет ужас в сердца
и умы обычных пользователей компьютеров. В конечном итоге пользователи
должны как следует обдумать, стоит ли устанавливать новое программмое обеспечение
на их компьютеры. Что касается меня, то я решил вовсе не пробовать
устанавливать некоторые приложения из опасения, что они нанесут вред наиболее
важным для меня программам.
Второй фактор, повлиявший на репутацию Windows, - сложности при
установке приложений. Большинство приложений при установке умудряются
<<просочиться􀃘 во все части операционной системы. Н апример, при установке
приложения происходит копирование файлов в разные каталоги, модификация
параметров реестра, установка ярлыков и ссылок на рабочий стол ( Desktop) ,
в меню Пуск ( Start) и н а паиель быстрого запуска. Проблема в том, что приложение
- это не одиночная изолированная сущность. Нельзя легко и просто
создать резервную копию приложения, поскольку, кроме файлов приложения,
придется скопировать соответствующие части реестра. Вдобавок, нельзя просто
взять и переместять приложение с одной машины на другую - для этого
нужно запустить программу установки еще раз, чтобы корректно скопировать
все файлы и параметры реестра. Наконец, приложение не всегда просто удалить
- часто остается неприятное ощущение, что какая-то его часть затаилась
где-то внутри компьютера.
Третий фактор - безопасность. При установке приложений записывается
множество файлов, созданных самыми разными компаниями. Вдобавок, многие
веб-приложения (например, ActiveX) зачастую содержат проrраммный код, который
сам загружается из Интернета, о чем пользователи даже не подозревают.
На современном уровне технологий такой код может выполнять любые действия,
включая удаление файлов и рассылку электронной почты. Пользователи
справедливо опасаются устанавливать новые приложения из-за угрозы потен-
36 Глава 2. Компоновка, упаковка, развертывание и администрирование
циального вреда, который может быть нанесен их компьютерам. Для того чтобы
пользователи чувствовали себя спокойнее, в системе должны быть встроенные
функции защиты, позволяющие явно разрешать или запрещать доступ к системным
ресурсам коду, созданному теми или иными компаниями.
Как показано в этой и следующей главах, платформа .NET Framework
устраняет •ад D LL• и делает существенный шаг вперед к решению проблемы,
связанной с распределением данных приложения по всей операционной системе.
Например, в отличие от модели С О М компонентам больше не требуется
хранить свои параметры в реестре. К сожалению, приложениям пока еще требуются
ссылки и ярлыки. Совершенствование системы защиты связано с новой
моделью безопасности платформы .NET Framework - безопасностью доступа
на уровне кода (code access security) . Если безопасность системы Windows
основана на идентификации пользователя, то безопасность доступа к коду - на
правах, которые контролируются хостом приложений, загружающим компоненты.
Такой хает приложения, как Microsoft Silverlight, может предоставить
совсем немного полномочий загруженному программному коду, в то время как
лакальна установленное приложение во время своего выполнения может иметь
уровень полного доверия (со всеми полномочиями). Как видите, платформа
.NET Framework предоставляет пользователям намного больше возможностей
по контролю над тем, что устанавливается и выполняется на их машинах, чем
когда-либо давала им система Windows.
Компоновка типов в модул ь
В этом разделе рассказывается, как превратить файл, содержащий исходный
код с разными типами, в файл, пригодный для развертывания. Для начала рассмотрим
следующее простое приложение:
puЫ i c s e a l ed c l a s s P rogram {
puЫ i c s t at i c v o i d M a i n ( ) {
Syst em . Consol е . W r i tel i ne ( " H i " ) :
Здесь определен тип Program с единственным статическим открытым методом
M a i n. Внутри метода M a i n находится ссылка на другой тип - System . Consol e.
Этот тип разработан в компании Microsoft, и его программный код на языке
IL, реализующий его методы, находится в файле MSCorlib.dll. Таким образом,
данное приложение определяет собственный тип, а также использует тип, созданный
другой компанией.
Для того чтобы скомпоновать это приложение-пример, сохраните этот код
в файле Pro g ram.cs, а затем наберите в командной строке следующее:
c s c . exe /out : P rogram . exe / t : exe / r : MSCo r l i b . d l l Program . c s
Ком поновка типов в модул ь 37
Эта команда указывает компилятору С# создать исполняемый файл Progra m .
ехе (имя задано параметром / o ut : P rogram . exe). Тип создаваемого файла - консольное
приложение Win32 (тип задан параметром / t [ a rget] : ех е).
При обработке файла с исходным кодом компилятор С # обнаруживает
ссылку на метод W r i t e l i n e типа Sys t em . Co n s o l e . На этом этапе компилятор
должен убедиться, что этот тип существует и у него есть метод W r i t e l i n e .
Компилятор также проверяет, чтобы типы аргументов, предоставляемых программой,
совпадали с ожидаемыми типами метода Wri tel i ne. Поскольку тип
не определен в исходном коде на С#, компилятору С# необходимо передать
набор сборок, которые позволят ему разрешить все ссылки на внешние типы.
В показаиной команде параметр / r [ efe rence] : MSC o r L i Ь . d l l приказывает компилятору
вести поиск внешних типов в сборке, идентифицируемой файлом
MSCorli b.d l l .
MSCorli b . d l l - это особый файл в том смысле, что в нем находятся все
основные типы: Byte, Char, Stri ng, I nt32 и т. д. В действительности, эти типы используются
так часто, что компилятор С# ссылается на эту сборку ( M SCorli b .
dll) автоматически. Другими словами, следующая команда ( в н е й опущен параметр
1 r) даст тот же результат, что и предыдущая:
csc . exe / out : Program . exe / t : exe P rogram . cs
Более того, поскольку значения, заданные параметрами командной строки
/ out : Program . exe и / t : exe, совпадают со значениями по умолчанию, следующая
команда даст аналогичный результат:
csc . exe P rogram . cs
Если по какой-то причине вы не хотите, чтобы компилятор С# ссылался
на сборку MSCorli b . d l l, используйте параметр / nostdl i Ь. В компании M icrosoft
задействуют именно этот параметр при компоновке сборки M SC o r li b . d l l .
Например, в о время исполнения следующей команды п р и компиляции файла
Program.cs генерируется ошибка, поскольку тип Sys tem . Co n s o 1 е определен
в сборке MSCorli b . d l l :
csc . exe /out : Program . exe / t : exe /nostd l i b P rogram . cs
А теперь присмотримся поближе к файлу Pr ogram.exe, созданному компилятором
С#. Что он из себя представляет? Для начала это стандартный файл
в формате РЕ (portaЬle executaЬle). Это значит, что машина, работающая под
управлением 32- или 64-разрядной версии Windows, способна загрузить этот
файл и что-нибудь с ним сделать. Система Windows поддерживает два типа
приложений: с консольными ( Console User Interface, C U I ) и графическими
пользовательскими интерфейсами ( Graphical User Interface, G U I ) . Параметр
/t : ехе указывает компилятору С# создать консольное приложение. Для создания
приложения с графическим интерфейсом необходимо указать параметр
/t : wi nexe.
38 Глава 2. Комп оновка, упаковка, развертывание и адм инистрирование
Файл параметров
В завершение рассказа о параметрах компилятора хотелось б ы сказать несколько
слов о файлах параметров (response files) - текстовых файлах, содержащих
набор параметров командной строки для компилятора. При выполнении компилятора
CSC.exe открывается файл параметров и используются все указанные
в нем параметры, как если бы они были переданы в составе командной строки.
Файл параметров передается компилятору путем указания его в командной
строке с префиксом @. Например, пусть есть файл параметров MyProj ect. rsp со
следующим текстом:
/out : My P roj ect . exe / t a rget : wi nexe
Для того чтобы компилятор (CSC.exe) использовал эти параметры, необходимо
вызвать файл следующим образом:
c s c . exe @My P roj ect . rs p CodeFi l e l . cs Code F i l e2 . cs
Эта строка сообщает компилятору С# имя выходного файла и тип скомпилированной
программы. Очевидно, что файлы параметров исключительно
полезны, так как избавляют от необходимости вручную вводить все аргументы
командной строки каждый раз при компиляции проекта.
Компилятор С# поддерживает работу с несколькими файлами параметров.
Помимо явно указанных в командной строке файлов, компилятор автоматически
ищет файл с именем CSC. rsp в текущем каталоге, поэтому относящиеся
к проекту параметры нужно указывать именно в этом файле. Компилятор также
проверяет каталог с файлом CSC.exe на наличие глобального файла параметров
C SC . rsp, в котором следует указывать параметры, относящиеся ко всем
проектам. В процессе своей работы компилятор объединяет параметры из всех
файлов и использует их. В случае конфликтующих параметров в глобальных
и локальных файлах предпочтение отдается последним. Кроме того, любые
явно заданные в командной строке параметры имеют более высокий приоритет,
чем указанные в локальных файлах параметров.
При установке платформы .NET Framework по умолчанию глобальный
файл C SC. rsp устанавливается в каталог %SystemRoot % \ M i c rosoft . NET\ F ramewo rk\
vX . X . X (где Х.Х.Х - версия устанавливаемой платформы .NET Framework).
В версии 4.0 этот файл содержит следующие параметры:
# Этот фа й л содержит n а р а метры к о м а н д н о й с т р о к и .
# которые к ом n и л я тор С# к о м а н д н о й с т р о к и ( CSC )
# будет обраба т ы в а т ь в каждом с е а н с е к о м n ил я ц и и .
# ес л и тол ь ко не з а д а н n а ра ме т р " / noconfi g " .
# Ссыл к и н а с т а н д а р т н ы е б и б л и о т е к и Framewo rk
/ r : Acces s i b i l i ty . d l l
/ r : Mi c ro s o ft . CSha rp . d l l
/ r : System . Confi g u r at i on . d l l
/ r : System . Confi g u r a t i on . I n st a l l . d l l
/ r : System . Core . d l l
/ r : System . Data . d l l
/ r : System . Data . DataSetExtens i ons . d l l
/ r : System . Data . L i nq . d l l
/ r : System . Depl oyment . d l l
/ r : System . Dev i ce . d l l
/ r : System . D i rect o ryServi ces . d l l
/ r : System . d l l
/ r : System . D rawi ng . d l l
/ r : System . Enterp r i seServ i ces . d l l
/ r : System . Management . d l l
Компоновка типов в модуль 39
/ r : System . Messagi ng . d l l
/ r : System . Runt i me . Remot i ng . d l l
/ r : System . Runt i me . Se r i a l i za t i on . d l l
/ r : System . Runt i me . Se r i a l i za t i on . Formatters . So a p . d l l
/ r : System . Secu r i ty . d l l
/ r : System . Servi ceModel . d l l
/ r : System . Servi ceProcess . d l l
/ r : System . Transact i ons . d l l
/ r : System . Web . Servi ces . d l l
/ r : System . Wi ndows . Fo rms . D l l
/ r : System . Xml . d l l
/ r : System . Xml . L i nq . d l l
В глобальном файле C SC . rsp есть ссылки на все перечисленные сборки,
поэтому нет необходимости указывать их явно с помощью параметра / reference.
Этот файл параметров исключительно удобен для разработчиков, так как позволяет
использовать все типы и пространства имен, определенные в различных
опубликованных компанией M icrosoft сборках, не указывая их все явно с применением
параметра / reference.
Ссылки на все эти сборки могут немного замедлить работу компилятора, но
если в исходном коде нет ссылок на типы или члены этих сборок, это никак не
сказывается ни на результирующем файле сборки, ни на производительности
его выполнения.
П Р И М ЕЧАНИ Е
При использовании параметра /refereпce для ссылки н а какую-либо сборку
можно указать пол н ы й путь к конкретному файлу. Однако если такой путь
не указать, компилятор будет искать нужный файл в следующих местах (в
указанном порядке) :
Рабоч и й каталог.
Каталог, содержащий файл с а м ого комп илятора (CSC . exe) . Библ иотека
MSCorlib.dll всегда извлекается из этого каталога. Путь к нему и меет п р и мерно
следующий вид:
%SystemRoot %\Mi c rosoft . N ET\ F ramewo r k \ v4 . 0 . #####.
Все каталоги, указанные с использованием параметра /lib ком пилятора.
Все каталоги, указанные в переменной окружения LIB.
40 Глава 2. Компоновка, упаковка, развертывание и администрирование
Конечно, вы вправе добавлять собственные параметры в глобальный файл
CSC . rsp, но это сильно усложняет репликацию среды компоновки на разных
машинах - приходится помнить про обновление файла CSC . rsp на всех машинах,
используемых для сборки приложений. Можно также дать компилятору
команду игнорировать как локальный, так и глобальный файлы CSC. rsp, указав
в командной строке параметр / noconfi g .
Несколько слов о метадан н ых
Что же именно находится в файле Program .exe? Управляемый РЕ-файл состоит
из 4-х частей: заголовка РЕ32( + ), заголовка C LR, метаданных и кода на промежуточном
языке ( intermediate language, I L). Заголовок РЕ32( +) хранит стандартную
информацию, ожидаемую Windows. Заголовок CLR - это небольшой
блок информации, специфичной для модулей, требующих CLR (управляемых
модулей). В него входит старший и младший номера версии C LR, для которой
скомпонован модуль, ряд флагов и маркер Met hodDef (о нем - чуть позже),
указывающий метод точки входа в модуль, если это исполняемый CUI- или
G UI -файл, а также необязательную сигнатуру строгого имени (она рассмотрена
в главе 3 ) . Наконец, заголовок содержит размер и смещение некоторых таблиц
метаданных, расположенных в модуле. Для того чтобы узнать точный формат
заголовка C LR, изучите структуру I MAGE_COR20_HEADER, определенную в файле
CorHdr. h .
М етаданные - это блок двоичных данных, состоящий и з нескольких таблиц.
Существуют три категории таблиц: определений, ссылок и манифестов.
В табл. 2 . 1 приводится описание некоторых наиболее распространенных таблиц
определений, существующих в блоке метаданных модуля.
Таблица 2.1 . Общие табл и цы определ е н и й , входя щих в метаданные
И мя таблицы Описание
определений
ModuleDef Всегда содержит одну запись, идентифицирующую модуль. Запись
включает имя файла моду ля с расширением (без указания пути к
файлу) и идентификатор версии модуля (в виде созданного компи-
лятором значения GUID). Это позволяет переименовывать файл,
не теряя сведений о его исходном имени. Однако настоятельно
рекомендуется не переименовывать файл, иначе среда CLR может
не найти сборку во время выполнения
TypeDef Содержит по одной записи для каждого типа, определенного в мо-
дуле. Каждая запись включает имя типа, базовый тип, флаги сборки
(public, private и т. д.) и указывает на записи таблиц MethodDef,
PropertyDef и EventDef, содержащие соответственно сведения о ме-
тодах, свойствах и событиях этого типа
Имя таблицы
определений
MethodDef
FieldDef
ParamDef
PropertyDef
EventDef
Нескол ько слов о метаданных 4 1
Описание
Содержит по одной записи для каждого метода, определенного
в модуле. Каждая строка включает имя метода, флаги (private,
puЬlic, viгtual, abstract, static, final и т. д.), сигнатуру и смещение
в модуле, по которому находится соответствующий IL-код. Каждая
запись также может ссылаться на запись в таблице ParamDef, где
хранятся.дополнительные сведения о параметрах метода
Содержит по одной записи для каждого поля, определенного в мо-
дуле. Каждая запись состоит из флагов (например, pгivate, public
и т. д.) и типа поля
Содержит по одной записи для каждого параметра, определенного
в модуле. Каждая запись состоит из флагов (in, out, retval и т. д.),
типа и имени
Содержит по одной записи для каждоrо свойства, определенного
в модуле. Каждая запись включает имя, флаrи, тип и вспомоrатель-
ное поле (оно может быть пустым)
Содержит по одной записи для каждоrо события, определенного
в моду л е. Каждая запись включает имя и флаги
Для каждой сущности, определяемой в компилируемом исходном тексте,
компилятор генерирует строку в одной из таблиц, перечисленных в табл. 2 . 1 .
В ходе компиляции исходного текста компилятор также обнаруживает типы,
поля, методы, свойства и события, на которые имеются ссылки в исходном
тексте. Все сведения о найденных сущностях регистрируются в нескольких
таблицах ссылок, составляющих метаданные. В табл. 2 .2 показаны некоторые
наиболее распространенные таблицы ссылок, которые входят в состав метаданных.
Таблица 2.2. Общие таблицы ссылок, входящие в метадан н ы е
Имя таблицы Описание
ссылок
AssemЬlyRef Содержит по одной записи для каждой сборки, на которую ссы-
лается модуль. Каждая запись включает сведения, необходимые
для привязки к сборке: ее имя (без указания расширения и пути),
номер версии, региональные стандарты и маркер открытого ключа
(обычно это небольшой хэш, созданный на основе открытого ключа
издателя и идентифицирующий издателя сборки, на которую ссыла-
ется модуль).
Каждая запись также содержит несколько флагов и хэш. Этот
хэш служит контрольной суммой битов, составляющих сборку, на
которую ссылается код. Среда CLR полностью игнорирует этот хэш
и, вероятно, будет игнорировать его в будущем
продолжение .Р
42 Глава 2. Компоновка , упаковка, развертывание и адм и н истрирование
Таблица 2.2 (продолжение)
И м ята блицы Описание
ссылок
ModuleRef Содержит по одной записи для каждого РЕ-модуля, реализующего
типы, на которые он ссылается. Каждая запись включает имя файла
сборки и его расширение (без указания пути). Эта таблица служит
для привязки моду ля вызывающей сборки к типам, реализованным
в других модулях
TypeRef Содержит по одной записи для каждого типа, на который ссылается
модуль. Каждая запись включает имя типа и ссылку, по которой
можно его найти.
Если этот тип реализован внутри другого типа, запись содержит ссыл-
ку на соответствующую запись таблицы TypeRef. Если тип реализован
в том же модуле, приводится ссылка на запись таблицы ModuleDef.
Если тип реализован в другом модуле вызывающей сборки, приво-
дится ссылка на запись таблицы ModuleRef. Если тип реализован
в другой сборке, приводится ссылка на запись в таблице AssemЬlyRef
MemberRef Содержит по одной записи для каждого члена (поля, метода, а так-
же свойства или метода события), на который ссылается модуль.
Каждая запись включает имя и сигнатуру члена и указывает на
запись таблицы TypeRef, содержащую сведения о типе, определяю-
щим этот член
На самом деле таблиц метаданных намного больше, чем показано в табл. 2 . 1
и 2 . 2 , я просто хотел создать у вас представление о б информации, на основании
которой компилятор создает метаданные. Ранее уже упоминалось о том, что
в состав метаданных входят также таблицы манифестов. О них мы поговорим
чуть позже.
Метаданные управляемого РЕ-файла можно изучать при помощи различных
инструментов. Лично я предпочитаю I LDas m.exe - дизассемблер языка I L. Для
того чтобы увидеть содержимое таблиц метаданных, выполните следующую
команду:
I LDasm Program . exe
Запустится файл I LDasm .exe и загрузится сборка Prog ram .exe. Для того
чтобы вывести метаданные в читабельном виде, выберите в меню команду
View 􀗒 Metalпfo 􀂘 Sh ow! ( или нажмите клавиши Ctri+M). В результате появится
следующая информация:
ScopeName : Program . exe
MV I D : { CA73FFE 8 - 0 D42 - 46 1 0 - A803 - 92761 95C35AA }
G l oba l funct i ons
G l oba l fi e l d s
Несколько слов о метадан ных 43
G l oba l MemberRefs
TypeDef #1 ( 02 0 0 0 0 0 2 )
TypDefName : P rogram ( 02 0 0 0 0 0 2 )
F l ags : [ PuЬl i c ] [Autolayout ] [ C l a s s ] [ Sea l ed ] [Ant i C l a s s ]
[ Befo reFi e l d l n i t ] ( 0 0 1 0 0 1 0 1 )
Extends : 0 1 0 0 0 0 0 1 [ TypeRef] System . Obj ect
Method #1 ( 0600000 1 ) [ E NTRYPO I NT ]
Met hodName : Ma i n ( 0600000 1 )
F l ags [ P uЬl i c ] [Stat i c ] [ H i deByS i g ] [ ReuseS l ot ] ( 0 0 0 0 0096 )
RVA Ох00002050
Impl F l ags [ I L J [Managed ] ( 00 0 0 0 0 0 0 )
C a l l Cnvntn : [ D E FAULTJ
Ret u rnType : Voi d
No a rgument s .
Method #2 ( 06 0 0 0 0 0 2 )
MethodName : . ct o r ( 0600000 2 )
F l ags [ PuЬl i c ] [ H i deByS i g ] [ ReuseS l ot] [ Spec i a l Name]
[ RTSpec i a l Name] [ . ct o r ] ( 0 0 0 0 1886 )
RVA Ох00002 05с
I mp l F l ags
C a l l Cnvntn :
hasTh i s
[ I L J [Managed] ( 0 0 0 0 0 00 0 )
[ D E FAULTJ
Ret u rnType : Voi d
No a rgument s .
TypeRef #1 ( 0 1 0 0 0 0 0 1 )
Token : Ох0 1 0 0 0 0 0 1
Reso l ut i onScope : Ох230 0 0 0 0 1
TypeRefName : System . Obj ect
MemberRef #1 ( 0а 0 0 0004 )
Membe r : ( 0а 0 0 0 0 04 ) . ct o r :
C a l l Cnvntn : [ D E FAULTJ
hasTh i s
Ret u rnType : Voi d
No a rgument s .
TypeRef #2 ( 0 1 0 0 0 0 0 2 )
Token :
Reso l uti onScope :
Ох0 1 0 0 0 0 0 2
Ох23 0 0 0 0 0 1
'
продолжение .Р
44 Глава 2. Компоновка , упаковка, развертывание и администрирование
TypeRefName : System . Runt i me . Compi l e rServi ces . Compi l at i onRel a xati onsAtt ri bute
Membe rRef #1 ( 0а 0 0 0 0 0 1 )
Membe r : ( 0а 0 0 0 0 0 1 ) . ct o r :
C a l l Cnvntn : [ DE FAULTJ
h a s Th i s
Ret u rnType : Voi d
1 Arguments
Argument #1 : 1 4
TypeRef #3 ( 0 1 0 0 0 003 )
Token : Ох0 1 0 0 0 0 0 3
Res o l ut i onScope : Ох23 0 0 0 0 0 1
TypeRefName : Syst em . Runt i me . Comp i l e rServ i ces .
Runti meCompat i b i l i tyAtt r i bute
MemberRef #1 ( 0а 0 0 0 0 0 2 )
Membe r : ( 0а 0 0 0 0 0 2 ) . ct o r :
C a l l Cnvntn : [ DE FAULTJ
h a s Th i s
Ret u rnType : Voi d
No a rgument s .
TypeRef #4 ( 0 1 0 0 0004 )
Token : О х 0 1 0 0 0004
Reso l u t i onScope : Ох230 0 0 0 0 1
TypeRefNa me : System . Conso l e
Membe rRef #1 ( 0а 0 0 0 0 0 3 )
Membe r : ( 0а 0 0 0 0 0 3 ) W r i tel i ne :
C a l l Cnvntn : [ D E FAULTJ
Ret u rnType : Voi d
1 Arguments
Argument #1 : St r i ng
AssemЬ l y
Token : Ох20 0 0 0 0 0 1
Name : P rogram
PuЬl i c Кеу :
H a s h Al g o r i thm : Ох00008004
V e rs i on : 0 . 0 . 0 . 0
Maj o r V e rs i on : О хО О О О О О О О
M i n o r V e rs i on : О хО О О О О О О О
B u i l d N umbe r : О хО О О О О О О О
Rev i s i on N umbe r : О хО О О О О О О О
Loca l e : <nul l >
F l ags : [ nопе] ( 0 0 0 0 0 0 0 0 )
CustomAttri bute #1 ( 0с 0 0 0 0 0 1 )
CustomAtt ri bute Туре : О а О О О О О 1
CustomAtt r i buteName :
Несколько слов о метада нных 45
System . Runt i me . Compi l e rServ i ces . Compi l a t i onRel axati onsAtt r i bute · ·
i nstance v o i d . ct o r ( i nt32 )
Lengt h : 8
V a l ue : 0 1 0 0 08 0 0 0 0 00 0 0 0 0 > <
ctor a rg s : ( 8 )
CustomAtt ri bute #2 ( 0с 0 0 0 00 2 )
CustomAttri bute Туре : О а О О О О О 2
CustomAtt r i buteName : System . Runt i me . Compi l e rServ i ces .
Runti meCompat i bi l i tyAtt ri bute · ·
i nstance voi d . cto r ( )
Lengt h : 3 0
Va l ue : 0 1 0 0 0 1 0 0 5 4 02 1 6 57 7 2 6 1 7 0 4е бf бе 45 7 8 > Т WrapNonEx<
: 63 65 7 0 74 69 бf 6е 54 68 72 бf 77 73 0 1 >cept i onTh rows <
ctor а rgs : ( )
As semЬ l yRef #1 ( 23 0 0 0 0 0 1 )
Token : Ох2300 0 0 0 1
PuЬl i c Кеу o r Token : Ь7 7 а 5 с 56 1 9 3 4 еО 8 9
Name : mscorl i b
Vers i on : 2 . 0 . 0 . 0
Ma j o r Vers i on : ОхООООООО2
Mi nor Vers i on : ОхОООООООО
Bui l d Numbe r : ОхО О О О О О О О
Revi s i on Numbe r : ОхО О О О О О О О
Loca l e : <nul l >
HashVa l ue B l ob :
F l ags : [ nопе] ( 00 0 0 0 0 0 0 )
User St ri ngs
7 0 0 0 0 0 0 1 : ( 2) L " H i "
Coff symbol name overhea d : О
К счастью, I L Dasm самостоятельно обрабатывает таблицы метаданных
и комбинирует информацию, поэтому пользователю не приходится заниматься
46 Глава 2. Компоновка , упаковка, разверты вание и администрирование
синтаксическим разбором необработанных табличных данных. Например,
в приведеином фрагменте видно, что, показывая строку таблицы TypeDef,
ILDasm выводит перед первой записью таблицы TypeRef определение соответствующего
члена.
Не обязательно понимать, что означает каждая строка этого дампа, важно
запомнить, что Prog ram .exe содержит в таблице TypeDef описание типа P rogram.
Этот тип идентифицирует открытый изолированный класс, производный от
Syst em . Object (то есть это ссылка на тип из другой сборки). Тип P rog ram также
определяет два метода: Ma i n и . ct o r (конструктор) .
Метод Ma i n - это статический открытый метод, чей программный код представлен
на языке IL (а не в машинных кодах процессора, например х86). Ma i n
возвращает тип voi d и принимает единственный аргумент a rgs - массив значений
типа St r i ng. Метод-конструктор (всегда отображаемый под именем . ctor)
является открытым, его код также записан на языке IL. Тип возвращаемого
значения конструктора - voi d, у него нет аргументов, но есть указатель t h i s ,
ссылающийся н а область памяти, в которой должен создаваться экземпляр
объекта при вызове конструктора.
Я настоятельно рекомендую вам поэкспериментировать с дизассемблером
I LDasm. Он предоставляет массу сведений, и чем лучше вы в них разберетесь,
тем быстрее изучите общеязыковую исполняющую среду CLR и ее возможности.
В этой книге еще не раз будет использоваться дизассемблер ILDasm.
Ради забавы посмотрим на некоторую статистику сборки P rog ra m .exe .
Выбрав в меню программы I L Dasm команду View 􀂘 Statistics, увидим следующее:
F i l e s i ze : 3 0 7 2
Р Е h e a d e r s i z e
Р Е addi t i ona l i n fo
N um . of РЕ sect i ons
CLR header s i ze
C L R meta - d a t a s i ze
CLR a d d i t i ona l i n fo
CLR method headers
Managed code
Data
Unaccounted
Num . of РЕ
. text
. rs rc
. rel ос
sect i ons
- 1 0 24
- 1 0 24
- 5 1 2
5 1 2 ( 496 used ) ( 1 6 . 67% )
839 ( 27 . 3 1 % )
3
72 ( 2 . 34% )
604 ( 1 9 . 66% )
о ( 0 . 0 0% )
2 ( 0 . 07 % )
18 ( 0 . 59% )
1536 ( 50 . 00% )
- 5 1 1 ( - 16 . 63% )
3
C L R metadata s i ze : 604
Mod u l e - 1 ( 10 bytes )
TypeDef - 2 ( 28 bytes ) О i nte rfaces . О expl i c i t l ayout
Объеди нение модулей для создания сборки 47
TypeRef - 4 ( 24 bytes )
Met hodDef - 2 ( 28 bytes ) О abst ract . О n a t i ve . 2 bod i es
MemberRef - 4 ( 24 bytes )
CustomAtt r i bute- 2 ( 12 bytes )
As s emЫ y - 1 ( 22 bytes )
As s emЫ yRef - 1 ( 20 bytes )
St r i ngs - 176 bytes
B l obs - 68 bytes
Us erSt r i ngs - 8 bytes
Gu i ds - 16 bytes
Uncatego ri zed - 168 bytes
CLR met hod headers : 2
Num . of met hod bod i es - 2
Num . of fat headers - О
Num . of t i ny headers - 2
Managed code : 1 8
Ave met hod s i ze - 9
Здесь можно видеть как размеры самого файла (в байтах), так и размеры его
составляющих частей (в байтах и процентах от размера файла). Приложение
P rogra m . cs очень маленькое, поэтому большая часть его файла занята заголовком
РЕ и метаданными. Фактически I L-код занимает всего 18 байт. Конечно,
чем больше размер приложения, тем чаще типы и ссылки на другие типы
и сборки используются повторно, поэтому размеры метаданных и данных заголовка
существенно уменьшаются по отношению к общему размеру файла.
П Р И М ЕЧАНИ Е
Кстати (некстати?), в I LDasm .exe есть «Жучок», искажающий отображаемую
информацию о размере файла. В ч астности , н ел ьзя доверять сведе н и я м
в строке Unaccounte d.
Объединение модулей
для создан ия сборки
Файл P rogra m . exe - это не просто РЕ-фаJ:л с метаданными, а еще и сборка
(assemЬly), то есть коллекция из одного или нескольких файлов с определениями
типов и файлов ресурсов. Один из файлов сборки выбирают для хранения
ее манифеста. Ма нифест (manifest) - это еще один набор таблиц метаданных,
которые в основном содержат имена файлов, составляющих сборку. Кроме
того, эти таблицы описывают версию и региональные стандарты сборки, ее
издателя, общедоступные экспортируемые типы, а также все составляющие
сборку файлы.
48 Глава 2. Комп оновка, упаковка, развертывание и админ истрирование
CLR работает со сборками, то есть сначала CLR всегда загружает файл
с таблицами метаданных манифеста, а затем получает из манифеста имена
остальных файлов сборки. Некоторые характеристики сборки стоит запомнить:
О в сборке определены многократно используемые типы;
О сборка помечена номером версии;
О со сборкой может быть связана информация безопасности.
У отдельных файлов сборки, кроме файла с таблицами метаданных манифеста,
таких атрибутов нет.
Чтобы упаковать типы, сделать их доступными, а также обеспечить безопасность
типов и управление их версиями, нужно поместить типы в модули,
объединенные в сборку. Чаще всего сборка состоит из одного файла, как приложение
P rogra m .exe в рассмотренном примере, но могут быть и сборки из
нескольких файлов: РЕ-файлов с метаданными и файлов ресурсов, например
GIF- или J РG-файлов. Наверное, проще представлять себе сборку как «логический
􀃘 ЕХЕ- или D LL-файл.
Уверен, многим читателям интересно, зачем компании Microsoft попадобилось
вводить новое понятие - « сборка􀃘. Дело в том, что сборка позволяет разграничить
логическое и физическое понятия многократно используемых типов.
· Допустим, сборка состоит из нескольких типов. При этом типы, применяемые
чаще всех, можно поместить в один файл, а применяемые реже - в другой.
Если сборка развертывается путем загрузки через Интернет, клиент может
вовсе не загружать файл с редко используемыми типами, если он никогда их
не задействует. Например, независимый поставщик ПО (independent software
vendor, ISV), специализирующийся на разработке элементов управления пользовательского
интерфейса, может реализовать в отдельном модуле активно
используемые типы Active Accessibllity (необходимые для соответствия требованиям
логотипа Microsoft). Загружать этот модуль достаточно лишь тем, кому
нужны специальные возможности.
Можно настроить приложение так, чтобы оно загружало файлы сборки,
определив в его конфигурационном файле элемент codeB a s e (см. подробнее
главу 3 ) . Этот элемент идентифицирует URL-aдpec, по которому можно найти
все файлы сборки. При попытке загрузить файл сборки CLR получает URL
из элемента codeB a s e и проверяет наличие нужного файла в локальном кэше
загруженных файлов. Если он там есть, то он загружается, если нет - CLR использует
для загрузки файла в кэш URL-aдpec. Если не удается найти нужный
файл, CLR генерирует исключение Fi l eNotFoundExcept i on.
У меня есть три аргумента в пользу применения многофайловых сборок.
О Можно распределять типы по нескольким файлам, допуская избирательную
загрузку необходимых файлов из Интернета, а также частично упаковывать
и развертывать типы, варьируя функциональность приложения.
О бьединение м одулей для создания сборки 49
1:1 Можно добавлять к сборке файлы с ресурсами и данными. Допустим, имеется
тип для расчета некоторой страховой суммы. Ему может потребоваться
доступ к актуарным таблицам. Вместо встраивания актуарных таблиц в исходный
текст можно включить соответствующий файл с данными в состав
сборки (например, с помощью компоновщика сборок AL.exe, который рассмотрен
далее). Можно включать в сборки данные в любом формате: в текстовом,
в виде таблиц Microsoft Excel или Microsoft Word, а также в любом
другом при условии, что приложение способно анализировать содержимое
этого файла.
1:1 Можно создавать сборки, состоящие из типов, написанных на разных языках
программирования. При компиляции исходного текста на языке С# компилятор
создает один модуль, а при компиляции исходного текста на Visual
Basic - другой. Одна часть типов может быть написана на С#, другая - на
Visual Basic, остальные - на других языках программирования. Затем при
помощи соответствующего инструмента все эти моду ли можно объединить
в одну сборку. Использующие такую сборку разработчики увидят в ней
лишь набор типов. Разработчики даже не заметят, что применялись разные
языки программирования. Кстати, при желании с помощью I LDasm .exe можно
получить файлы с исходным текстом всех модулей на языке I L. После
этого можно запустить утилиту I LAsm .exe и передать ей полученные файлы,
и утилита выдаст файл, содержащий все типы. Для этого компилятор исходного
текста должен генерировать только I L- код, поэтому эту методику
нельзя использовать с Visual С++.
В Н И МАН И Е
Подводя итог, можно сказать, что сборка - это единица многократного использования,
управления версиями и безопасности типов. Она позволяет
распределять типы и ресурсы по отдельным файлам, чтобы ее пользователи
могли решить, какие файлы упаковывать и развертывать вместе. Загрузив
файл с манифестом, среда C L R может определить, какие файлы сборки содержат
типы и ресурсы, на которые ссылается п р иложение. Л юбому потребителю
сборки достаточн о знать лишь имя файла, содержащего манифест,
после чего он сможет, не нарушая работы приложения, абстрагироваться
от особенностей расп ределения содержимого сборки по файлам, которое
со временем может меняться .
При работе со многими типами, совместно использующими одну версию
и набор параметров безопасности, по соображениям производительности
рекомендуется размещать все типы в одном файле, не распределяя их по нескольким
файлам, не говоря уже о разных сборках. На загрузку каждого файла
или сборки CLR и Windows тратят значительное время: на поиск сборки, ее
загрузку и инициализацию. Чем меньше файлов и сборок, тем быстрее загрузка,
потому уменьшение числа сборок способствует сокращению рабочего пространства
и степени фрагментации адресного пространства процесса. Ну, и наконец,
5О Глава 2 . Компоновка, упаковка, разверты вание и администрирование
n G e n .exe лучше оптимизирует код, если обрабатываемые файлы больше по
размеру.
Чтобы скомпоновать сборку, нужно выбрать один из РЕ-файлов, который
станет хранителем манифеста. Можно также создать отдельный РЕ-файл, в котором
не б у дет ничего, кроме манифеста. В табл. 2.3 перечислены таблицы метаданных
манифеста, наличие которых превращает управляемый моду ль в сборку.
Таблица 2.3. Табл и ца м етада н н ы х м а н ифеста
Имя табл ицы мета- Описание
данных манифеста
AssemЬlyDef Состоит из единственной записи, если модуль идентифицирует
сборку. Запись включает имя сборки (без расширения и пути),
сведения о версии (старший и младший номера версии, номер
компоновки и редакции), региональные стандарты, флаги, хэш-
алгоритм и открытый ключ издателя (это поле может быть
пустым - null)
FileDef Содержит по одной записи для каждого РЕ-файла и файла ре-
сурсов, входящих в состав сборки. В каждой записи содержит-
ся имя и расширение файла (без указания пути), хэш и флаги.
Если сборка состоит из одного файла, таблица FileDef пуста
ManifestResourceDef Содержит по одной записи для каждого ресурса, включенного
в сборку. Каждая запись включает имя ресурса, флаги (puЬlic
или private), а также индекс для таблицы FileDef, указываю-
щий файл или поток с ресурсом. Если ресурс не является
отдельным файлом (например, JPEG- или GIF-файлом), он
хранится в виде потока в составе РЕ-файла. В случае встроен-
наго ресурса запись также содержит смещение, указывающее
начало потока ресурса в РЕ-файле
ExportedTypesDef Содержит записи для всех открытых типов, экспортируемых
всеми РЕ-модулями сборки. В каждой записи указано имя типа,
индекс для таблицы FileDef (указывающий файл сборки, в кота-
ром реализован этот тип), а также индекс для таблицы TypeDef
Манифест позволяет потребителям сборки абстрагироваться от особенностей
распределения ее содержимого и делает сборку самоописываемой.
Обратите внимание, что в файле, который содержит манифест, находится также
информация о том, какие файлы составляют сборку, но отдельные файлы 4Не
знают�>, что они включены в сборку.
П Р И М ЕЧАНИ Е
Файл сборки , содержащий манифест, содержит также табл ицу AssemЫyRef.
В ней хранятся записи с описанием всех сборок, на которые ссылаются
файлы д а н н о й сборки . Это позволяет и н струмента м , отк р ы в ман ифест
сборки, сразу увидеть весь набор сборок, на которые ссылается эта сборка,
не открывая другие файлы сборки. И в этом случае дан ные AssemЫyRef
п ризваны сделать сборку самоописываемой.
Объеди нение модулей для создания сборки 5 1
Компилятор С# создает сборку, если указан любой из параметров командной
строки - / t [ a rget] : exe, / t [ a rget ] : wi nexe или / t [ a rget ] : 1 i b r a ry. Каждый из этих
параметров заставляет компилятор генерировать единый РЕ-файл с таблицами
метаданных манифеста. В итоге генерируется соответственно консольное приложение,
приложение с графическим интерфейсом или DLL-файл.
Кроме этих параметров компилятор С# поддерживает параметр / t [ a rget ] :
modu 1 e , который заставляет компилятор создать Р Е - файл без таблиц метаданных.
При использовании этого параметра всегда получается D LL-файл
в формате РЕ. Для того чтобы получить доступ к типам такого файла, его необходимо
поместить в сборку. При указании параметра / t : mod u 1 e компилятор
С# по умолчанию присваивает выходному файлу расширение . ne tm o d u l e.
В Н И МА Н И Е
К сожалению, в и нтегрированной среде разработки ( l nteg rated Development
Environment, IDE) Microsoft Visual Studio нет встроенной nоддержки создания
многофайловых сборок - для этого nриходится исnол ьзовать инструменты
командной строки.
Существует несколько способов добавления модуля в сборку. Если РЕ-файл
с манифестом собирается при помощи компилятора С#, можно применять параметр
1 addmodu 1 е. Для того чтобы понять, как создают многофайловые сборки,
рассмотрим пример. Допустим, есть два файла с исходным текстом:
О файл RUT. c s содержит редко используемые типы;
О файл FUT.cs содержит часто используемые типы.
Скомпилируем редко используемые типы в отдельный модуль, чтобы
пользователи сборки могли отказаться от развертывания этого модуля, если
содержащиеся в нем типы им не нужны:
csc / t : modu 1 e RUT . cs
Команда заставляет компилятор С# создать файл RUТ. netmod u l e , который
представляет собой стандартную РЕ-библиотеку D LL, но среда CLR не сможет
просто загрузить ее.
Теперь скомпилируем в отдельном модуле часто используемые типы и сделаем
его хранителем манифеста сборки, так как к расположенным в нем типам
обращаются довольно часто. Фактически теперь этот моду ль представляет
собой целую сборку, поэтому я изменил имя выходного файла с FUT. d l l на
J eftтypes.dll:
csc /out : J effTypes . d 1 1 / t : 1 i b ra ry / a ddmodu 1 e : RUT . netmod u1 e FUT . cs
Эта команда приказывает компилятору С# при компиляции файла FUT. c s
создать файл Jeftтype s . d l l . Поскольку указан параметр /t : 1 i bra ry, результирующий
РЕ-файл DLL с таблицами метаданных манифеста называется J eftтyp e s . d l l .
52 Глава 2 . Ком поновка, упаковка, развертыван ие и адм ин истрирование
Параметр 1 a d dm o d u l е : RUT . n etmo d u l е указывает компилятору, что файл RUT .
netmod u l e должен быть частью сборки. В частности, параметр / a ddmodu l e заставляет
компилятор добавить к таблице Fi l eDef в метаданных манифеста
сведения об этом файле, а также занести в таблицу Expo rtedTypesDef сведения
об открытых экспортируемых типах этого файла.
Завершив работу, компилятор создаст несколько файлов (рис. 2 . 1 ) . Модуль
справа содержит манифест.
RUT.netmodule JelfТypes.dll
IL-код. сrенермрованныil IL-код. сгенермрованныll
npll IIOMПIUIRЦIIII RUТ.cs Пpll IIOMПIIIIRЦIIII fUТ.CS
Метаданнь1е Метаданные
Ти n ы , методы и другие сущности , Тиn ы , методы и другие сущности ,
оnределенные е RUT.cs оnределенные в FUT.cs
Тиn ы , методы и други е сущности,
на которые ссылается R UT.cs
Ти n ы , мвтоды и другие сущности , 1 на которые ссылается FUT.cs
Манифест
Файлы сборки
(JеНТуреs dll и RUT netmodule)
Открытые файлы сборки
(JeНТypes.dll и R UT. netmodule)
Рис . 2 . 1 . Многофайловая сборка из двух управляемых модулей и манифеста
Файл RUT. пet m od u le содержит I L-код, сгенерированный при компиляции
R UT.cs. Кроме того, этот файл содержит таблицы метаданных, описывающие
типы, методы, поля, свойства, события и т. п., определенные в RUТ.cs, а также
типы, методы и др., на которые ссылается RUT. cs. Jeftтypes.dll - это отдельный
файл. Подобно RUT. net m od u le, он включает I L-код, сгенерированный при компиляции
FUT.cs, а также аналогичные метаданные в виде таблиц определений
и ссылок. Однако Jeftтype s . d l l содержит дополнительные таблицы метаданных,
которые и делают его сборкой. Эти дополнительные таблицы описывают
все файлы , составляющие сборку ( сам файл Jeftтype s . d l l и RUT. n etm odu le).
Таблицы метаданных манифеста также включают описание всех открытых
типов, экспортируемых файлами Jeftтypes . d l l и RUT. пetm od u l e.
П Р И М ЕЧАН И Е
Н а самом деле в таблицах метаданных манифеста н е описаны типы, экспортируемые
РЕ-файлом, в котором находится манифест. Цель этой оптимизации -
уменьшить число байт, необходимое для хранения данных манифеста в РЕфайле.
Таким образом, утверждения вроде «таблицы метаданных манифеста
включают все открытые типы, экспортируемые JeffТypes.dll и RUT.netmodule»
верны лишь отчасти. Однако это утверждение абсолютно точно отражает логический
набор экспортируемых типов.
Объеди нение м одулей для создания сборки 53
Скомпоновав сборку JeffТypes . d l l , можно изучить ее таблицы метаданных
манифеста при помощи I LDasm .exe, чтобы убедиться, что файл сборки действительно
содержит ссылки на типы из файла RUT. netm o d u l e . Если скомпоновать
этот проект и затем проанализировать его метаданные при помощи
утилиты I LDas m . exe, в выводимой информации вы увидите таблицы Fi 1 eDef
и Expo rtedTypesDef. Они выглядят следующим образом:
Fi 1 e #1 ( 26 0 0 0 0 0 1 )
с2
Token : Ох2600 0 0 0 1
Name : RUT . netmodu 1 e
HashVa 1 ue В 1 оЬ : е6 е6 d f 62 2с а 1 2с 59 97 65 O f 2 1 44 1 0 1 5 96 f2 ?е db
F1 ags : [Cont a i nsMetaDat a ] ( 0 0 0 0 0 0 0 0 )
Expo rtedType #1 ( 27 0 0 0 00 1 )
Token : Ох27 0 0 0 0 0 1
Name : ARa re1 yUsedType
Imp1 ementat i on token : Ох2600 0 0 0 1
TypeDef token : Ох02000002
F 1 ags : [ PuЬl i c ] [Autolayout ] [ C 1 a s s ] [Sea 1 ed ] [Ans i C 1 a s s ]
[ BeforeFi e 1 d i n i t ] ( 0 0 1 0 0 10 1 )
Из этих сведений видно, что RUT.netmodule - это файл, который считается
частью сборки с маркером Ох2600 0 0 0 1 . Таблица ExportedType показывает наличие
открытого экспортируемого типа ARa re 1 yUsedType. Этот тип помечен .маркером
реализации (implementation token) Ох2600 0 0 0 1 , означающим, что I L-код этого
типа находится в файле RUT. netmodule.
П Р И М ЕЧАНИ Е
Для л ю б о п ы т н ы х : р а з м е р м а р ке р о в м етада н н ы х - 4 б а й т а . Ста р ш и й
байт указывает т и п маркера (Ox01 =Type Ref, Ox02=TypeDef, Ox26=FileRef,
Ox27=ExportedType). П ол н ы й список типов маркеров см. в перечисл и м о м
т и п е CorTokenType в заголовочном файле CorHdr.h из . N ET Framework S D K .
Тр и младш их байта маркера п росто идентиф ицируют запись в соответствующей
табли це метаданных. Например, маркер реал изации Ох26000001
ссылается на первую строку табли цы FileRef (нумерация строк нач и н ается
с 1 , а не с 0). Кстати, в TypeDef нумерация строк нач и н ается с 2 .
Любой клиентский код, использующий типы сборки J effТypes . d l l , должен
компоноваться с указанием параметра компилятора / r [ e fe re n c e ] : J e ffТype s .
d 1 1 , который заставляет компилятор загрузить сборку J effТy p es . d l l и все
файлы, перечисленные в ее таблице F i 1 eDef. Компилятору необходимо, чтобы
все файлы сборки были установлены и доступны. Если бы мы удалили файл
54 Глава 2. Компон овка, упаковка, развертывание и адм ин истрирование
R UТ. п et m o d u l e , компилятор С# сгенерировал бы следующее сообщение об
ошибке:
fat a l e r r o r CS0009 : Metadata fi l e ' C : \JeffTypes . d l l ' cou l d not Ье opened- ' E rror
i mport i ng modu l e ' rut . netmodu l e ' o f a s s emЬ l y ' C : \JeffTypes . d l l '- The system
ca nnot fi nd t h e fi l e spec i fi ed
Эrо означает, что при компоновке новой сборки должны присутствовать все
файлы, на которые она ссылается.
Во время исполнения клиентский код вызывает разные методы. При первом
вызове некоторого метода среда CLR определяет, на какие типы он ссылается
как на параметр, возвращаемое значение или локальную переменную. Далее
CLR пытается загрузить из сборки, на которую ссылается код, файл с манифестом.
Если этот файл описывает типы, к которым обращается вызванный
метод, срабатывают внутренние механизмы C LR, и нужные типы становятся
доступными. Если в манифесте указано, что нужный тип находится в другом
файле, CLR загружает этот файл, и внутренние механизмы CLR обеспечивают
доступ к данному типу. CLR загружает файл сборки только при вызове метода,
ссылающегося на расположенный в этом файле тип. Это значит, что наличие
всех файлов сборки, на которую ссылается приложение, для его работы не
обязательно.
Добавление сборок в проект в среде Visual Studio
Если проект создается в среде Visual Studio, необходимо добавить в проект все
сборки, на которые он ссылается. Для этого откройте окно S o lutioп Explorer,
щелкните правой кнопкой мыши на проекте, на который нужно добавить
ссылку, и выберите команду Add Reference. Откроется диалоговое окно Add
Refere n c e (рис. 2 . 2 ) .
Для того чтобы добавить в проект ссылки на сборку, выберите е е в списке.
Если в списке нет нужной сборки, то для того чтобы ее найти (файл с манифестом),
щелкните на кнопке B rowse. Вкладка СОМ в диалоговом окне Add
Refere n c e позволяет получить доступ к неуправляемому С О М - серверу из
управляемого кода через класс-представитель, автоматически генерируемый
Visual Studio. Вкладка Projects служит для добавления в текущий проект ссылки
на сборки, созданные в другом проекте этого же решения.
Чтобы сборки отображались в списке на вкладке . N ET, добавьте в реестр
подраздел:
HKEY_LOCAL_MACH I N E \ SOFTWAR E \ M i c rosoft \ . N ETFramewo r k \As s emЬ l yFol ders\Myli bName
Myl i bName - это созданное разработчиком уникальное имя, Visual Studio его
не отображает. Создав такой подраздел, измените его строкавое значение по
умолчанию, чтобы оно указывало на каталог, в котором хранятся файлы сборок
(например, C:\ Program Files\MyLib Path).
Обьединение модулей для создания сборки 55
Add Roftttrc􀫯
NЕТ l:So\1 1 ProJЮ� Вrows-;f R«tnt1
Componcnt NAmt VtfSIOП \S,.sttш 48.0 0
S,.sttrn.Actrvrtlts н о о
Svstttll дctrvrtlts ( ort Prts н о о
Svsttrn A􀃉trntrts Dщ JЬitlп н о о
W sttm A(trvrt1ts Prtstrltatr но о
Sy sttrll.Addlн н о о
I Systtm.Addln C o 111r1ct н о о
􀃊sttrn.C ornpontпtModtl н о о
􀃊sttm Co11frguratron н о о
Systtm С onfrgurnron .I.1stJII н о о
􀃋sttrп Cort н о о 1 Systtn1.Do�to� н о о
Systtm Dиа D1.t1􀃌xttnst 4 0 0 0
Runtrme
v4 О lO!ll
.􀀠 о 20921
.􀀠 о 20921
.􀀠 О 20􀫭21
.􀃋 о 20􀫮21
.􀃋 о 20q21
.􀀓 о 20921
•4 о 20921
.􀀓 о 20921
.􀀠 о 20921
.􀀓 о 20921
.􀀓 о 2092&
.􀀓 о 20921
Poth
( . Progrom F olts (oolt) Rt ..
( Po ogшn f olts (Jd6) Rt
С Рооgш>1 f olts (Jd6) R t
( P o ogo•m f olts (><16) Rt
( P r o g r " rп F rlts {xi6) Rt
( .Progr anl F rlts (xS6) R t
С P r o g r • m f olts (Jd6) Rt
( Poogrom f olts (Jd6) R t .
( ·\Pr ogrom f olts (Jd6)· Rt.
С Progo•m f olt> (Jd6) R t
( Рооgо ооп f olts (Jd6) Rt
( Рооgо ооп f olts (Jd6) R t
С · Progr•m f olts (оо!6) R t -
Ok 1 1 Canctl
Рис. 2 . 2 . Диалоговое окно Add Reference в Visual Studio
Использование утилиты AssemЫy Lin ker
Вместо компилятора С# для создания сборки можно задействовать компоновщик
сборок (assemЬly linker) AL.exe. Эта утилита оказывается кстати, если
нужно создавать сборки из модулей, скомпонованных разными компиляторами
(если компилятор языка не поддерживает параметр, эквивалентный параметру
/ a ddmod u l e из С#), а также в случае, когда требования к упаковке сборки на
момент компоновки просто не известны. Утилита AL.exe пригодна и для компоновки
сборок, состоящих исключительно из ресурсов ( или сопутствующих
сборок - к ним мы еще вернемся), которые обычно используются для локализации
ПО.
Утилита A L . exe может генерировать файлы формата ЕХЕ или D L L Р Е ,
которые н е содержат ничего, кроме манифеста, описывающего типы и з других
модулей. Чтобы понять, как работает AL.exe, скомпонуем сборку JeffТy p e s . d l l
по-другому:
csc / t : modu l e RUT . cs
csc / t : modu l e FUT . cs
a l /out : J effТypes . d l l / t : l i b ra ry FUT . netmod u l e RUT . netmod u l e
Файлы, генерируемые в результате исполнения этих команд, показаны на
рис. 2.3.
В этом примере два из трех отдельных модулей, R UT. netmod u l e и FUT.
netm o d u l e , сборками не являются (так как не содержат таблиц метаданных
5 6 Глава 2 . Компоновка , упаковка , развертывание и администрирование
манифеста) . Третий же - J eftтypes.dl l - это небольшая библиотека РЕ DLL
( поскольку она скомпонована с параметром / t [ a rg et ] : 1 i b r a ry ) , в которой
нет I L - кода, а только таблицы метаданных манифеста, указывающие, что
файлы J eftтypes.d l l, RUT. net mod u le и FUT. netmodu le входят в состав сборки.
Результирующая сборка состоит из трех файлов: Jeftтypes. d l l , RUT.netmod u le
и FUT. netmod ul e, так как компоновщик сборок не «умеет>> объединять несколько
файлов в один.
RUT.netmodule RUТ.netmodule
IL-код. сгенерированный
при комnиляции RUT.cs IL-код. сгенерированный при компиляции FUТ.cs 1
Метаданнь1е Метаданные
Типы, методы и другие сущности, Типы, методы и другие сущности,
определенные е RUT.cs определенные е FUT.cs
Типы, методы и другие сущности , Типы, методы и другие сущности,
на которые ссылается RUT.cs на которые ссылается FUT.cs
JefПypes.dll
1 IL -код отсутствует 1
Метаданные
(табл ицы ссылок
и оп ределений отсутствуют)
Манифест Файлы сборки (JeffТypes . d l l ,
R UT. netmodule и FUT.пetmodule)
Открытые типы сборки
(RUT. пetmodule и FUT.пetmodule)
Рис. 2 . 3 . М но гофайловая сборка из трех у правляемых модулей
и манифеста
Утилита A L . exe может генерировать консольные РЕ -файлы и РЕ-файлы
с графическим интерфейсом ( с помощью параметра 1 t [ а r g e t J : е х е или
/ t [ a rget ] : wi nexe). Однако это довольно необычно, поскольку означает, что
будет сгенерирован исполняемый РЕ-файл, содержащий не больше IL-кода,
чем нужно для вызова метода из другого моду ля. Можно указать, какой метод
должен использоваться в качестве входной точки, задав при вызове компоновщика
сборок параметр командной строки / ma i n. Приведем пример вызова
AL.exe с этим параметром:
csc / t : mo d u l e App . cs al / out : App . exe / t : exe /ma i n : P rogram . Ma i n app . netmod u l e
Первая строка компонует App.cs в модуль, а вторая генерирует небольшой
РЕ-файл Арр.ехе с таблицами метаданных манифеста. В нем также находится
О бьединение модулей для создания сборки 57
небольшал глобальная функция, сгенерированная AL.exe благодаря параметру
/ma i n: App . Ma i n. Эта функция, _Ent ryP o i nt, содержит следующий I L-код:
. method p r i vatescope stat i c voi d Ent ryPo i nt$ PST060 0 0 0 0 1 ( ) c i l mana ged
{
. entrypoi nt
11 Code s i ze 8 ( 0х8 )
. maxstack 8
I L 0 0 0 0 : ta i l .
I L 0002 : c a l l voi d [ . modu l e ' P rogram . netmodu l e ' ] P rogram : : Ma i n ( )
I L 0007 : ret
11 end of met hod ' Gl oba l Funct i ons ' : : EntryPo i nt
Как видите, этот код просто вызывает метод Mai n , содержащийся в типе
P rogram, который определен в файле App. netmodule. Параметр / ma i n , указанный
при вызове AL.exe, здесь не слишком полезен, так как вряд ли вы когда-либо
будете создавать приложение, у которого точка входа расположена не в РЕфайле
с таблицами метаданных манифеста. Здесь этот параметр упомянут лишь
для того, чтобы вы знали о его существовании.
В программнам коде для данной книги имеется файл Ch02- 3 - B u i l d M ultiFi l e Library.
bat, в котором инкапсулированы последовательно все шаги, показывающие,
как создать многофайловую сборку. Ch02-4-AppUsin g M u lti Filelibrary project
в Visual Studio выполняет данный файл на этапе предварительной сборки. Вы
можете изучить этот пример, чтобы понять, как интегрировать многофайловую
сборку из Visual Studio.
Включение в сборку файлов ресурсов
Если сборка создается при помощи AL.exe, параметр / embed [ resource] позволяет
добавить в сборку файлы ресурсов (файлы в формате, отличном от РЕ).
Параметр принимает любой файл и включает его содержимое в результирующий
РЕ-файл. Таблица M a n i fes tResou rceDef в манифесте обновляется, отражая
наличие нового ресурса.
Утилита A L . exe поддерживает также параметр / l i n k [ r e s o u r c e ] , который
принимает файл с ресурсами. Однако параметр только обновляет таблицы
манифеста Ma n i festResou rceDef и F i l eDef сведениями о ресурсе и о том, в каком
файле сборки он находится. Сам файл с ресурсами не внедряется в РЕ-файл
сборки, а хранится отдельно и подлежит упаковке и развертыванию вместе
с остальными файлами сборки.
Подобно AL.exe, CSC.exe позволяет объединять ресурсы со сборкой, генерируемой
компилятором С#. Параметр 1 re source компилятора С# включает
указанный файл с ресурсами в результирующий РЕ-файл сборки и обновляет
таблицу Ma n i fes t ResourceDef. Параметр компилятора / l i n k resource добавляет
в таблицы Ma n i festResou rceDef и Fi l eDef записи со ссылкой на отдельный файл
с ресурсами.
58 Глава 2 . Ком поновка, упаковка, разверты вание и адм и н истрирование
И последнее: в сборку можно включить стандартные ресурсы Win32. Это
легко сделать, указав при вызове AL.exe или CSC .exe путь к RЕS-файлу и параметр
/wi n32res. Кроме того, можно легко включить стандартный ресурс значка
Win32 в файл сборки, указав при вызове AL.exe или CSC.exe путь к !СО-файлу
и параметр /wi n32i con. В Visual Studio файл ресурсов добавляют в сборку на
вкладке A p p l icatio п в диалоговом окне свойств проекта. Обычно значки включают,
чтобы Проводник Windows (Windows Explorer) мог отображать значок
для управляемого исполняемого файла.
П Р И М ЕЧАН И Е
В файлах управляемой сборки содержится также файл манифеста Wiп32. По
умолчанию компилятор С # автоматически создает файл манифеста, однако
ему можно запретить это делать п р и помощи параметра jnowin32man ifest.
П рограмм н ы й код манифеста, генери руемого ком п илятором С# по умолчанию
, выглядит следующим образо м:
1 1 Со з д а н и е и и н и ц и ал и з а ц и я масс и в а St r i ng
<?xml vers i on=" l . O " encod i ng= " UTF - 8 " standa l one= "yes " ? >
<assemЬ l y xml ns= " u r n : schema s -mi c rosoft - com : a sm . v l " ma n i festVe r s i on= " l . O " >
<a s s emЬ l y i denti ty vers i on= " l . O . O . O " name= " MyAppl i ca t i on . a p p " / >
<t rust i n fo xml n s= " u r n : schema s -mi crosoft - com : a sm . v 2 " >
<sec u r i ty>
<requested P r i v i l eges xml n s= " u r n : s chema s -mi crosoft - com : asm . vЗ " >
<requestedExec u t i onlevel l evel = " a s i n vok e r " u i Acces s= " fa l s e " />
</ requested P r i v i l eges>
< / s e c u r i ty>
< / t r u s t i n fo>
</ a s s emЬl у>
Ресурсы со сведениями
о верси и сборки
Когда утилита A L. exe или CSC.exe генерирует сборку в виде РЕ-файла, она
также включает в этот файл стандартную Win32-версию. Пользователи могут
увидеть версию, просматривая свойства файла. Для получения этой информации
из программы служит статический метод Get V e r s i o n i nfo типа System .
D i agnosti c s . F i l eVers i on i nfo. На рис. 2.4 показава вкладка Detai l s диалогового
окна свойств файла J effТypes . d l l .
При компоновке сборки следует задавать значения полей версии в исходном
тексте программы с помощью специализированных атрибутов, применяемых на
уровне сборки. Вот как выглядит код, генерирующий информацию о версии,
показанную на рис. 2.4.
Ресурсы со сведениями о версии сборки 59
ГG.;Jtfoi;,yfТ}p •s.dll Proptrtlts S"""'ity J D etool• \ Ptoмous 1 VoraionO]
Ptoperty Vlii.Je
o􀫬sc rlpttan
F/e desщJt100 J eii Т ypes dll
Т уре Applocot100 e><tensюn
F/e VOI$100 1 о о о
PtodJct nате Wrotelect (R ) J ell's Туре libцwy
ProdJct VOISIOO 2 О О О
Сс.руrф COI>\IIф (cJ Wrntelect 􀁔 1 0
S rze 4 5О КВ
D ote mod/red 1 0/1 8/2009 1 26 РМ
l􀆎 L􀆎 Neutrol
L􀗑 trodemorks J e/IТypes rs • regrste�ec 􀁕odemork ol
Orrgrnol l/ename J e/IТ ypes cl
ок 1 [ Crrcel
Рис. 2 . 4 . В кладка Detai ls диалогового окна свойств файла Jeftтypes.dll
u s i ng System . Ref1 ect i on :
1 1 З а д а т ь з н а ч е н и я п ол я F i 1 eDesc r i pt i on :
[ a s semЬ l y : As s emЬ l yTi t 1 е ( OO JeffТypes . d 1 1 00 ) J
1 1 За д а т ь з н а ч е н и я п ол я Comments :
[ a s semЬ l y : As s emЬ l yDesc ri pt i on ( OO Th i s a s s emЬ l y cont a i n s Jeff ' s types oo ) J
1 1 За д а т ь з н а ч е н и я п ол я CompanyName :
[ a s s emЬ l y : As semЫ yCompa ny ( oo w ; nte 1 1 ect 00 ) ]
1 1 З а д а т ь з н а ч е н и я пол я P roductName :
[ a s s emЬ l y : As semЬ l y P roduct ( OO W i nte1 1 ect ( R ) Jeff ' s Туре L i b r a ry oo ) J
1 1 За д а т ь з н а ч е н и я поля Lega 1 Copy r i ght :
[ a s semЬ l y : As semЬ l yCopy r i ght ( OO Copy r i ght ( с ) Wi nte1 1 ect 2 0 1 0 00 ) ]
1 1 За д а т ь з н а ч е н и я п ол я Lega 1 Tra dema r k s :
[ a s semЬ l y : As s emЬl yTradema rk ( OO J effТypes i s а regi ste red t r a dema rk o f
Wi n t e1 1 ect oo ) J
продолже11.ие .Р
60 Глава 2 . Компоновка, упаковка, развертывание и администрирование
11 Зада т ь з на ч ен и я п ол я As s emЫ yVers i on :
[ a s s emЫ y : As s emЫ yVe r s i о п ( " З . О . О . О " ) J
1 1 З а д а т ь з н а ч е н и я п ол я Fi l eVers i on :
[ a s s emЫ y : As s emЫ yFi l eVe r s i оп ( " 1 . О . О . О " ) J
1 1 З а д а т ь з н а ч е н и я поля P roductVe r s i on :
[ a s s emb l y : As s emЬ l y i nfo rmati ona l Versi on ( " 2 . О . О . О " ) J
1 1 Зада т ь з н а ч е н и я п ол я ( по дробнее с м . р а з дел " Ре г ионал ь н ые ста н дарты " )
[ a s s emЫ y : As s emЫyCu l t u re C " " ) J
В Н И МАНИ Е
К сожалению, в диалоговом окне свойств П роводни ка Windows отсутствуют
поля для некоторых атр ибутов. В частности , было бы замечательно, есл и
бы был отоб ражен атр ибут AssemЫyVersion, потому что среда CLR использует
знач е н и е этого атр и бута при з агрузке сборки (об этом рассказано
в главе 3).
В табл. 2 .4 перечислены поля ресурса со сведениями о версии и соответствующие
им атрибуты, определяемые пользователем. Если сборка компонуется
утилитой AL.exe, сведения о версии можно задать, применяя параметры
командной строки вместо атрибутов. Во втором столбце табл. 2.4 показавы
параметры командной строки для каждого поля ресурса со сведениями о версии.
Обратите внимание на отсутствие аналогичных параметров у компилятора
С#; поэтому сведения о версии обычно задают, применяя специализированные
атрибуты.
Таблица 2.4. Поля ресурса со сведе н и я м и о в е р с и и и соответствующие
им параметры AL.exe и пользовател ьские атрибуты
Поnе ресурса Параметр AL.exe Атрибут/комментарий
со сведениями
о версии
FILEVERSION jfileversion Systern.Reflection.
Assern Ьlу File VersionAttribute
PRODUCTVERSION jproductversion Systern. Reflection.AssernЬly Inforrnational-
VersionAttribute
FILEFLAGSMASK Нет Всегда задается равным VS_FFI_
FILEFLAGSMASK (определяется
в Win Ver.h как ОхООООООЗF)
FILEFLAGS Нет Всегда равен О
FILEOS Нет в настоящее время всегда равен vos_
WINDOWS32
Поnе ресурса
со сведениями
о версии
FILEТYPE
FILESUBTYPE
AssemЬlyVersion
Comments
CompanyName
FileDescription
FileVersion
Interna!Name
Lega!Copyright
Lega!Trademarks
Origina!Filename
PrivateBuild
ProductName
ProductVersion
Specia!Вuild
В Н И МАН И Е
Ресурсы с о сведениями о версии сборки 61
Параметр AL.exe Атрибут /комментари й
jtarget Задается равным VFT АРР, если задан
параметр jtarget:exe или jtarget:winexe.
При наличии параметра /target:library
приравнивается VFT DLL
Нет Всегда задается равным VFT2_
UNKNOWN (это поле не имеет значения
для VFT_APP и VFT_DLL)
jversion System.Reflection.
AssemЬlyVersionAttribute
jdescription System. Reflection.
AssemЬly DescriptionAttribute
jcompany System. Reflection.
AssemЬlyCompany Attribute
jtitle System. Reflection.AssemЬlyTitleAttribute
jversion System.Reflection.
AssemЬlyVersionAttribute
jout Задается равным заданному имени
выходного файла (без расширения)
jcopyright System.Reflection.
AssemblyCopyrightAttribute
jtrademark System. Reflection.
AssemЬlyTrademarkAttribute
jout Задается равным заданному имени
выходного файла (без пути)
Нет Всегда остается пустым
/product System. Reflection.
AssemЬlyProductAttribute
jproductversion System.Reflection.AssemЬly Inf ormational-
VersionAttribute
Нет Всегда остается пустым
При создании нового проекта С# в Visual Studio файл Assem Ыylпfo.cs генерируется
автоматически . Он содержит все атрибуты сборки, описанные в
этом разделе, а также нескол ько дополн ительных - о них речь идет в главе
3. Можно п росто открыть файл AssemЫyl пfo.cs и изменить относящиеся
к кон кретно й сборке сведе н и я . Visual Studio также п редоставляет диа логовое
окно для редактирования информации о версии сборки, которое
представлено на рис. 2 . 5 .
62 Глава 2 . Компоновка , упаковка, развертывание и администрирование
Qoscroptoon.
􀀺ompony. Wtnt􀁉lt􀁉ct
froduct W>nttlltct (R) Jtfl's Ty pt l •b• •ry
C opyroght (<) W1r1ttlltct 1 0 1 0
JtffТy pts rs 1 rtgtSH'ftd tr 1dtr111rl of W!l\ttllt<t
AsstmЬiy YflSMП: .)
ftlt vtrs1on:
!jUJD, dtOIISH- 7tb6 - Hf!- bd•d - S J 13f6t l c H9
/!ltutr•l longuogt· (Nont)
lrJ M•k• шtrФiy СОМ-VшЫt
ОК j j Conctl j
Рис . 2 . 5 . Диалоговое окно с информацией о сборке в Visual Studio
Номера версии
Ранее было показано, что сборка может идентифицироваться п о номеру версии.
У частей этого номера одинаковый формат: каждая состоит из 4 частей, разделенных
точками (табл. 2.5).
Таблица 2.5. Ф о р м ат н о м е р о в версии
Старший Младший Номер Номер
номер номер компоновки редакции
Пример 2 5 7 1 9 2
В табл. 2.5 показан пример номера версии 2.5.7 1 9.2. Первые две цифры составляют
то, что обычно понимают под номером версии: пользователи будут
считать номером версии 2.5. Третье число, 7 1 9, указывает номер компоновки.
Если в вашей компании сборка компонуется каждый день, увеличивать этот
номер надо ежедневно. Последнее число 2 - номер редакции сборки. Если
в компании сборка компонуется дважды в день (скажем, после исправления
критической и обязательной к немедленному исправлению ошибки, тормозившей
всю работу над проектом), надо увеличивать номер редакции.
Такая схема нумерации версий принята в компании Microsoft, и я настоятельно
рекомендую ей следовать. В следующих версиях CLR предполагается
обеспечить более совершенную поддержку механизма загрузки новых версий
сборок и отката к предыдущим версиям сборки, если новая несовместима с
имеющимся приложением. В рамках механизма поддержки версий среда CLR
будет ожидать, что у версии сборки, в которой устранены определенные неполадки,
будут те же старший и младший номера версий, а номера компоновки
Регионал ьные стандарты 63
и редакции будут указывать на служебuую версию (servicing version) с исправлениями.
При загрузке сборки CLR будет автоматически искать самую
последнюю служебную версию (с теми же старшим и младшим номерами
версий) нужной сборки.
Со сборкой ассоциированы три номера версии. Это очень неудачное решение
стало источником серьезной путаницы. Попробую объяснить, для чего нужен
каждый из этих номеров и как его правильно использовать.
О AssemЬlyFile Version - этот номер версии хранится в ресурсе версии Win32
и предназначен лишь для информации, C L R его полностью игнорирует.
Обычно устанавливают старший и младший номера версии, определяющие
отображаемый номер версии. Далее при каждой компоновке увеличивают
номер компоновки и редакции. В идеале инструмент от компании Microsoft
(например, CSC.exe или A L. exe) должен автоматически обновлять номера
компоновки и редакции (в зависимости от даты и времени на момент
компоновки), но этого не происходит. Этот номер версии отображается
Проводником Windows и служит для определения точного времени компоновки
сборки.
О AssemЬlylnformationaiVersion - этот номер версии также хранится в ресурсе
версии Win32 и, как и предыдущий, предназначен лишь для информации;
для CLR он абсолютно безразличен. Этот номер служит для указания версии
продукта, в который входит сборка. Например, продукт версии 2.0 может состоять
из нескольких сборок. Одна из них может отмечаться как версия 1 .0,
если это новая сборка, не входившая в комплект поставки продукта версии
1 .0. Обычно отображаемый номер версии формируется при помощи старшего
и младшего номеров версии. Затем номера компоновки и редакции
увеличивают при каждой упаковке всех сборок готового продукта.
О AssemЬlyVersion - этот номер версии хранится в манифесте, в таблице
метаданных Assemb l yDef. CLR использует этот номер версии для привязки
к сборкам, имеющим строгие имена (о них рассказано в главе 3). Этот номер
версии чрезвычайно важен и уникально идентифицирует сборку. Начиная
разработку сборки, следует задать старший и младший номера версии, а также
номера компоновки и редакции; не меняйте их, пока не будете готовы
начать работу над следующей версией сборки, пригодной для развертывания.
При создании сборки, ссылающейся на другую, этот номер версии
включается в нее в виде записи таблицы As semЬ l yRef. Это значит, что сборка
жестко привязана к конкретной версии.
Регионал ьные стандарты
Помимо номера версии, сборки идентифицируют реzиоuальuым.и стаuдартам.и
( culture ). Например, одна сборка может бьrrь исключительно на немецком языке,
64 Глава 2. Компоновка, упаковка, развертывание и адми нистрирование
другая - на швейцарском варианте немецкого, третья - на американском английском
и т. д. Региональные стандарты идентифицируются строкой, содержащей
основной и вспомогательный теги (как описано в RFC 1 766). Несколько
примеров приведено в табл. 2.6.
Таблица 2.6. П римеры тегов, оп ределяющих р е г и о н ал ь н ы е
стандарты сборки
Основной тег Вспомогательный тег Региональные стандарты
de Нет Немецкий
de АТ Австрийский немецкий
de с н lllвейцарский немецкий
еп Нет Английский
еп GB Английский
еп us Английский
В общем случае сборкам с кодом не назначают региональные стандарты,
так как код обычно не содержит зависящих от них встроенных параметров.
Сборку, для которой не определен региональный стандарт, называют сборкой
с нейтральны.м.и региональны.м.и стандартами ( culture neutгal) .
П р и создании приложения, ресурсы которого привязаны к региональным
стандартам, компания Microsoft настоятельно рекомендует объединять программный
ресурс и ресурсы приложеимя по умолчанию в одной сборке и не
назначать ей региональных стандартов при компоновке. Другие сборки будут
ссылаться на нее при создании и работе с типами, которые она предоставляет
для общего доступа.
После этого можно создать одну или несколько отдельных сборок, содержащих
только ресурсы, зависящие от региональных стандартов, и никакого
программнаго кода. Сборки, помеченные для применеимя в определенных
региональных стандартах, называют сопутствующими (satellite assemЬlies ) .
Региональные стандарты, назначенные такой сборке, в точности отражают
региональные стандарты размещенного в ней ресурса. Следует создавать отдельные
сборки для каждого регионального стандарта, который планируется
поддерживать.
Обычно сопутствующие сборки компонуются при помощи утилиты AL.exe.
Не стоит использовать для этого компилятор - ведь в сопутствующей сборке
не должно быть программнога кода. Применяя утилиту AL.exe, можно задать
желаемые региональные стандарты параметром / c [ u l t u re ] : text, где text - это
строка ( например, en - US , представляющая американский вариант английского
языка). При развертывании сопутствующие сборки следует помещать в подкаталог,
имя которого совпадает с текстовой строкой, идентифицирующей региональные
стандарты. Например, если базовым каталогом приложеимя является
С : \ М уАрр, сопутствующая сборка для американского варианта английского
Развертывание простых приложе н и й 6 5
языка должна быть в каталоге C:\MyApp\en-US. Во время выполнения доступ
к ресурсам сопутствующей сборки осуществляют через класс System . Resou rces .
Resou rceMa nager.
П Р И М ЕЧАН И Е
Хотя это и н е реко м ендуетс я , можно создавать сопутствующие сборки
с п рограм м н ы м кодо м . П р и желании в место параметра fcu lt u re утил иты
AL.exe региональный стандарт можно указать в атрибуте Syste m . Reflection.
AssemЫyCulture, оп ределяемом пользователем, например, следующим образом:
1 1 На з н а ч и т ь д л я сборки ре г ио н а л ь н ы й с т а ндарт Swi s s German
[ a s s emЫ y : AssemЫ yCul ture ( " d e - C H " ) J
Лучше не создавать сборки, ссылающиеся на сопутствующие сборки.
Другими словами, все записи таблицы AssemЫ yRef должны ссылаться на сборки
с нейтральными региональными стандартами. Если необходимо получить
доступ к типам или членам, расположенным в сопутствующей сборке, следует
воспользоваться методом отражения (см. главу 23).
Разверты ван ие п ростых п риложен ии
(закрытое разверты ван ие сборок)
Ранее в этой главе было показано, как компоновать модули и объединять их
в сборки. Теперь можно приступить к изложению того, как упаковывать и развертывать
сборки, чтобы пользователь мог работать с приложением.
Особых средств для упаковки сборки не требуется. Легче всего упаковать
набор сборок, просто скопировав все их файлы. Например, можно поместить
все файлы сборки на комлакт-диск и передать их пользователю вместе с программой
установки, написанной в виде пакетного файла. Такая программа просто
копирует файлы с комлакт-диска в каталог на жестком диске пользователя.
Поскольку сборка включает все ссылки и типы, определяющие ее работу, ему
достаточно запустить приложение, а CLR найдет в каталоге приложения все
сборки, на которые ссылается данная сборка. Так что для работы приложения
не нужно модифицировать реестр, а чтобы удалить приложение, достаточно
просто удалить его файлы - и все !
Конечно, можно применять для упаковки и установки сборок другие механизмы,
например САВ-файлы (они обычно используются в сценариях с загрузкой
из Интернета для сжатия файлов и сокращения времени загрузки).
Можно также упаковать файлы сборки в MSI - файл, предназначенный для
службы установщика Windows (Windows Installer), MSI Exec.exe. MSI позволяет
установить сборку по требованию при первой попытке CLR ее загрузить.
66 Глава 2 . Компо новка, упаковка, развертывание и администрирование
Эта функция не нова для службы MSI, она также поддерживает аналогичную
функцию для неуправляемых ЕХЕ- и DLL-файлов.
П Р И М ЕЧ А Н И Е
Пакетн ы й файл или подобная простая «установочная программа» скопирует
приложение на машину пользователя, однако для создания ярлыков на
рабочем столе, в меню Пуск (Start) и на панели быстрого запуска понадоб
ится п рограмма посложнее. Кроме того, ско п и ровать, восстановить или
переместить п риложение с одн ой машины на другую легко, но ссылки и
я рл ы ки потребуют специального обращения.
Естественно, в Visual Studio есть встроенные механизмы, которые можно
задействовать для публикации приложений, - это делается на вкладке PuЫish
страницы свойств проекта. Можно использовать ее, чтобы заставить Visual
Studio создать М S I-файл и скопировать его на веб-сайт, FТР-сайт или в заданную
папку на диске. МSI-файл также может установить все необходимые
компоненты, такие как .NET Framework или Microsoft S QL Server 2005 Express
Edition. Наконец, приложение может автоматически проверять наличие обновлений
и устанавливать их на пользовательской машине посредством технологии
ClickOnce.
Сборки, развертываемые в том же каталоге, что и приложение, называют
сборками с закрытым развертыванием (privately deployed assemЬlies ), так как
файлы сборки не используются совместно другими Приложениями (если только
другие приложения не развертывают в том же каталоге). Сборки с закрытым
развертыванием - серьезное преимущества для разработчиков, конечных
пользователей и администраторов, поскольку достаточно скопировать такие
сборки в базовый каталог приложения, и CLR сможет загрузить и исполнить
содержащийся в них код. Кроме того, легко удалить приложение, просто удалив
сборки из его каталога. Также легко создавать резервные копии подобных
сборок и восстанавливать их.
Несложный сценарий установкиjперемещенияjудаления приложения стал
возможным благодаря наличию в каждой сборке метаданных. Метаданные указывают,
какую сборку, на которую они ссылаются, нужно загрузить - для этого
не нужны параметры реестра. Кроме того, область видимости сборки охватывает
все типы. Это значит, что приложение всегда привязывается именно к тому типу,
с которым оно было скомпоновано и протестировано. CLR не может загрузить
другую сборку просто потому, что она предоставляет тип с тем же именем. Этим
CLR отличается от С О М , где типы регистрируются в системном реестре, что
делает их доступными любому приложению, работающему на машине.
В г лаве 3 рассказано о развертывании совместно используемых сборок, доступных
нескольким приложениям.
П ростое средство администрирования (конфи гураци о н н ы й файл) 6 7
П ростое средство адм и н истр и рования
(конфигурационн ый файл)
Пользователи и администраторы лучше всех могут определять разные аспекты
работы приложения. Например, администратор может решить переместять
файлы сборки на жесткий диск пользователя или заменить данные в манифесте
сборки. Есть и другие сценарии управления версиями и удаленного администрирования,
о некоторых из них рассказано в г лаве 3.
Для того чтобы предоставить администратору контроль над приложением,
можно разместить в каталоге приложения конфигурационный файл. Его может
создать и упаковать издатель приложения, после чего программа установки запишет
конфигурационный файл в базовый каталог приложения. Кроме того,
администратор или конечный пользователь машины может сам создать или
модифицировать этот файл. CLR интерпретирует его содержимое для изменения
политики поиска и загрузки файлов сборки.
Конфигурационные файлы содержат ХМL-теги и могут быть ассоциированы
с приложеннем или с компьютером. И спользование отдельного файла
(вместо параметров, хранимых в реестре) позволяет легко создать резервную
копию файла, а администратору - без труда копировать файлы с машины на
машину: достаточно скопировать нужные файлы - в результате будет также
скопирована административная политика.
В главе 3 такой конфигурационный файл рассматривается подробно, а пока
вкратце обсудим его. Допустим, издатель хочет развернуть приложение вместе
с файлами сборки J e ffТype s , но в отдельном каталоге. Желаемая структура
каталогов с файлами выглядит следующим образом:
Ка тало г AppDi r ( содержи т фа йлы сборки приложен и я )
Арр . ехе
App . exe . confi g ( обсуждается н иже )
Под катало г AuxF i l es ( содержи т файлы сборки JeffТypes )
JeffТypes . d l l
FUT . netmod u l e
RUT . netmodu l e
Поскольку файлы сборки JeffТypes более не находятся в базовом каталоге
приложения, CLR не сможет найти и загрузить их, и при запуске приложения
будет сгенерировано исключение System . I O . Fi l eNot FoundExcepti on. Чтобы
избежать этого, издатель создает конфигурационный файл в формате X M L
и размещает его в базовом каталоге приложения. И м я этого файла должно совпадать
с именем главного файла сборки и иметь расширение confi g , в данном
68 Глава 2. Ком поновка, упаковка, развертывание и адми н истрирование
случае - App.exe.confi g. Содержимое этого конфигурационного файла должно
выглядеть примерно следующим образом:
<confi g u r a t i on>
<runti me>
<a s s emb l yBi ndi ng xml n s= " u rn : s chema s -mi c ro s o ft - com : as m . vl " >
<probi ng p r i vatePath= " AuxFi l es " / >
< / a s s emЬl yBi ndi ng>
</ runti me>
</confi g u r a t i on>
Пытаясь найти файл сборки, CLR всегда сначала ищет в каталоге приложения,
и если поиск заканчивается неудачей, продолжает искать в подкаталоге
AuxFiles. В атрибуте pri vatePath элемента, направляющего поиск, можно указать
несколько путей, разделенных точкой с запятой. Считается, что все пути заданы
относительно базового каталога приложения. Идея здесь в том, что приложение
может управлять своим каталогом и его подкаталогами, но не может управлять
другими каталогами.
Алгоритм поиска файлов сборки
В поиске сборки среда CLR просматривает несколько подкаталогов. Порядок
при поиске сборки с нейтральными региональными стандартами таков (при
условии, что параметры fi rstPri vatePath и secondPri vatePath определены в атрибуте
p r i vatePath конфигурационного файла):
AppDi r \ AsmName . d l l AppDi r \ AsmName\AsmName . d l l AppDi r \ f i rstPri vatePath\
AsmName . dl l AppDi r \ fi rstPri v atePath\AsmName\AsmName . d l l AppDi r\
s econd P r i v atePath\AsmName . d l l AppDi r \ secondPri vatePath\AsmName\
AsmN ame . d l l
В этом примере конфигурационный файл вовсе не понадобится, если файлы
сборки J effТypes развернуты в подкаталоге JeffТypes, так как CLR автоматически
проверлет подкаталог, имя которого совпадает с именем искомой сборки.
Если ни в одном из упомянутых каталогов сборка не найдена, CLR начинает
поиск заново, но теперь ищет файл с расширением ЕХЕ вместо DLL.
Если и на этот раз поиск оканчивается неудачей, генерируется исключение
F i l eNotFound Except i on.
В отношении сопутствующих сборок действуют те же правила поиска за
одним исключением: ожидается, что сборка находится в подкаталоге базового
каталога приложения, имя которого совпадает с наз:ванием региональных
стандартов. Например, если для файла Asm N a m e . d l l назначен региональный
стандарт 4en- U S • , порядок просмотра каталогов таков:
C : \AppD i r \ e n - US\AsmName . d l l
C : \ AppD i r \ e n - US\AsmName\AsmName . d l l
Простое средство администрирования (конфи гурационный файл) 69
C : \AppDi r \ fi rst P r i vatePa t h \ en - US\AsmName . d l l
C : \AppDi r \ f i rstPri vatePa t h \ e n - US\AsmName\AsmName . d l l
C : \AppD i r\ second P r i vatePat h \ en - US\AsmName . dl l
C : \AppD i r\ second P r i vatePat h \ en - US\AsmName\AsmName . d l l
C : \AppDi r \ e n - US\AsmName . exe
C : \AppDi r\en - US\AsmName\AsmName . exe
C : \AppDi r \ fi rstPri vatePa t h \ e n - US\AsmName . exe
C : \AppDi r \ fi rstPri vatePa t h \ e n - US\AsmName\AsmName . exe
C : \AppDi r\ second P r i vatePa t h \ en - US\AsmName . exe
C : \AppDi r\ second P r i vatePat h \ e n - US \AsmName\AsmName . exe
C : \AppD i r\en \AsmName . d l l
C : \AppD i r\en\AsmName\AsmName . d l l
C : \AppDi r\fi rstPri vatePath\en \AsmName . d l l
C : \AppDi r \ fi rstPri vatePat h\en\AsmName\AsmName . d l l
C : \AppD i r \ s econd P r i vatePath\en\AsmName . d l l
C : \AppD i r\ second P r i vatePa th\en\AsmName\AsmName . d l l
C : \AppDi r \ en \AsmName . exe
C : \AppDi r \en\AsmName\AsmName . exe
C : \AppDi r \ fi rstPri vatePat h \ en \ AsmName . exe
C : \AppDi r \ fi rstPri vatePat h\en\AsmName\AsmName . exe
C : \AppDi r \ s econd P r i vatePath\ en \AsmName . exe
C : \AppD i r\ second P r i vatePath\en\AsmName\AsmName . exe
Как видите, CLR ищет файлы с расширением ЕХЕ или DLL. Поскольку поиск
может занимать значительное время (особенно когда CLR пытается найти
файлы в сети) , в конфигурационном Х М L-файле можно указать один или
несколько элементов региональных стандартов, чтобы ограничить круг проверяемых
каталогов при поиске сопутствующих сборок.
Имя и расположение конфигурационного ХМL-файла может различаться
в зависимости от типа приложения.
D Для исполняемых приложений ( Е Х Е ) конфигурационный файл должен
располагаться в базовом каталоге приложения. У него должно быть то же
имя, что и у ЕХЕ-файла, но с расширением config.
D Для приложений A S P.NET Web Foгm конфигурационный файл всегда
должен находиться в виртуальном корневом каталоге веб- приложения
и называться Web.config. Кроме того, в каждом вложенном каталоге может
быть собственный файл Web.config с унаследованными параметрами конфигурации.
Например, веб-приложение, расположенное по адресу http://
www.Wi ntellect.comjТra i n i n g , будет использовать параметры из файлов Web .
confi g, расположенных в виртуальном корневом каталоге и в подкаталоге
Trai n i n g .
Как уже было сказано, параметры конфигурации применяют к конкретному приложению
и конкретному компьютеру. При установке платформа .NET Framework
создает файл Mach i n e . co n f i g . Существует по одному файлу M a ch i n e . co n f i g
70 Глава 2 . Компоновка , упаковка, развертывание и администрирование
на каждую версию среды C LR, установленную на данной машине. Файл
Machi п e .c o пfig расположен в следующем каталоге:
%SystemRoot % \ M i c rosoft . N ET\ F ramewo rk \ вepcия \ CON F I G
Естественно, % Sy s t emRoot% - это каталог, в котором установлена система
Windows (обычно C:\Windows), а версия - номер версии, идентифицирующий
определенную версию платформы .NET Framework (например, v4.0. #####).
Параметры файла Machine .config заменяют параметры конфигурационного
файла конкретного приложения. Администраторам и пользователям следует
избегать модификации файла M ac h i n e.coп fig, поскольку в нем хранятся многие
параметры, связанные с самыми разными аспектами работы системы, что
серьезно затрудняет ориентацию в его содержимом. Кроме того, требуется
резервное копирование и восстановление конфигурационных параметров приложения,
что возможно лишь при использовании конфигурационных файлов,
специфичных для приложения.


Глава 3 . Совместно
испол ьзуе м ы е сборки
и сбор ки со строг и м и ме н ем
В