Взаимодействие с CIL

46

Теперь, когда стало более понятно, как изнутри выглядит типичный файл CIL-кода, давайте завершим эксперимент с двунаправленным проектированием. Для этого предположим, что требуется обновить CIL-код в существующем файле *.il, следующим образом:

В первую очередь понадобится добавить новую директиву .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-кода в утилите ilasm.exe

После этого новое приложение можно запустить. Вполне очевидно, что теперь сообщение будет отображаться не в окне консоли, а в отдельном окне сообщения. Хотя вывод этого простого примера не является столь уж впечатляющим, один из практических способов применения двунаправленного программирования на языке CIL он действительно иллюстрирует.

Утилита peverify.exe

При создании либо изменении сборок за счет редактирования CIL-кода рекомендуется проверять, правильно ли оформлен скомпилированный двоичный образ согласно требованиям .NET, с помощью утилиты командной строки peverify.exe:

peverify NewProgram.exe

Эта утилита производит проверку корректности кодов операций CIL внутри указанной сборки. Например, в рамках CIL-кода стек вычислений должен всегда опустошаться перед выходом из функции. Даже если забыть извлечь оставшиеся значения, компилятор ilasm.exe сгенерирует сборку (поскольку компиляторы заботит только синтаксис). С другой стороны, утилита peverify.exe контролирует правильность семантики, и потому если стек не был очищен перед выходом из функции, она обязательно уведомит об этом, тем самым позволяя выявить проблему до запуска кодовой базы.

Пройди тесты
Лучший чат для C# программистов