Для получения этой информации из программы служит статический метод GetVersionInfo типа System.Diagnostics.FileVersioninfo. На рис. 2.4 показана вкладка Details диалогового окна свойств файла JeffТypes.dll.
При компоновке сборки следует задавать значения полей версии в исходном тексте программы с помощью специализированных атрибутов, применяемых на уровне сборки. Вот как выглядит код, генерирующий информацию о версии, показанную на рис. 2.4.
Рис. 2.4. Вкладка Details диалогового окна свойств файла Jeftтypes.dll
{codecitation class="brush: csharp; gutter: true;" width="100%" }using System.Reflection:
// Задатьзначенияполя FileDescription:
[assembly: AssemblyTitlе(“JeffТypes.dll”)]
/// Задатьзначенияполя Comments:
[assembly: AssemblyDescription(“This assembly contains Jeff's types”)]
// Задатьзначенияполя CompanyName:
[assembly: AssemblyCompany(“Wintellect”)]
// Задатьзначенияполя ProductName:
[assembly: AssemblyProduct(“Wintellect (R) Jeff 's Туре Library”)]
// Задатьзначенияполя LegalCopyright:
[assembly: AssemblyCopyright(“Copyright (с) Wintellect 2010”)]
// Задатьзначенияполя Lega1Trademarks:
[assembly :AssemblyTrademark (“JeffТypes i s а registered trademark of Wintellect”)]
// Задатьзначенияполя AssemblyVersion:
[assemblly: AssemblyVersiоп( "3 0.0.0") ]
// Задатьзначенияполя FileVersion:
[assembly: AssemblFileVersiоn(“1.0.0.0")]
// Задатьзначенияполя ProductVersion:
[assembly: AssemblyinformationalVersion( "2.0.0.0")]
// Задать значения поля (подробнее см. раздел "Региональные стандарты")
[assembly :AssemblyCulture( "")]
{/codecitation}
К сожалению, в диалоговом окне свойств Проводника Windows отсутствуют поля для некоторых атрибутов. В частности, было бы замечательно, если бы был отображен атрибут AssemblyVersion, потому что среда CLR использует значение этого атрибута при загрузке сборки (об этом рассказано в главе 3).
В табл. 2.4 перечислены поля ресурса со сведениями о версии и соответствующие им атрибуты, определяемые пользователем. Если сборка компонуется утилитой AL.exe, сведения о версии можно задать, применяя параметры командной строки вместо атрибутов. Во втором столбце табл. 2.4 показаны параметры командной строки для каждого поля ресурса со сведениями о вер сии. Обратите внимание на отсутствие аналогичных параметров у компилятора С#; поэтому сведения о версии обычно задают, применяя специализированные атрибуты.
Таблица 2.4. Поля ресурса со сведениями о версии и соответствующие им параметры AL.exe и пользовательские атрибуты
Поле со сведениями о версии ресурса
|
Параметр AL.exe |
Атрибут/комментарий |
FILEVERSION |
/fileversion |
System.Reflection.AssemblуFileVersionAttribute
|
PRODUCTVERSION |
/productversion |
Systern.Reflection.AssernblyInforrnationalVersionAttribute
|
FILEFLAGSMASK |
Нет |
Всегда задается равным VS_FFI_FILEFLAGSMASK (определяется в WinVer.hкак 0x000000ЗF) |
FILEFLAGS |
Нет |
Всегда равен 0 |
FILEOS |
Нет |
В настоящее время всегда равен VOS_WINDOWS32 |
FILEТYPE |
/target |
Задается равным VFT_АРР, если задан параметр /target:exeили /target:winexe. При наличии параметра /target:library приравнивается VFT_DLL |
FILESUBTYPE |
Нет |
Всегда задается равным VFT2_ UNKNOWN (это поле не имеет значения для VFT_APP и VFT_DLL) |
AssemblyVersion |
/version |
System.Reflection.AssemblyVersionAttribute |
Comments |
/description |
System.Reflection.AssemblyDescriptionAttribute |
CompanyName |
/company |
System.Reflection.AssemblyCompanyAttribute |
FileDescription |
/title |
System.Reflection.AssemblyTitleAttribute |
FileVersion |
/version |
System.Reflection.AssemblyVersionAttribute |
InternalName |
/out |
Задается равным заданному имени выходного файла (без расширения) |
LegalCopyright |
/copyright |
System.Reflection.AssemblyCopyrightAttribute |
LegalTrademarks |
/trademark |
System.Reflection.AssemblyTrademarkAttribute |
OriginalFilename |
/out |
Задается равным заданному имени выходного файла (без пути) |
PrivateBuild |
Нет |
Всегдаостаетсяпустым |
ProductName |
/product |
System.Reflection.AssemblyProductAttribute |
ProductVersion |
/productversion |
System.Reflection.AssemblyInformationalVersionAttribute |
SpecialВuild |
Нет |
Всегдаостаетсяпустым |
При создании нового проекта С# в Visual Studio файл Assemblylпfo.cs генерируется автоматически. Он содержит все атрибуты сборки, описанные в этом разделе, а также несколько дополнительных-о них речь идет в главе 3. Можно просто открыть файл Assemblylпfo.cs и изменить относящиеся к конкретной сборке сведения. Visual Studio также предоставляет диалоговое окно для редактирования информации о версии сборки, которое представлено на рис. 2.5.
Рис. 2.5. Диалоговое окно с информацией о сборке в Visual Studio
Номера версии
Ранее было показано, что сборка может идентифицироваться по номеру версии. У частей этого номера одинаковый формат: каждая состоит из 4 частей, разделенных точками (табл. 2.5).
Таблица 2.5. Формат номеров версии
Старшийномер |
Младший номер |
Номер компоновки |
Номер редакции |
|
Пример |
2 |
5 |
719 |
2 |
В табл. 2.5 показан пример номера версии 2.5.719.2. Первые две цифры составляют то, что обычно понимают под номером версии: пользователи будут считать номером версии 2.5. Третье число, 719, указывает номер компоновки. Если в вашей компании сборка компонуется каждый день, увеличивать этот номер надо ежедневно. Последнее число 2 - номер редакции сборки. Если в компании сборка компонуется дважды в день (скажем, после исправления критической и обязательной к немедленному исправлению ошибки, тормозившей всю работу над проектом), надо увеличивать номер редакции.
Такая схема нумерации версий принята в компании Microsoft, и я настоятельно рекомендую ей следовать. В следующих версиях CLR предполагается обеспечить более совершенную поддержку механизма загрузки новых версий сборок и отката к предыдущим версиям сборки, если новая несовместима с имеющимся приложением. В рамках механизма поддержки версий среда CLR будет ожидать, что у версии сборки, в которой устранены определенные неполадки, будут те же старший и младший номера версий, а номера компоновки и редакции будут указывать на служебную версию (servicing version) с исправлениями. При загрузке сборки CLR будет автоматически искать самую последнюю служебную версию (с теми же старшим и младшим номерами версий) нужной сборки.
Со сборкой ассоциированы три номера версии. Это очень неудачное решение стало источником серьезной путаницы. Попробую объяснить, для чего нужен каждый из этих номеров и как его правильно использовать.
·AssemblyFile Version - этот номер версии хранится в ресурсе версии Win32 и предназначен лишь для информации, CLR его полностью игнорирует. Обычно устанавливают старший и младший номера версии, определяющие отображаемый номер версии. Далее при каждой компоновке увеличивают номер компоновки и редакции. В идеале инструмент от компании Microsoft (например, CSC.exe или AL.exe) должен автоматически обновлять номера компоновки и редакции (в зависимости от даты и времени на момент компоновки), но этого не происходит. Этот номер версии отображается Проводником Windows и служит для определения точного времени компоновки сборки.
·AssemblylnformationalVersion - этот номер версии также хранится в ресурсе версии Win32 и, как и предыдущий, предназначен лишь для информации; для CLR он абсолютно безразличен. Этот номер служит для указания версии продукта, в который входит сборка. Например, продукт версии 2.0 может со стоять из нескольких сборок. Одна из них может отмечаться как версия 1.0, если это новая сборка, не входившая в комплект поставки продукта версии 1.0. Обычно отображаемый номер версии формируется при помощи старшего и младшего номеров версии. Затем номера компоновки и редакции увеличивают при каждой упаковке всех сборок готового продукта.
·AssemblyVersion - этот номер версии хранится в манифесте, в таблице метаданных AssemblyDef. CLR использует этот номер версии для привязки к сборкам, имеющим строгие имена (о них рассказано в главе 3). Этот номер версии чрезвычайно важен и уникально идентифицирует сборку. Начиная разработку сборки, следует задать старший и младший номера версии, а так же номера компоновки и редакции; не меняйте их, пока не будете готовы начать работу над следующей версией сборки, пригодной для развертывания. При создании сборки, ссылающейся на другую, этот номер версии включается в нее в виде записи таблицы AssemblyRef. Это значит, что сборка жестко привязана к конкретной версии.
Региональные стандарты
Помимо номера версии, сборки идентифицируют региональными стандартами (culture). Например, одна сборка может бьrrь исключительно на немецком языке, другая - на швейцарском варианте немецкого, третья - на американском английском и т. д. Региональные стандарты идентифицируются строкой, содержащей основной и вспомогательный теги (как описано в RFC1766). Несколько примеров приведено в табл. 2.6.
Таблица 2.6. Примеры тегов, определяющих региональные стандарты сборки
Основной тег |
Вспомогательный тег |
Региональные стандарты |
de |
Нет |
Немецкий |
de |
AT |
Австрийский немецкий |
de |
CH |
Швейцарский немецкий |
en |
Нет |
Английский |
en |
GB |
Английский |
en |
US |
Английский |
В общем случае сборкам с кодом не назначают региональные стандарты, так как код обычно не содержит зависящих от них встроенных параметров. Сборку, для которой не определен региональный стандарт, называют сборкой с нейтральными региональными стандартами (culture neutгal).
При создании приложения, ресурсы которого привязаны к региональным стандартам, компания Microsoft настоятельно рекомендует объединять программный ресурс и ресурсы приложения по умолчанию в одной сборке и не назначать ей региональных стандартов при компоновке. Другие сборки будут ссылаться на нее при создании и работе с типами, которые она предоставляет для общего доступа.
После этого можно создать одну или несколько отдельных сборок, содержащих только ресурсы, зависящие от региональных стандартов, и никакого программного кода. Сборки, помеченные для применения в определенных региональных стандартах, называют сопутствующими (satellite assemblies). Региональные стандарты, назначенные такой сборке, в точности отражают региональные стандарты размещенного в ней ресурса. Следует создавать отдельные сборки для каждого регионального стандарта, который планируется поддерживать.
Обычно сопутствующие сборки компонуются при помощи утилиты AL.exe. Не стоит использовать для этого компилятор - ведь в сопутствующей сборке не должно быть программного кода. Применяя утилиту AL.exe, можно задать желаемые региональные стандарты параметром /c[ulture]:text, где text - это строка (например, en-US, представляющая американский вариант английского языка). При развертывании сопутствующие сборки следует помещать в подкаталог, имя которого совпадает с текстовой строкой, идентифицирующей региональные стандарты. Например, если базовым каталогом приложения является С:\МуАрр, сопутствующая сборка для американского варианта английского языка должна быть в каталоге C:\MyApp\en-US. Во время выполнения доступ к ресурсам сопутствующей сборки осуществляют через класс System.Resources.ResourceManager.
Примечание. Хотя это и не рекомендуется, можно создавать сопутствующие сборки с программным кодом. При желании вместо параметра /culture утилиты AL.exe региональный стандарт можно указать в атрибуте System.Reflection.AssembyCulture, определяемом пользователем, например, следующим об разом:
{codecitation class="brush: csharp; gutter: true;" width="100%" }// Назначить для сборки региональный стандарт Swiss German
[assembly:AssemblyCulture("de-CH")]
{/codecitation}
Лучше не создавать сборки, ссылающиеся на сопутствующие сборки. Другими словами, все записи таблицы AssemblyRef должны ссылаться на сборки с нейтральными региональными стандартами. Если необходимо получить доступ к типам или членам, расположенным в сопутствующей сборке, следует воспользоваться методом отражения (см. главу 23).
Развертывание простых приложении (закрытое развертывание сборок)
Ранее в этой главе было показано, как компоновать модули и объединять их в сборки. Теперь можно приступить к изложению того, как упаковывать и развертывать сборки, чтобы пользователь мог работать с приложением.
Особых средств для упаковки сборки не требуется. Легче всего упаковать набор сборок, просто скопировав все их файлы. Например, можно поместить все файлы сборки на компакт-диск и передать их пользователю вместе с программой установки, написанной в виде пакетного файла. Такая программа про сто копирует файлы с компакт-диска в каталог на жестком диске пользователя. Поскольку сборка включает все ссылки и типы, определяющие ее работу, ему достаточно запустить приложение, а CLR найдет в каталоге приложения все сборки, на которые ссылается данная сборка. Так что для работы приложения не нужно модифицировать реестр, а чтобы удалить приложение, достаточно просто удалить его файлы - и все!
Конечно, можно применять для упаковки и установки сборок другие механизмы, например САВ-файлы (они обычно используются в сценариях с за грузкой из Интернета для сжатия файлов и сокращения времени загрузки). Можно также упаковать файлы сборки в MSI -файл, предназначенный для службы установщика Windows (Windows Installer), MSIExec.exe. MSI позволяет установить сборку по требованию при первой попытке CLR ее загрузить. Эта функция не нова для службы MSI, она также поддерживает аналогичную функцию для неуправляемых ЕХЕ- и DLL-файлов.
Примечание. Пакетный файл или подобная простая «установочная программа» скопирует приложение на машину пользователя, однако для создания ярлыков на рабочем столе, в меню Пуск (Start) и на панели быстрого запуска понадобится программа посложнее. Кроме того, скопировать, восстановить или переместить приложение с одной машины на другую легко, но ссылки и ярлыки потребуют специального обращения.
Естественно, в Visual Studio есть встроенные механизмы, которые можно задействовать для публикации приложений, - это делается на вкладке Publish страницы свойств проекта. Можно использовать ее, чтобы заставить Visual Studio создать МSI-файл и скопировать его на веб-сайт, FТР-сайт или в за данную папку на диске. МSI-файл также может установить все необходимые компоненты, такие как .NET Framework или Microsoft SQL Server 2005 Express Edition. Наконец, приложение может автоматически проверять наличие обновлений и устанавливать их на пользовательской машине посредством технологии ClickOnce.
Сборки, развертываемые в том же каталоге, что и приложение, называют сборками с закрытым развертыванием (privately deployed assemblies), так как файлы сборки не используются совместно другими Приложениями (если только другие приложения не развертывают в том же каталоге). Сборки с закрытым развертыванием - серьезное преимущества для разработчиков, конечных пользователей и администраторов, поскольку достаточно скопировать такие сборки в базовый каталог приложения, и CLR сможет загрузить и исполнить содержащийся в них код. Кроме того, легко удалить приложение, просто уда лив сборки из его каталога. Также легко создавать резервные копии подобных сборок и восстанавливать их.
Несложный сценарий установки/перемещения/удаления приложения стал возможным благодаря наличию в каждой сборке метаданных. Метаданные указывают, какую сборку, на которую они ссылаются, нужно загрузить - для этого не нужны параметры реестра. Кроме того, область видимости сборки охватывает все типы. Это значит, что приложение всегда привязывается именно к тому типу, с которым оно было скомпоновано и протестировано. CLR не может загрузить другую сборку просто потому, что она предоставляет тип с тем же именем. Этим CLR отличается от СОМ, где типы регистрируются в системном реестре, что делает их доступными любому приложению, работающему на машине.
В главе 3 рассказано о развертывании совместно используемых сборок, доступных нескольким приложениям.
Простое средство администрирования (конфигурационный файл)
Пользователи и администраторы лучше всех могут определять разные аспекты работы приложения. Например, администратор может решить переместить файлы сборки на жесткий диск пользователя или заменить данные в манифесте сборки. Есть и другие сценарии управления версиями и удаленного администрирования, о некоторых из них рассказано в г лаве 3.
Для того чтобы предоставить администратору контроль над приложением, можно разместить в каталоге приложения конфигурационный файл. Его может создать и упаковать издатель приложения, после чего программа установки за пишет конфигурационный файл в базовый каталог приложения. Кроме того, администратор или конечный пользователь машины может сам создать или модифицировать этот файл. CLR интерпретирует его содержимое для измене ния политики поиска и загрузки файлов сборки.
Конфигурационные файлы содержат ХМL-теги и могут быть ассоциированы с приложением или с компьютером. Использование отдельного файла (вместо параметров, хранимых в реестре) позволяет легко создать резервную копию файла, а администратору - без труда копировать файлы с машины на машину: достаточно скопировать нужные файлы - в результате будет также скопирована административная политика.
В главе 3 такой конфигурационный файл рассматривается подробно, а пока вкратце обсудим его. Допустим, издатель хочет развернуть приложение вместе с файлами сборки JeffТypes, но в отдельном каталоге. Желаемая структура каталогов с файлами выглядит следующим образом:
{codecitation class="brush: plain; gutter: true;" width="100%" }
Каталог AppDir (содержитфайлысборкиприложения)
Арр.ехе
App.exe.config (обсуждаетсяниже)
Подкаталог AuxFiles (содержитфайлысборки JeffТypes)
JeffТypes.dll
FUT. netmodule
RUT.netmodule
{/codecitation}
Поскольку файлы сборки JeffТypesболее не находятся в базовом каталоге приложения, CLRне сможет найти и загрузить их, и при запуске приложения будет сгенерировано исключение System. IO. FileNotFoundException. Чтобы избежать этого, издатель создает конфигурационный файл в формате XML и размещает его в базовом каталоге приложения. Имя этого файла должно со впадать с именем главного файла сборки и иметь расширение config, в данном случае - App.exe.config. Содержимое этого конфигурационного файла должно выглядеть примерно следующим образом:
{codecitation class="brush: xml; gutter: true;" width="100%" }<configuration>
<runtime>
<assemblyBinding xmlns="urn: schemas-microsoft-com:asm.v1">
<probing privatePath="AuxFiles" />
</assemblyBinding>
</runtime>
</configuration>
{/codecitation}
Пытаясь найти файл сборки, CLR всегда сначала ищет в каталоге приложения, и если поиск заканчивается неудачей, продолжает искать в подкаталоге AuxFiles. В атрибуте privatePath элемента, направляющего поиск, можно указать несколько путей, разделенных точкой с запятой. Считается, что все пути заданы относительно базового каталога приложения. Идея здесь в том, что приложение может управлять своим каталогом и его подкаталогами, но не может управлять другими каталогами.
Алгоритм поиска файлов сборки
В поиске сборки среда CLR просматривает несколько подкаталогов. Порядок при поиске сборки с нейтральными региональными стандартами таков (при условии, что параметры firstPrivatePath и secondPrivatePath определены в атрибуте privatePath конфигурационного файла):
{codecitation class="brush: plain; gutter: true;" width="100%" }AppDir\AsmName.dll
AppDir\AsmName\AsmName.dll
AppDir\firstPrivatePath\AsmName.dll
AppDir\firstPrivatePath\AsmName\AsmName.dll
AppDir\ secondPrivatePath\AsmName.dll
AppDir\secondPrivatePath\AsmName\AsmName.dll
{/codecitation}
В этом примере конфигурационный файл вовсе не понадобится, если файлы сборки JeffТypes развернуты в подкаталоге JeffТypes, так как CLR автоматически проверлет подкаталог, имя которого совпадает с именем искомой сборки.
Если ни в одном из упомянутых каталогов сборка не найдена, CLR начинает поиск заново, но теперь ищет файл с расширением ЕХЕ вместо DLL. Если и на этот раз поиск оканчивается неудачей, генерируется исключение FileNotFoundException.
В отношении сопутствующих сборок действуют те же правила поиска за одним исключением: ожидается, что сборка находится в подкаталоге базового каталога приложения, имя которого совпадает с названием региональных стандартов. Например, если для файла AsmName.dll назначен региональный стандарт “en-US”, порядок просмотра каталогов таков:
{codecitation class="brush: plain; gutter: true;" width="100%" }
C:\AppDir\en-US\AsmName.dll C:\AppDir\en-US\AsmName\AsmName.dll
C:\AppDir\firstPrivatePath\en-US\AsmName.dll
C:\AppDir\firstPrivatePath\en-US\AsmName\AsmName.dll
C:\AppDir\secondPrivatePath\en-US\AsmName.dll
C:\AppDir\secondPrivatePath\en-US\AsmName\AsmName.dll
C:\AppDir\en-US\AsmName.exe C:\AppDir\en-US\AsmName\AsmName.exe
C:\AppDir\firstPrivatePath\en-US\AsmName.exe
C:\AppDir\firstPrivatePath\en-US\AsmName\AsmName.exe
C:\AppDir\secondPrivatePath\en-US\AsmName.exe
C:\AppDir\secondPrivatePath\en-US\AsmName\AsmName.exe
C:\AppDir\en\AsmName.dll C:\AppDir\en\AsmName\AsmName.dll
C:\AppDir\firstPrivatePath\en\AsmName.dll
C:\AppDir\firstPrivatePath\en\AsmName\AsmName.dll
C:\AppDir\secondPrivatePath\en\AsmName.dll
C:\AppDir\secondPrivatePath\en\AsmName\AsmName.dll
C:\AppDir\en\AsmName.exe C:\AppDir\en\AsmName\AsmName.exe
C:\AppDir\firstPrivatePath\en\AsmName.exe
C:\AppDir\firstPrivatePath\en\AsmName\AsmName.exe
C:\AppDir\secondPrivatePath\en\AsmName.exe
C:\AppDir\secondPrivatePath\en\AsmName\AsmName.ex
{/codecitation}
Как видите, CLRищет файлы с расширением ЕХЕ или DLL. Поскольку поиск может занимать значительное время (особенно когда CLR пытается найти файлы в сети), в конфигурационном ХМL-файле можно указать один или несколько элементов региональных стандартов, чтобы ограничить круг проверяемых каталогов при поиске сопутствующих сборок.
Имя и расположение конфигурационного ХМL-файла может различаться в зависимости от типа приложения.
·Для исполняемых приложений (ЕХЕ) конфигурационный файл должен располагаться в базовом каталоге приложения. У него должно быть то же имя, что и у ЕХЕ-файла, но с расширением config. Для приложений ASP.NET Web Foгm конфигурационный файл всегда должен находиться в виртуальном корневом каталоге веб-приложения и называться Web.config. Кроме того, в каждом вложенном каталоге может быть собственный файл Web.config с унаследованными параметрами кон фигурации. Например, веб-приложение, расположенное по адресу http://www.Wintellect.com/Тraining, будет использовать параметры из файлов Web.config, расположенных в виртуальном корневом каталоге и в подкаталоге Training.
Как уже было сказано, параметры конфигурации применяют к конкретному приложению и конкретному компьютеру. При установке платформа .NET Framework создает файл Machine.config. Существует по одному файлу Machine.config на каждую версию среды CLR, установленную на данной машине. Файл Machiпe.coпfig расположен в следующем каталоге:
{codecitation class="brush: plain; gutter: true;" width="100%" }%SystemRoot%\Microsoft .NET\Framework\вepcия\CONFIG
{/codecitation}
Естественно, %SystemRoot% - это каталог, в котором установлена система Windows (обычно C:\Windows), а версия - номер версии, идентифицирующий определенную версию платформы .NET Framework (например, v4.0.#####). Параметры файла Machine.config заменяют параметры конфигурационного файла конкретного приложения. Администраторам и пользователям следует избегать модификации файла Machine.coпfig, поскольку в нем хранятся многие параметры, связанные с самыми разными аспектами работы системы, что серьезно затрудняет ориентацию в его содержимом. Кроме того, требуется резервное копирование и восстановление конфигурационных параметров приложения, что возможно лишь при использовании конфигурационных файлов, специфичных для приложения.