Частные и общие модули До настоящего момента нам приходилось иметь дело только с частными моду- лями — т е . модулями, устанавливаемыми в качестве составной части единствен- ного приложения. В .NET имеются специальные средства, позволяющие создавать общие модули, которые могут использоваться несколькими программами одновре- менно. Частные модули По умолчанию модуль является частным по отношению к проекту. Частные мо- дули обязательно должны располагаться в той же директории, что и само прило- жение. Модуль shapes.dll является частным; для того чтобы ссылаться на него из проекта shapes, нам пришлось на предыдущем этапе строить оба проекта в одной и той же директории, в противном случае потребовалось бы осуществлять поиск по директории разработки проекта shapes из проекта shapeuser при добавлении ссылки в VS; в этом случае VS создает частную копию shapes.dll и размещает ее В директории ShapeUser. Создание копии shapes.dll позволяет гарантировать, что приложение shapeuser будет работать, даже если оригинальный файл shapes.dll окажется недоступным из-за продолжающегося процесса разработки. Однако подход с созданием копий каждой динамически загружаемой библиотеки для широко используемых компо- нентов оказывается не слишком эффективным, поэтому в .NET Framework преду- смотрено использование общих модулей. Общие модули Общие модули оказываются доступными для использования всеми программа- ми, выполняющимися на данной системе. От программы не требуется, чтобы она располагала информацией о местоположении общего модуля, поскольку все общие модули хранятся в специальной системной директории .NET, которая называется Global Assembly Cache (GAC — кэш глобальных модулей). Поскольку эти моду- ли доступны по всей системе, то на систему выполнения программ .NET налагается обязанность выполнения нескольких дополнительных проверок, касающихся общих модулей и позволяющих определить допустимость их использования программой, которая к ним обращается, например, по вопросам обеспечения безопасности и совместимости версий. Безопасность и строгие имена Любой общий модуль должен обеспечивать гарантию того, что он не был заме- нен на какой-либо другой модуль, обладающий теми же именем и номером версии, но в который были внесены те или иные изменения, например, вследствие зара- жения вирусом. Это достигается за счет того, что для общего модуля существует дополнительное требование — он должен быть помечен зашифрованным ключом, прежде чем он будет загружен в глобальный кэш модулей. Такой ключ обеспечи- вает защиту не только от нарушения системы обеспечения безопасности, но и от обычных коллизий, связанных с тем, что у двух компонентов оказываются одина- ковые имя и номер версии. Если значения ключей у компонентов оказываются различными, то такие компоненты не считаются одинаковыми, даже несмотря на то, что они обладают идентичными именами. Уникальное сочетание, состоящее из имени модуля, номера его версии и его ключа, называется строгим именем. Модули .NET 607 Кэш глобальных модулей Кэш глобальных модулей представляет собой специальную директорию, кото- рая располагается в директории winNT\assembly. Все общие создаваемые компа- нией Microsoft модули, включая модули System .NET Framework, хранятся в этой директории и загружаются именно оттуда. Если вы попытаетесь просмотреть эту директорию с помощью Windows Explorer, то вы увидите специальное внешнее расширение Windows, в котором выводятся различные свойства модулей, в том числе и ключ, входящий в состав строгого имени каждого из них. Внешнее расширение Windows (называемое shfusion.dll) встроено в Windows Explorer и призвано расширить его возможности по сравнению с обычным выво- дом файлов. На следующем рисунке изображен тот вид, который GAC имеет в Windows Explorer. При этом осуществляется вывод имени, номера версии, ключа и других свойств модулей: 1 ; { ф ! I h '. i lift •1 • •и еедмф ""' • iiJ 123 HMM [ffia •• • sU System t * s ^ a O^Wt?* f Sib Sy«*-T t*«rt*KI rfntiiH» ituiiw» n<g—m sib Sy$?«»~ Kir*)r*8-S^ristea X о Э« Ik** if, 9 .о.ги .0.243 AJ4 .O..g41 .0.241 Mti .0.24J .0.241 4MNI L.0.241 «O.?4 .0,211 0.Г4 —. - j .0 bowseM s,sa*scs3 ;.$ MefVMtiiia^ 5,0 bOSf^iTf i 1 dSGdS* ; A faommiMXMSi t.0 bQ3f S?7f i 1й5Оа^4 5.0 bO3^SF 3f 3 кШаЗа ; Q 1 - J Внешнее расширение Windows делает обязательным применение политики обеспечения безопасности для 1тюбальных модулей, позволяя копировать модули в эту директорию посредством простого переноса мышью только в том случае, если соблюдены все необходимые правила. Чтобы иметь доступ к кэшу глобальных модулей, необходимо обладать правами локального администратора. Создание общего модуля Чтобы создать общий модуль со строгим именем, требуется сгенерировать два ключа — общий и частный, которыми будет помечен этот модуль. Асимметричные системы шифрования на основе общих/частных ключей используют частный ключ, известный только отправителю закодированного сообщения, и общий ключ, кото- рый открыт для всех. В среде .NET используется такой же механизм, чтобы гаран- тировать, что общий модуль, на который делается ссылка,— это действительно тот модуль, который требуется (модули, создаваемые различными компаниями, могут обладать одинаковыми именами и номерами версий, а то какой-нибудь хакер по- пытается "вскрыть" программу, создав модуль с теми же именем и номером вер- сии или испортив содержимое существующего модуля). Ключи, содержащиеся в вызове модуля и непосредственно в (помеченном) модуле, сравниваются, и если они не совпадают, то загрузка общего модуля не производится. 608 Глава 21 Сочетание имени модуля, номера его версии и общего ключа гарантированно является уникальным; такое сочетание называется строгим именем. В .NET Framework предусмотрен специальный инструмент sn.exe, предназна- ченный для создания строгих имен (sn расшифровывается как Strong Name — строгое имя). К сожалению, этот инструмент может использоваться только из командной строки, и придется настроить переменную среды PATH перед тем, как вы сможете воспользоваться им (инструкции содержатся в приложении А). Запустите командную строку с помощью VS Command Prompt (выбрав пункт меню View | Other Windows | Command Window) или откройте окно командной стро- ки непосредственно в Windows. Для того чтобы создать ключ, потребуется задать некоторое имя файла (обычно с расширением .snk), к которому вы собираетесь^ обращаться из своего модуля: sn -k shapes.snk Это приведет к созданию файла с именем shapes.snk в текущей директории. Для того, чтобы пометить модуль с помощью данного ключа, модифицируйте атри- бут AssemblyKeyFile, Находящийся В последней ЧЭСТИ файла Assemblylnfo.cs ва- шего проекта: [assembly: AssemblyKeyFileС shapes.snk•)] В VS местонахождение ключа является относительным к директории вашего проекта obj\debug или obj\reiease; если вы помещаете файл в какую-либо другую директорию, например, в корневую директорию проекта, то необходимо задать п у т ь К Э Т ОЙ Д и р е к т о р и и : [ a s s e m b l y : A s s e m b l y K e y F i l e (@и . . \ . . \ s h a p e s . s n k " ) ] . Обратите внимание на то, что при использовании компилятора командной строки следует просто добавить Строку AssemblyKeyFile в начало вашего файла shapes, cs, перед которой должны располагаться соответствующие директивы using. using System.Reflection; using System.Runtime.CompilerServices; [assembly: AssemblyKeyFile("shapes.snk")J Теперь необходимо заново откомпилировать модуль shapes.dll. Если VS ока- зывается не в состоянии обнаружить файл shapes.snk, то проект не компилирует- ся и выдается сообщение об ошибке следующего вида: Cryptographic failure while signing assembly (Ошибка шифрования при попытке пометить файл) 'C:\BegCSharp\Chapter21\Shapes\obj\Debug\Shapes.dH' — 'Error reading key file 'shapes.snk' ('C:\BegCSharp\Chapter2l\Shapes\obj\Debug\Shapes.dll' — 'Ошибка при попытке прочитать файл 'shapes.snk') — The system cannot find the file specified. (— Система не в состоянии обнаружить указанный файл.) В этом сообщении содержится точная информация относительно того, где именно VS осуществляет поиск файла с ключом, и вы можете либо перенести туда требуемый файл, либо указать путь к директории, в которой он на самом деле рас- положен. Модули .NET 609 сuston in cwstan in' a t«*scQflibisysten-R«riection ( ^ y s f c e n . R e f l e c t i o n void jW>carlitj]Systpn.Reflection void [rescorHbjSybteiKReflection t l r t i b l $ t K n U a imcQrlib]$l><iti>tt.Reflection d [ascorlibJSysten.Reflection [ l l b j s f c R f l t i custo* imtinei Mil . // *• — The following c^tH' // .cu-iton inst«nc* ueid .pubiickfy • <Й8 2h BO 88 №1» 88 08 88 9h 88 B8 88 06 82 Be 88 // .$ ibut utei t iptionftttril: ».Rcfl«tid»». is added autofSiitically, do not ibvi { 88 2A вв 00 S2 $3 85 *Z 31 06 6* as 68 »! U 81 78 1С E2 £f 6? 12 ВС »9 f t $8 ?1 « t Гв Ж tft 82 £9 I f 1С AC ftE ft9 // -. 3Z 5f 8% г« 8Й «f 8C 79 CC 96 56 *6 55 7« Й? 1С // 2_ at i r 19 as эс 3f 6? ев за о% вг ft os м 6? a* / / I I •50 £t £« 99 65 £3 «0 SO 92 78 At 95 DC IE ОС 13 // я, as *8 5ft »6 96 70 CB SO 83 0? ?ft FO CC «SF 5» CS ) // 58 t;8:594J2e«/J» После того как проект будет успешно постро- ен заново, модуль будет считаться помеченным. Если теперь внимательно посмотреть на мани- фест МОДУЛЯ shapes.dll С ПОМОЩЬЮ ildasm, TO можно обнаружить вновь созданный общий ключ и встроенный внутрь модуля (см. рис. слева). Обратите внимание на то, что если вы прямо сейчас попытаетесь запустить shapeuser.exe, не откомпилировав его заново, то вы получите при- веденное ниже сообщение об ошибке, поскольку модуль shapes.dll больше не соответствует об- ращению к нему (внешняя ссылка указывает на частный модуль, a shapes теперь является общим модулем): Unhandled Exception: System.lO.FileLoadException: The located assembly's manifest (Необрабатываемая исключительная ситуация: System.IO.FileLoadException: Имеющееся определение) definition with name 'shapes' does not match the assembly reference. File name: "shapes" (манифеста модуля с именем 'shapes' не соответствует ссылке на этот модуль. Имя файла: "shapes") at ShapeUser.ShapeUser.MainQ Этой Ошибки Не ПроиЗОШЛО бы, если бы МЫ СОЗдали ShapeUser с помощью VS, поскольку VS создает частную копию модуля, а эта копия не претерпевает никаких изменений. Теперь необходимо ПОВТОРНО Откомпилировать файл shapeuser.es, ЧТО ПОЗВОЛИТ обновить ссылку на внешний модуль, находящуюся в shapeuser.exe в соответствии с помеченной версией shapes.dll. После повторной компиляции все будет рабо- тать как прежде, поскольку при этом используется локальная копия shapes.dll. В завершение необходимо скопировать модуль, который теперь обладает стро- гим именем, в кэш глобальных модулей (winNT\assembiy). В качестве альтернати- вы можно воспользоваться специальным инструментом командной строки .NET, который называется gacutii (Global Assembly Cache Utility — утилита для работы с кэшем глобальных модулей). Чтобы инсталлировать shapes.dll в этот глобаль- ный кэш, следует воспользоваться инструментом gacutii с флагом /i из команд- ной строки Visual Studio.NET: gacutii /i shapes.dll Полученное сообщение "Assembly successfully added to the cache" свидетельст- вует об успешной инсталляции данного модуля. При установке коммерческого приложения наиболее предпочтительным спосо- бом включения общего модуля в глобальный кэш является использование Windows Installer. Чтобы убедиться в том, что shapes.dll занесен в кэш, попробуйте удалить ко- пию shapes.dll ИЗ текущей Директории. Запустите файл shapeuser.exe, И ВЫ ПОЛу- чите следующее сообщение: Area of Circle(1.0) is 3.141596 610 Глава 21 Программа по-прежнему работает, несмотря на отсутствие shapes.dll, посколь- ку загрузка модуля shapes осуществляется из GAC. Для продолжения тестирова- ния ВОСПОЛЬЗуемСЯ gacutil ДЛЯ Обратной ИНСТаЛЛЯЦИИ shapes." gacutil /using shapes Обратите внимание на то, что для команды обратной инсталляции расширение .dii опускается. Теперь попробуйте еще раз запустить shapeuser: C:\shapes>shapeuser Unhandled Exception: System. 10.FileNotFoundException: File or assembly name shapes, or one of (Необрабатываемая исключительная ситуация: System.IO. FileNotFoundException: Имя файла или модуля shapes, либо файла) its dependencies, was not found. (или модуля, на которые они ссылаются, обнаружить не удалось.) Это свидетельствует о том, что shapes.dll был предварительно загружен из GAC. Поиск модулей Система выполнения программ .NET следует набору предварительно описан- ных правил для определения местонахождения внешнего модуля, если на него име- ется ссылка. Мы только что познакомились с тем, каким образом осуществляется поиск общих модулей сначала в локальной директории, а затем в GAC. Для частных модулей поиск сначала выполняется в локальной директории, а затем система осуществляет поиск в поддиректории с тем же именем, что и сам модуль. Система выполнения программ также осуществляет поиск файла DLL или ЕХЕ, который носит то же имя, что и искомый модуль. Для класса shapes сочета- ние этих правил дает следующий перечень просматриваемых директорий: ./shapes.DLL ./shapes/shapes.dll ./shapes/shapes.exe ./shapes.exe '••:• '••' ••••["[ '• ' : , , : [ '.'• ' ' \ . . V : ; : 7 ,••'.:'V : ' : ' • • ••' Дополнительные области поиска и даже адреса URL, по которым данный модуль может быть найден и загружен с удаленного местонахождения, могут задаваться посредством файла конфигурации. Файлы конфигурации для модулей представля- ют собой файлы в формате XML, определяющие правила, которые система выпол- нения программ .NET должна применять при поиске модуля. Файлы конфигурации также позволяют предопределять поведение, реализующееся при проверке номеров версий. Детальное описание файлов конфигурации и XML оказывается достаточно сложным и выходит за рамки настоящей книги. Для получения такой информации можно обратиться к документации по .NET Framework, доступной в режиме он- лайн, по теме "файлы конфигурации модуля". Итоги Программы на С# и библиотеки классов распространяются в виде модулей, которые обладают большим количеством возможностей, облегчающих распростра- нение компонентов Microsoft .NET Framework. Компоненты предоставляют возмож- ность повторного использования двоичных объектов. Очень важной особенностью .NET-компонентов является их самоописательность; это их самое главное их отли- чие от исторических предшественников (например, от СОМ-компонентов). Модули .NET 611 Свойство самоописательности обладает определенными преимуществами, среди которых простота инсталляции и совместного использования с Common Language Runtime (CLR — единая система выполнения программ), что позволяет одновре- менно с программированием на С# использовать различные другие языки про- граммирования и унаследованные программы. Нами был создан компонент, представляющий собой написанную на С# биб- лиотеку классов, который был затем откомпилирован в модуль с использованием как инструментария командной строки .NET, так и Visual Studio.NET. После этого мы создали приложение на С#, в котором данный компонент использовался, а также познакомились с тем, каким образом можно просматривать содержимое модулей с помощью iidasm. Это позволило нам лучше понять структуру модулей. Мы рассмотрели различные составные части модуля, включая манифест, номер версии и другие атрибуты модуля. Кроме лучшего понимания структуры модулей, это позволило нам научиться выполнять сравнение номеров версий в ссылках на внешние модули с целью отладки программ. Модули могут быть либо частными, относящимися только к некоторому конк- ретному приложению, либо общими, которые доступны всем приложениям, рабо- тающим в данной системе. Мы познакомились с тем, каким образом можно генерировать общие модули, создавая ключ файла и помечая этим ключом данный модуль, в результате чего создается строгое имя модуля. Мы познакомились также с тем, каким образом осуществляется проверка номеров версий для общих моду- лей, и рассмотрели понятие кэша глобальных модулей, в котором хранятся общие модули, а также различные инструменты, предназначенные для просмотра и вы- полнения манипуляций над ними. Мы рассмотрели компромисс между преимуществами проверки совместимости, обеспечения безопасности и эффективности использования ресурсов с одной сто- роны, и теми трудностями, к которым это приводит при создании и инсталляции модулей, с другой. В заключение мы коротко рассмотрели, каким образом система выполнения программ .NET осуществляет поиск модулей, к которым происходит обращение. Все эти возможности модулей существенно упрощают разработку, выполняе- мую в среде .NET. Упражнения • Измените НОМер Версии И Другие атрибуты МОДУЛЯ В Assemblylnfo.cs или shapes.cs. и понаблюдайте с помощью iidasm за тем, к каким изменениям в файле shapes.dll это приведет. • Создайте новый модуль moreshapes, в который — наряду с классами circle и Triangle — будет включен класс square (квадрат), а затем изучите его свойства с помощью iidasm. • Замените в shapeuser.exe ссылку на модуль, с тем чтобы воспользоваться модулем moreshapes.dll, а затем посмотрите в iidasm, какие изменения произошли со свойствами. • Создайте клиента для moreshapes, который будет использовать Объекты Square И Triangle наряду С объектом Circle. С помощью iidasm исследуйте модуль, созданный для этого клиента. 612 Глава 21 • Выведите список командных опций gacutii с помощью флажка /?, а затем воспользуйтесь его опциями для вывода перечня свойств всех глобальных модулей, которые имеются на вашей системе. • Создайте строгое имя для модуля moreshapes и пометьте его с помощью этого имени. Посмотрите на результат с помощью iidasm. Попробуйте запустить клиентскую программу, а затем откомпилируйте ее заново, включив ссылку на помеченный модуль. • Инсталлируйте moreshapes.dll в Global Assembly Cache С ПОМОЩЬЮ gacutii И Поэкспериментируйте С shapeuser и созданным вами клиентом, запуская их как совместно с модулем moreshapes, находящимся в локальной директории и/или в GAC, так и без него
|