ЯЗЫК ПРОГРАММИРОВАНИЯ С# 2005 И ПЛАТФОРМА .NET 2.0. 3-е издание - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
.method public hidebysig specialname rtspecialname instance void .ctor cil managed {
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
Роль меток в программном коде CIL
Вы. конечно, заметили, что в каждой строке программного кода реализации содержится префикс в форме лексемы IL_XXX: (например, IL_0000: IL_0001: и т.д.). Эти лексемы называются метками кода, и они могут иметь любой вид, какой вы только пожелаете (лишь бы они не дублировались в пределах одного и того же контекста). При записи содержимого компоновочного блока в файл с помощью ildasm.exe автоматически генерируются метки кода, имеющие вид IL_XXX:. Но вы можете изменить их с тем, чтобы они стали более информативными.
.method private hidebysig static void Main(string[] args) cil managed {
.entrypoint
.maxstack 8
Nothing_1: nop
Load_String: ldstr "Hello CIL code!"
PrintToConsole: call void [mscorlib]System.Console::WriteLine(string)
Nothing_2: nop
WaitFor_KeyPress: call string [mscorlib] System.Console::ReadLine()
RemoveValueFromStack: pop
Leave_Functlon: ret
}
Суть в том, что большинство меток кода совсем необязательно. Единственным случаем, когда метки кода оказываются по-настоящему полезными (и обязательными), является случай, когда в программном коде CIL используются ветвления или циклические конструкции. Например, в нашем случае вы можете исключить метки вообще.
.method private hidebysig static void Main(string[] args) cil managed {
.entrypoint
.maxstack 8
nop
ldstr "Hello CIL code!"
call void [mscorlib]System.Console::WriteLine(string)
nop
call string [mscorlib]System.Console::ReadLine()
pop
ret
}
Взаимодействие с CIL: модификация файла *.il
Теперь, когда вы понимаете, как компонуется базовый файл 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 E0 89)
.ver 2:0:0:0
}
Значение, указанное директивой .ver. может у вас оказаться другим, поскольку оно зависит от версии платформы .NET, установленной на вашей машине. Здесь указано использование System.Windows.Forms.dll версии 2.0.0.0 с кодом открытого ключа В77А5С561934Е089. Если открыть GAC (см. главу 11) и найти там компоновочный блок System.Windows.Forms.dll, можно скопировать правильный номер версии и значение открытого ключа со страницы свойств этого компоновочного блока.
.method private hidebysig static void Main(string[] args) cil managed {
.entrypoint
.maxstack 8
// Задача: написать новый CIL-код.
}
Итак, целью является помещение новой строки в стек и вызов метода MessageBox.Show() (а не метода Console.WriteLine()). Напомним, что при указании имени внешнего типа следует использовать абсолютное имя типа (в совокупности с понятным именем компоновочного блока). С учетом этого обновите метод Main() так, как показано ниже.
.method private hidebysig static void Main(string[] args) cil managed {
.entrypoint
.maxstack 8
ldstr "CIL работает прекрасно!"
call valuetype [System.Windows.Forms] System.Windows.Forms.DialogResult [System.Windows.Forms] System.Windows.Forms.MessageBox::Show(string)
pop
ret
}
В результате вы получите программный код CIL, соответствующий следующему определению класса C#.
public class Program {
static void Main(string[] args) {
System.Windows.Forms.MessageBox.Show("CIL работает прекрасно!");
}
}
Компиляция CIL-кода с помощью ilasm.exe
Сохранив измененный файл *.il, вы можете скомпилировать новый компоновочный блок .NET, используя для этого утилиту ilasm.exe (компилятор CIL). Возможно, вы удивитесь тому, что компилятор CIL имеет гораздо меньше опций командной строки, чем компилятор C#. В табл. 15.1 приводятся их описания.
Таблица 15.1. Опции командной строки ilasm.exe
Опция Описание /debug Включает информацию отладки (такую как имена локальных переменных и аргументов, а также номера строк) /dll Создает выходной файл" *.dll /exe Создает выходной файл *.exe. Это значение устанавливается по умолчанию, поэтому его можно опустить /key Компилирует компоновочный блок со строгим именем, используя заданный файл *.snk /noautoinherit Запрещает автоматическое наследование типов класса из System. Object, когда конкретный базовый класс не определен /output Указывает имя и расширение выходного файла. Если флаг /output не используется, имя выходного файла будет соответствовать имени первого исходного файлаЧтобы откомпилировать обновленный файл simplehelloclass.il в .NET-файл *.exe, в командном окне Visual Studio 2005 выполните следующую команду.
ilasm.exe HelloProgram.il
Если все пройдет без сбоев, вы должны получить вывод, подобный показанному на рис. 15.1.
Рис. 15.1. Компиляция файлов *.il с помощью ilasm.exe
После этого вы сможете выполнить свое новое приложение. Достаточно очевидно, что теперь вместо сообщения в окне консоли вы должны увидеть окно Windows с вашим сообщением (рис. 15.2).
Рис. 15.2. Результат челночной технологии
Компиляция CIL-кода с помощью SharpDevelop
Для работы с файлами *.il вы можете использовать бесплатную среду разработки SharpDevelop (см. главу 2). При создании нового "комбината" (для этого выберите File→New Combine из меню), одним из вариантов выбора является создание рабочего пространства CIL-проекта. Хотя SharpDevelop пока что не предлагает поддержку IntelliSense для CIL-проектов, лексемы CIL выделяются цветом, и вы получаете возможность компилировать и выполнять свои приложения непосредственно в окне IDE (а не в командной строке, как в случае ilasm.exe).
Компиляция CIL-кода с помощью ILIDE#
Если вам интересно поэкспериментировать с языком программирования CIL, рекомендую загрузить самую последнюю версию бесплатного редактора исходных текстов CIL, имеющего открытый код и название ILIDE#. Этот инструмент, подобно SharpDevelop, обеспечивает выделение цветом ключевых слов и программных структур, интеграцию с ilasm.exe и набор соответствующих инструментов. В отличие от SharpDevelop, последняя версия ILIDE# поддерживает IntelliSense для CIL. Установщик ILIDE# можно загрузить со страницы http://ilide.aspfreeserver.com/default-en.aspx (этот адрес URL может измениться). На рис. 15.3 показано окно ILIDE# в действии.
Рис. 15.3. Редактор ILIDE# – бесплатная среда разработки для CIL
Роль peverify.exe
При создании или модификации компоновочных блоков, в которых используется программный код CIL, всегда целесообразно проверить, будет ли скомпилированный двоичный образ правильно сформирован с точки зрения правил .NET. Для этого можно использовать средство командной строки peverify.exe.
peverifу HelloProgram.exe
Этот инструмент проверит все коды операций в указанном компоновочном блоке на соответствие правилам CIL. Например, в терминах CIL-кода стек оценок должен всегда опустошаться: перед выходом из функции, Если вы забудете извлечь из него какие-то значения, компилятор ilasm.exe все равно сгенерирует допустимый компоновочный блок (поскольку компиляторы "заботятся" только о синтаксисе).
А вот peverifу.exe, с другой стороны, заботится о семантике. Если вы забудете очистить стек перед выходом из функции, peverify.exe сообщит вам об этом.
Исходный код. Файл HelloProgram.il размещен в подкаталоге, соответствующем главе 15.
Директивы и атрибуты CIL
Теперь, когда вы знаете, как использовать ildasm.exe и ilasm.exe в рамках челночной технологии разработки, мы можем заняться непосредственным анализом синтаксиса и семантики CIL. Следующие разделы предлагают описание процесса построения пользовательского пространства имен, содержащего определенный набор типов. Чтобы упростить рассмотрение, эти типы не будут содержать никакого программного кода реализации их членов. После того как вы поймете, как создаются пустые типы, вы сможете сосредоточить все свое внимание на процессе создания "реальных" членов типа с помощью кодов операций CIL.