Программирование на Visual C++. Архив рассылки
Шрифт:
Вы должны увидеть то же самое, что и на рисунке. Древовидная структура (далее просто дерево) показывает вам сборку изнутри.
[…]
ПРИМЕЧАНИЕ
Тип по значению (Value Type) задается ключевым словом struct и отличается от класса тем, что размещается в стеке, а не в динамической памяти.
Поэкспериментируйте немного с ildasm, чтобы привыкнуть к этой программе. Не пугайтесь при виде каких ни будь непонятных данных, дальше будет еще страшнее. :)
Теперь откройте манифест (manifest) и внимательно посмотрите. Ниже я привожу содержание манифеста, полученное мной при помощи утилиты ildasm.
Что, вам кажется, что это полная чушь? Ошибаетесь, если в этом разобраться, что, кстати, не так уж и трудно, то вам откроется много очень полезной и порой необходимой информации. В начале вы увидите записи со словами .assembly extern, которые описывают зависимости от внешних сборок, необходимых для функционирования этой программы. А данные, идущие далее в блоке, заключённом в фигурных скобках, описывают версию и контрольную сумму сборки. Эти данные берутся из сборок при компиляции программы, что гарантирует использование именно тех сборок, которые использовались при компиляции и тестировании. Далее следует .assembly, но уже без модификатора extern. С этой директивы и начинается описание нашей с вами сборки. Как вы могли догадаться, .ver описывает версию нашей сборки. Ну а .hash algorithm определяет функцию, по которой будет вычисляться хэш, но об этом я расскажу позднее. Затем идут описания имени самого модуля, подсистемы исполнения, информация о выравнивании секций и еще некоторые данные. Полная документация по этому вопросу находится в Framework SDK. Более подробно об устройстве манифеста я расскажу далее.
ПРИМЕЧАНИЕ
На самом деле, .publickeytoken описывает не контрольную сумму файла, а является хэшем (контрольной суммой) публичного ключа автора, создавшего сборку, на которую ссылается ключ .assembly extern.
Как же сборка устроена изнутри? Что у нее "под капотом"? Оказывается, не так все и страшно, как вам могло показаться. Сборка помещается внутри файла в формате PE (Portable Executable), то есть внутри DLL или EXE. Здесь все зависит от того, будет ли сборка самостоятельной программой или "библиотекой". Любая сборка импортирует функции из библиотеки mscoree.dll,
которая является частью среды исполнения. Исполняемые файлы (EXE) импортируют из этой библиотеки функцию _CorExeMain, которую они вызывают для своего запуска. А происходит это так: как и в любом exe-файле, в нашем присутствует точка входа - это маленькая функция (6 байт), которая призвана передавать управление функции _CorExeMain из библиотеки mscoree.dll. Когда данная функция получает управление, она находит в exe-файле свою точку входа и начинает выполнение с нее. Вы можете проверить все сказанное мною сами при помощи утилиты dumpbin, запустив ее с параметрам /imports. Правда, у файла, скомпилированного на Managed C++, вы можете увидеть много других импортов. Не пугайтесь, это нормально, так как MC++ одновременно поддерживает как управляемые, так и неуправляемые данные (managed/unmanaged data). А значит, может делать самостоятельные системные вызовы в обход CLR.Динамически загружаемые библиотеки импортируют функцию _CorDllMain, которую они вызывают из DllMain, "точки входа" DLL. Сама же функция точки входа ничего, кроме вызова _CorDllMain, не делает. С exe– сборками дело обстоит аналогичным образом, только вместо _CorDllMain, импортируется функция _CorExeMain.
Вот как выглядят точки входа в dll– в exe-сборки.
Dll сборка | Exe сборка |
---|---|
start proc near | start proc near |
jmp ds:CorDllMain | jmp ds:CorExeMain |
start endp | start endp |
В будущих версиях Windows Microsoft планирует изменить процесс загрузки, при котором реальная точка входа будет находиться не таким странноватым способом. Вы спросите: а почему нельзя было сделать сразу, нормально? Это на самом деле не такой простой вопрос, как кажется. Ведь новые исполняемые файлы, по сути дела, со старыми ничего общего не имеют, а надо сделать так, чтобы они могли запускаться существующими версиями Windows без изменения загрузчика файлов. Microsoft пошла по давно известному пути "оборачивание нового в старое", то есть сам exe-файл является оберткой, для реального исполняемого файла CLR. Благодаря этому подходу файлы могут запускаться на уже существующих версиях Windows без изменения их самих.
Сборка может состоять из следующих частей: манифест – метаданные, описывающие сборку, описание типов, код в виде промежуточного языка (IL) и набор ресурсов. Не все из них должны обязательно присутствовать в каждой сборке, кроме, разумеется, манифеста. Сборка также может быть разбита на несколько файлов. Для большей наглядности я нарисовал, как это все происходит.
Рис. 2
Как вы можете видеть, за пределами сборки можно помещать не только другие специальные файлы, которые тоже являются сборками, а любые другие файлы, какие вам захочется, к примеру, картинки. Это дает несколько преимуществ. Во-первых, вы можете обновлять отдельные файлы сборки, не прибегая к компиляции, во-вторых, как я уже говорил ранее, не имеет никакого значения, где размещены эти файлы. Внешние файлы подгружаются только по мере надобности, что позволяет оптимизировать сетевой трафик, если часть приложения находиться в сети. Тут даже можно немного схитрить: сделать основной сборку которая не несет в себе ничего, кроме зависимостей. Тогда вы сможете поставить вашу программу в малюсеньком файле, который, по мере надобности, скачает все, что ему нужно, из сети.
По умолчанию COM-компоненты общедоступны. Я имею в виду, что для них присутствуют записи в реестре, с которым любой может делать все, что захочет, а также то, что большинство приложений хранит свои библиотеки в системных директориях, хотя Microsoft и не советует этого делать. То есть ни о какой изолированности приложений говорить не приходилось. Потенциально, любое приложение может быть приведено в неработоспособное состояние при обстоятельствах, никак от него не зависящих.