Взаимодействие с CIL
46C# --- Сборки .NET --- Взаимодействие с CIL
Теперь, когда стало более понятно, как изнутри выглядит типичный файл CIL-кода, давайте завершим эксперимент с двунаправленным проектированием. Для этого предположим, что требуется обновить CIL-код в существующем файле *.il, следующим образом:
добавить ссылку на сборку System.Windows.Forms.dll;
загрузить локальную строку в методе Main();
вызвать метод System.Windows.Forms.MessageBox.Show(), принимающий локальную строковую переменную в качестве аргумента.
В первую очередь понадобится добавить новую директиву .assembly (с атрибутом extern), указывающую на то, что нашей сборке требуется сборка System.Windows.Forms.dll. Для этого достаточно вставить в файл *.il сразу же после ссылки на внешнюю сборку mscorlib следующий код:
.assembly extern System.Windows.Forms
{
.publickeytoken = (B7 7A 5C 56 19 34 EO 89)
.ver 4:0:0:0
}
Значение, присваиваемое директиве .ver, зависит о установленной версия платформы .NET. Здесь используемая сборка System.Windows.Forms.dll относится к версии 4.0.0.0 и имеет значение открытого ключа b77a5c561934e089. Открыв кэш GAC и отыскав там версию сборки System.Windows.Forms.dll, можно подставить нужный номер версии и значение открытого ключа.
Теперь необходимо изменить текущую реализацию метода Main(). Для этого следует найти этот метод внутри файла *.il и удалить текущий код его реализации (оставив только директивы .maxstack и .entrypoint):
Требуется поместить новую переменную string в стек и вызвать метод MessageBox.Show() (вместо Console.WriteLine()). Вспомните, что при указании имени внешнего типа должно использоваться его полностью квалифицированное имя (вместе с дружественным именем сборки). Обратите внимание, что в CIL при вызове каждого метода необходимо обязательно полностью указывать возвращаемый тип. В результате метод Main() должен приобрести следующий вид:
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Размер кода: 19 (0x13)
.maxstack 8
ldstr "Всем привет, это сайт professorweb.ru"
call valuetype [System.Windows.Forms]
System.Windows.Forms.DialogResult
[System.Windows.Forms]
System.Windows.Forms.MessageBox::Show(string)
pop
ret
} // end of method Program::Main
Компиляция CIL-кода с помощью ilasm.exe
После сохранения измененного файла *.il можно приступать к компиляции новой сборки .NET за счет применения утилиты ilasm.exe (компилятора CIL). Эта утилита поддерживает множество опций командной строки (для их просмотра укажите при запуске опцию -?), наиболее интересные из которых описаны ниже:
- /debug
Включение отладочной информации (такую как имена локальных переменных и аргументов, а также номера строк)
- /dll
Генерация в качестве вывода файла *.dll
- /exe
Генерация в качестве вывода файла *.exe. Используется по умолчанию, поэтому может быть опущена
- /key
Компиляция сборки со строгим именем с использованием заданного файла *.snk
- /output
Имя и расширение выходного файла. Если флаг /output не указан, результирующее имя файла (без расширения) совпадает с именем первого исходного файла
Чтобы скомпилировать измененный файл Program.il в новую .NET-сборку *.exe, необходимо ввести в окне командной строки Visual Studio 2010 следующую команду:
ilasm /exe Program.il /output=NewProgram.exe
Если все прошло успешно, на экране должен быть следующий отчет:
После этого новое приложение можно запустить. Вполне очевидно, что теперь сообщение будет отображаться не в окне консоли, а в отдельном окне сообщения. Хотя вывод этого простого примера не является столь уж впечатляющим, один из практических способов применения двунаправленного программирования на языке CIL он действительно иллюстрирует.
Утилита peverify.exe
При создании либо изменении сборок за счет редактирования CIL-кода рекомендуется проверять, правильно ли оформлен скомпилированный двоичный образ согласно требованиям .NET, с помощью утилиты командной строки peverify.exe:
peverify NewProgram.exe
Эта утилита производит проверку корректности кодов операций CIL внутри указанной сборки. Например, в рамках CIL-кода стек вычислений должен всегда опустошаться перед выходом из функции. Даже если забыть извлечь оставшиеся значения, компилятор ilasm.exe сгенерирует сборку (поскольку компиляторы заботит только синтаксис). С другой стороны, утилита peverify.exe контролирует правильность семантики, и потому если стек не был очищен перед выходом из функции, она обязательно уведомит об этом, тем самым позволяя выявить проблему до запуска кодовой базы.