Jenter Алекс
Шрифт:
Вы должны увидеть то же самое, что и на рисунке. Древовидная структура (далее просто дерево) показывает вам сборку изнутри.
[…]
ПРИМЕЧАНИЕ
Тип по значению (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 |