Суббота, 13 октября 2012 10:52

Закрытое развертывание сборок со строгими именами

Оцените материал
(18 голосов)

Закрытое развертывание сборок со строгими именами

Установка сборок в каталог GAC дает несколько преимуществ. GAC позволяет нескольким приложениям совместно использовать сборки, сокращая в целом количество обращений к физической памяти.

Кроме того, при помощи GAC легче развертывать новую версию сборки и заставить все приложения использовать новую версию сборки посредством реализации политики издателя (см. далее). GAC также обеспечивает совместное управление несколькими версиями сборки. Однако GAC обычно находится под защитой механизмов безопасности, поэтому устанавливать сборки в GAC может только администратор. Кроме того, установка сборки в GAC делает невозможным развертывание сборки простым копированием.

Хотя сборки со строгими именами могут устанавливаться в GAC, это вовсе не обязательно. В действительности рекомендуется развертывать сборки в GAC, только если они предназначены для совместного использования несколькими приложениями. Если сборка не предназначена для этого, следует развертывать ее закрыто. Это позволяет сохранить возможность установки путем «простого» копирования и лучше изолирует приложение с его сборками. Кроме того, GAC не задуман как замена каталогу C:\Windows\System32 в качестве •общей помойки• для хранения общих файлов. Это позволяет избежать затирания одних сборок другими путем установки их в разные каталоги, но «отъедает» дополнительное место на диске.

Примечание. На самом деле элемент codeBase конфигурационного файла задает URL-адрес, который может ссылаться на любой каталог пользовательского жесткого диска или на адрес в Web. В случае веб-адреса CLR автоматически загрузит указанный файл и сохранит его в кэше загрузки на пользовательском жестком диске (в подкаталоге C:\Documents and Settings\<UserName>\ Local Settings\ApplicatioпData\Assembly, где <UserName> - имя учетной записи пользователя, вошедшего в систему). В дальнейшем при ссылке на эту сборку CLR сверит метку времени локального файла и файла по указан­ ному URL-адресу. Если последний новее, CLR загрузит файл только раз (это сделано для повышения производительности). Пример конфигурационного файла с элементом codeBase будет продемонстрирован позже.

Помимо развертывания в GAC или закрытого развертывания, сборки со строгими именами можно развертывать в произвольном каталоге, известном лишь небольшой группе приложений. Допустим, вы создали три приложения, совместно использующие одну и ту же сборку со строгим именем. После установки можно создать по одному каталогу для каждого приложения и дополнительный каталог для совместно используемой сборки. При установке приложений в их каталоги также записывается конфигурационный ХМL-файл, а в элемент codeBase для совместно используемой сборки заносится путь к ней. В результате при выполнении CLR будет знать, что совместно используемую сборку надо искать в каталоге, содержащем сборку со строгим именем. Обратите внимание, что эту методику применяют довольно редко и в силу ряда причин не рекомендуют. Дело в том, что в таком сценарии ни одно отдельно взятое приложение не в состоянии определить, когда именно нужно удалить файлы совместно используемой сборки.

Как исполняющая среда разрешает ссылки на типы

В начале главы 2 вы видели следующий исходный текст:

{codecitation class="brush: csharp; gutter: true;" width="100%" }public sealed class Program
{
public static void Main()
{
System.Console.Writeline("Hi"):
}
}{/codecitation}

В результате компиляции и компоновки этого кода получалась сборка, например Program.exe. При запуске приложения происходит загрузка и инициализация CLR. Затем CLR сканирует СLR-заголовок сборки в поисках атрибута MethodDefТoken, идентифицирующего метод Main, представляющий точку входа в приложение. CLR находит в таблице метаданных MethodDef смещение, по которому в файле находится IL-код этого метода, и компилирует его в машинный код процессора при помощи JIT -компилятора. Этот процесс включает в себя проверку безопасности типов в компилируемом коде, после чего начинается исполнение полученного машинного кода. Чтобы получить показан IL-код метода Main, нужно запустить ILDasm.exe, выбрать в меню View команду Show Bytes и дважды щелкнуть на методе Маin в дереве просмотра .

Во время JIT -компиляции этого кода CLR обнаруживает все ссылки на типы и члены и загружает сборки, в которых они определены (если они еще не загружены). Как видите, показанный код содержит ссылку на метод System.Consolе.WriteLine: команда Саll ссылается на маркер метаданных 0А000003. Этот маркер идентифицирует запись 3 таблицы метаданных MemberRef (таблица Од). Просматривая эту запись, CLR видит, что одно из ее полей ссылается на элемент таблицы TypeRef (описывающий тип System.Consolе). Запись таблицы TypeRef направляет CLR к следующей записи в таблице AssemblyRef:

{codecitation class="brush: plain; gutter: true;" width="100%" }MSCorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089{/codecitation}

На этом этапе CLR уже знает, какая сборка нужна, и ей остается лишь найти и загрузить эту сборку.

При разрешении ссылки на тип CLR может найти нужный тип в одном из следующих мест.

·В том же файле. Обращение к типу, расположенному в том же файле, определяется при компиляции (данный процесс иногда называют ранним связыванием). Этот тип загружается прямо из этого файла, и исполнение продолжается.

·В другом файле той же сборки. Исполняющая среда проверяет, что файл, на который ссылаются, описан в таблице FileRef в манифесте текущей сборки. При этом исполняющая среда ищет его в каталоге, откуда был загружен файл, содержащий манифест сборки. Файл загружается, проверяется его хэш, чтобы гарантировать его целостность, затем CLR находит в нем нужный член типа, и исполнение продолжается.

·В файле другой сборки. Когда тип, на который ссылаются, находится в файле другой сборки, исполняющая среда загружает файл с манифестом этой сборки. Если в файле с манифестом необходимого типа нет, загружается соответствующий файл, CLR находит в нем нужный член типа, и исполнение продолжается.

Примечание. Таблицы метаданных ModuleDef, ModuleRef и FileDef ссылаются на файлы по имени и расширению. Однако таблица метаданных AssemblyRef ссылается на сборки только по имени, без расширения. Во время привязки к сборке система автоматически добавляет к имени файла расширение DLL или ЕХЕ, пытаясь найти файл путем проверки каталогов по алгоритму, описанному в главе 2.

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

Примечание. При желании можно зарегистрировать в вашем коде методы обратного вызова с событиями из System.AppDomain.AssemblyResolve, System.AppDomain. ReflectionOnlyAssemblyRessolve и System.AppDomain.TypeResolve. В методах обратного вызова вы можете выполнить программный код, который решает эту проблему и позволяет приложению выполняться без выбрасывания исключения.

В предыдущем примере среда CLR обнаруживала, что тип System.Console реализован в файле другой сборки. CLR должна найти эту сборку и загрузить РЕ-файл, содержащий ее манифест. После этого манифест сканируется в поисках сведений о РЕ-файле, в котором реализован искомый тип. Если необходимый тип содержится в том же файле, что и манифест, все замечательно, а если в другом файле, то CLR загружает этот файл и посматривает его метаданные в поисках нужного типа. После этого CLR создает свою внутреннюю структуру данных для представления типа и JIТ-компилятор завершает компиляцию метода Маin. В завершение процесса начинается исполнение метода Маin.

Рисунок 3.2 иллюстрирует процесс привязки к типам.

 

Рис. 3.2. Блок-схема алгоритма поиска метаданных, используемых CLR, файла сборки, где определен тип или метод, на который ссылается IL-код

Обратите внимание на то, что если какая-либо операция заканчивается неудачей, то выбрасывается соответствующее исключение.

Примечание. Строго говоря, приведенный пример не является стопроцентно верным. Для ссылок на методы и типы, определенные в сборке, поставляемой в составе .NET Framework, все сказанное верно. Однако сборки .NET Framework (в том числе MSCorlib.dll) тесно связаны с работающей версией CLR. Любая сборка, ссылающаяся на сборки .NET Framework, всегда привязывается к соответствующей версии CLR. Этот процесс называют унификацией (uпificatioп), и Microsoft его поддерживает, потому что в этой компании все сборки .NET Framework тестируются во вполне определен­ ной версии CLR. Поэтому унификация стека кода гарантирует корректную работу приложений.

В предыдущем примере ссылка на метод Writeliпe объекта System. Coпsole привязывается к любой версии MSCorlib.dll, совпадающей с CLR, независимо от того, на какую версию MSCorlib.dll ссылается таблица AssemblyRef в метаданных сборки.

Есть еще один нюанс: CLR идентифицирует все сборки по имени, версии, региональному стандарту и открытому ключу. Однако GAC различает сборки по имени, версии, региональному стандарту, открытому ключу и процессорной архитектуре. При поиске сборки в GAC среда CLR выясняет, в каком процессе выполняется приложение - 32-разрядном х86 (возможно, с использованием технологии WoW64), 64-разрядном х64 или 64-разрядном IA64. Сначала выполняется поиск сборки в GAC с учетом процессорной архитектуры. В случае неудачи происходит поиск сборки без учета процессорной архитектуры.

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

Примечание. CLR поддерживает возможность перемещения типа (класса, структуры, перечисляемого типа, интерфейса или делегата) из одной сборки в другую. Например, в .NET 3.5 класс System.ТimeZoпelпfo определен в сборке System.Core.dll. Но в .NET 4.0 компания Microsoft переместила этот класс в сборку MsCorlib.dll. В стандартной ситуации перемещение типа из одной сборки в другую нарушает работу приложения. Однако CLR предлагает воспользоваться атрибутом System.Ruпtime.CompilerServices. TypeForwardedToAttribute, который применяется в оригинальной сборке (например, System.Core.dll). Параметр, который необходимо указать, - System.Type. Он показывает новый тип (он будет определен в MSCorlib.dll), который теперь должно использовать приложение. С того момента, как конструктор TypeForwardedToAttribute принимает этот тип, содержащая его сборка будет зависеть от сборки, в которой он определен.

Если вы воспользуетесь этим преимуществом, нужно также применить атрибут System.Ruпtime.CompilerServices.TypeForwardedToAttribute в новой сборке и указать конструктору атрибута полное имя сборки, которая служит для определения типа. Этот атрибут обычно используется для инструментальных средств, утилит и сериализации. Как только конструктор TypeForwardedToAttribute получает строку с этим именем, сборка, содержащая этот атрибут, становится независимой от сборки, определяющей тип.

Дополнительные административные средства (конфигурационные файлы)

В разделе «Простое средство администрирования (конфигурационный файл)» главы 2 мы кратко познакомились со способами изменения администратором алгоритма поиска и привязки к сборкам, используемого CLR. В том же разделе я показал, как перемещать файлы сборки, на которую ссылаются, в подката­ лог базового каталога приложения и как CLR использует конфигурационный ХМL-файл приложения для поиска перемещенных файлов.

Поскольку в главе 2 нам удалось обсудить лишь атрибут privatePath элемента probing, здесь мы рассмотрим остальные элементы конфигурационного ХМL-файла:

{codecitation class="brush: xml; gutter: true;" width="100%" }<?xml version="1.0"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn: schemas-microsoft-com: asm..v1">
<probing privatePath="AuxFiles:bin\subdir" />
<dependentAssembly>
<assemblyldentity name="Jeff
Тypes"
рublicKeyToken="32ab4ba45e0a69al" culture="neutral "/>
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />
<codeBase version="2.0.0.0" href="http://www.Wintellect.com/Jeff
Тypes.dll" />
</dependentAssembly>
<dependentAssembly>
<assemblyldentity name="TypeLib"
рublicKeyToken="lf2e74e897abbcfe" culture="neutral "/>
<bindingRedirect oldVersion="
З.0.0.0-3.5.0.0" newVersion-"4.0.0.0" />
<publisherPolicy apply="no" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>{/codecitation}

ХМL-файл предоставляет CLR обширную информацию.

·Элемент probing. Определяет поиск в подкаталогах AuxFiles и bin\subdir, расположенных в базовом каталоге приложения, при попытке найти сборку с нестрогим именем. Сборки со строгим именем CLR ищет в GAC или по URL-aдpecy, указанному элементом codeBase.

·Первыйнаборэлементов dependentAssembly, assemblyldentity иЬinding­ Redirect. При попытке найти сборки FredTypes с номерами версий с 3.0.0.0 по 3.5.0.0 включительно и нейтральными региональными стандартами, изданные организацией, владеющей открытым ключом с маркером 1f2e74e897abbcfe, система вместо этого будет искать аналогичную сборку, но с номером вер­ сии 4.0.0.0.

·Элемент codebase. При попытке найти сборку JeffТypes с номером версии 2.0.0.0 и нейтральными региональными стандартами, изданную организацией, владеющей открытым ключом с маркером 32ab4ba45e0a69al, система будет пытаться выполнить привязку по адресу, заданному в URL: http://wwwWintellect.com/JeffТypes.dll. Хотя я и не говорил об этом в главе 2, элемент codeBase можно применять и к сборкам с нестрогими именами. При этом номер версии сборки игнорируется и его следует опустить при определении элемента codeBase. URL-aдpec, заданный элементом codeBase, также должен ссылаться на подкаталог базового каталога приложения.

·Второйнаборэлементов dependentAssembly, assemblyldentity и bindingRedirect. Подменяет искомую сборку: при попытке найти сборку JeffТypes с номером версии 1.0.0.0 и нейтральными региональными стандарта­ ми, изданную организацией, владеющей открытым ключом с маркером 32ab4ba45e0a69al, система будет искать аналогичную сборку, но с номером версии 2.0.0.0.

·Элемент publisherPolicy. Если организация, производитель сборки TypeLib, развернула файл политики издателя (описание этого файла см. в следующем разделе), среда CLR должна игнорировать этот файл.

При компиляции метода CLR определяет типы и члены, на которые он ссылается. Используя эти данные, исполняющая среда определяет (путем просмотра таблицы AssemblyRef вызывающей сборки), на какую сборку исходно ссылалась вызывающая сборка во время компоновки. Затем CLR ищет сведения о сборке в конфигурационном файле приложения и следует любым изменениям номера версии, заданным в этом файле.

Если значение атрибута аррlу элемента publisherPoliсу равно yes или отсутствует, CLR проверяет наличие в GAC новой сборки/версии и применяет все перенаправления, которые счел необходимым указать издатель сборки (о политике издателя рассказвыается в следующем разделе); далее CLR ищет именно эту сборку/ версию. Наконец просматривает сборку/ версию в файле Machine.config и применяет все указанные в нем перенаправления к другим версиям.

На этом этапе CLR определяет номер версии сборки, которую она должна загрузить, и пытается загрузить соответствующую сборку из GAC. Если сборки в GAC нет, а элемент codeBase не определен, CLR пытается найти сборку, как описано в главе 2. Если конфигурационный файл, задающий последнее изменение номера версии, содержит элемент codeBase, CLR пытается загрузить сборку с URL-aдpeca, заданного этим элементом.

Эти конфигурационные файлы обеспечивают администратору настоящий контроль над решением, принимаемым CLR относительно загрузки той или иной сборки. Если в приложении обнаруживается ошибка, администратор может связаться с издателем сборки, содержащей ошибку, после чего издатель пришлет новую сборку. По умолчанию среда CLR не может загрузить новую сборку, потому что уже существующая сборка не ссылается на новую версию. Однако администратор может заставить CLR загрузить новую сборку, модифицировав конфигурационный ХМL-файл приложения.

Если администратор хочет, чтобы все сборки, установленные на компьютере, использовали новую версию, то вместо конфигурационного файла приложения он может модифицировать файл Machine.config для данного компьютера, и CLR будет загружать новую версию сборки при каждой ссылке из приложений на старую версию.

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

Управление версиями при помощи политики издателя

В ситуации, описанной в предыдущем разделе, издатель сборки просто присылал новую версию сборки администратору, который устанавливал сборку и вручную вносил изменения в конфигурационные ХМL-файлы машины или приложения. Вообще, после того как издатель исправил ошибку в сборке, ему нужен простой способ упаковки и распространения новой сборки всем пользователям. Кроме того, нужно как-то заставить среду CLR каждого пользователя задействовать новую версию сборки вместо старой. Естественно, каждый пользователь может сам изменить конфигурационные ХМL-файлы на своих машинах, но это ужасно неудобно, да и чревато ошибками. Издателю нужен подход, который позволил бы ему создать свою «политику» и установить ее на пользовательский компьютер с новой сборкой. В этом разделе показано, как издатель сборки может создать подобную политику.

Допустим, вы - издатель, только что создавший новую версию своей сборки, в которой исправлено несколько ошибок. Упаковывая новую сборку для рассылки пользователям, надо создать конфигурационный ХМL-файл. Он очень похож на те, что мы обсуждали раньше. Вот пример конфигурационного файла JeffТypes.config для сборки JeffТypes.dll:

{codecitation class="brush: xml; gutter: true;" width="100%" }<configuration>
<runtime>
<assemblyBinding xml ns="urn: schemas-microsoft-com:asm.v1 ">
<dependentAssembly>
<assemblyidentity name="Jeff
Тypes"
publicKeyToken="32ab4ba45e0a69al" culture=" neutral"/>
<bindingRedirect oldVersion="l.0.0.0" newVersion=" 2.0.0.0" />
<codeBase version="2.0.0.0" href="http://www.Wintellect.com/Jeff
Тypes.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>{/codecitation}

Конечно, издатель может определять политику только для своих сборок. Кроме того, показанные здесь элементы - единственные, которые можно задать в конфигурационном файле политики издателя. Например, в конфигурационном файле политики нельзя задавать элементы probing и publisherPoliсу.

Этот конфигурационный файл заставляет CLR при каждой ссылке на версию 1.0.0.0 сборки JeffТypes загружать вместо нее версию 2.0.0.0. Теперь вы, как издатель, можете создать сборку, содержащую конфигурационный файл политики издателя. Для создания сборки с политикой издателя вызывается утилита AL.exe со следующими параметрами:

{codecitation class="brush: plain; gutter: true;" width="100%" }AL.exe /out:policy.1.0.JeffTypes.dll
/version:1.0.0.0
/keyfile:MyCompany.keys
/linkresource:JeffTypes.config{/codecitation}

Смысл параметров командной строки для AL.exe следующий.

·Параметр /out приказывает AL.exe создать новый РЕ-файл с именем Policy.1.0.JeffТypes.dll, в котором нет ничего, кроме манифеста. Имя этой сборки имеет очень большое значение. Первая часть имени, Policy, сообщает CLR, что сборка содержит информацию политики издателя. Вторая и третья части имени, 1.0, сообщают CLR, что эта политика издателя предназначена для любой версии сборки JeffТypes, у которой старший и младший номера версии равны 1.0. Политики издателя применяются только к старшему и младшему номерам версии сборки; нельзя создать политику издателя для отдельных компоновок или редакций сборки. Четвертая часть имени, JeffТypes, указывает имя сборки, которой соответствует политика издателя. Пятая и последняя часть имени, dll, - это просто расширение, данное результирующему файлу сборки.

·Параметр /version идентифицирует версию сборки с политикой издателя, которая не имеет ничего общего с версией самой сборки. Как видите, версия­ ми сборок, содержащих политику издателя, тоже можно управлять. Сейчас издателю нужно создать политику, перенаправляющую CLR от версии 1.0.0.0 сборки JeffТypes к версии 2.0.0.0, а в будущем может потребоваться поли­ тика, перенаправляющая от версии 1.0.0.0 сборки JeffТypes к версии 2.5.0.0. CLR использует номер версии, заданный этим параметром, чтобы выбрать самую последнюю версию сборки с политикой издателя.

·Параметр /keyfilе заставляет AL.exe подписать сборку с политикой издателя при помощи пары ключей, принадлежащей издателю. Эта пара ключей так­ же должна совпадать с парой, использованной для подписания всех версий сборки JeffТypes. В конце концов, именно это совпадение позволяет CLR установить, что сборка JeffТypes и файл с политикой издателя для этой сборки созданы одним издателем.

·Параметр /linkresource заставляет AL.exe считать конфигурационный ХМL-файл отдельным файлом сборки. При этом в результате компоновки получается сборка из двух файлов. Оба следует упаковывать и развертывать на пользовательских компьютерах с новой версией сборки JeffТypes. Между прочим, конфигурационный ХМL-файл нельзя встраивать в сборку, вызвав AL.exe с параметром /embedresource, и создавать таким образом сборку, со­ стоящую из одного файла, так как CLR требует, чтобы сведения о конфигурации в формате XML размещались в отдельном файле.

Сборку, скомпонованную с политикой издателя, можно упаковать с файлом новой версии сборки JeffТypes.dll и передать пользователям. Сборка с политикой издателя должна устанавливаться в GAC. Саму сборку JeffТypes можно установить в GAC, но это не обязательно. Ее можно развернуть в базовом каталоге приложения или в другом каталоге, заданном в URL-aдpece из элемента codeBase.

Примечание. Издатель должен создавать сборку со своей политикой лишь для развертывания исправленной версии сборки или пакетов исправлений для нее. Установка нового приложения не должна требовать сборки с политикой издателя.

Прочитано 19449 раз
   

Поиск  

   

Материалы