В этой главе Вы
научитесь создавать нормальное Windows-приложение,
отображающее окно сообщения (messagebox) с текстом:
"Программировать на Ассемблере под Win32 очень
просто!".
Windows cодержит очень много
ресурсов для программ. Центральное место в них
занимает Win32 API. Windows API это набор очень полезных и
готовых к использованию любой программой
функций, расположенных внутри Windows и
функционально объединенных в DLL-файлах, таких как
Kernel32.dll, User32.dll и Gdi32.dll. Kernel32.dll содержит API-функции,
работающие с памятью и управляющие процессами.
User32.dll управляет интерфейсом Вашей программы.
Gdi32.dll содержит графические функции. Кроме трех
основных существуют и другие библиотеки,
информация о них вам также доступна.
Windows-приложение
динамически связано с dll-файлами, т.е. код
API-функции не включается в исполняемый файл, в это
место помещается команда безусловного перехода
в то место соответствующей библиотеки, где
находится данная функция. Для того, чтобы Ваша
программа смогла найти необходимые API-функции в
процессе работы, Вы должны включить некоторую
информацию в .exe файл. Необходимая информация
находится в библиотечных импортируемых функций.
Вы должны связать свою программу с верной
библиотекой импортируемых функций, иначе она не
сможет найти необходимую функцию.
Существует два типа
API-функций: ANSI и Unicode. Имена ANSI функций
оканчиваются символом "А", например MessageBoxA.
Имена Unicode функций оканчивается символом "W"
(Wide char), например MessageBoxW. Windows 95 в основном
использует ANSI функции, Windows NT - Unicode.
Чаще всего, Вы будете
использовать включаемый файл, который в
зависимости от платформы позволит определить,
какой тип API-функций написать. Поэтому, в своей
программе используйте имена функций без
окончаний "А" и "W", например MessageBox.
Я хотел бы привести
структуру всех Windows-приложений:
.386
.model flat, stdcall
.data
.code
Main:
end Main
А теперь давайте
попытаемся постепенно заполнить ее остальными
необходимыми строками.
Любое Windows-приложение
должно вызывать API-функцию ExitProcess для завершения
программы. В заголовочном файле языка Си winbase.h мы
можем видеть следующий прототип этой функции:
void WINAPI ExitProcess(UINT uExitCode);
Тип void означает, что
функция ничего не возвращает. WINAPI означает
использование способа передачи аргументов в
функцию - stdcall. UINT - тип данных, беззнаковое
целое число, разрядностью 32-бит. uExitCode -
значение, передаваемое в Windows после завершения
программы. Это значение в Windows не используется.
Чтобы вызвать ExitProcess из
программы на Ассемблере, Вы должны определить ее
прототип:
.386
.model flat, stdcall
ExitProcess PROTO ,:DWORD
.data
.code
Main:
INVOKE ExitProcess, 0
end Main
Это Ваше первое
Windows-приложение. Сохраните его в файле msgbox.asm.
Затем попробуйте ассемблировать этот файл с
помощью программы-ассемблера ml.exe (удостовертесь
в наличии пути к этому файлу в Вашем autoexec.bat) с
помощью следующей строки:
ml.exe /c /coff /Cp msgbox.asm
/c сообщает MASM, что необходимо
только ассемблировать файл, не производя
линковку с помощью link.exe
/coff сообщает MASM, что файлы с расширением .obj
должны создаватся в формате COFF
/Cp сообщает MASM, что он должен различать в
файле строчные и прописные буквы (в именах
функций, переменных и т.д.)
После этого произведем
линковку (связывание) Вашего приложения с
помощью команды link.exe:
link /SUBSYSTEM:WINDOWS /LIBPATH:c:masm611lib msgbox.obj
kernel32.lib
/SUBSYSTEM:WINDOWS сообщает Link, о типе
создаваемого .exe файла
/LIBPATH:<path to import library> сообщает Link, где находятся
библиотеки импортируемых функций. На моем
компьютере это c:masm611lib
Ассемблирование и
линковка вместе называются компиляцией.
Итак, мы получили
приложение msgbox.ехе. Пробуем его запустить... но
ничего не происходит. Не волнуйтесь,
единственное, что пока делает наше приложение -
закрывается после старта. Но все-таки это
нормальное Windows-приложение. Обратите внимание на
размер ехе-файла - 1,536 байт!
Вы определяете функцию
с помощью следующей строки:
ExitProcess PROTO, :DWORD,
а потом записываете список типов
параметров функции. MASM использует прототип
функции для проверки количества и типа
параметров, передаваемых функцию. Лучшим местом
для прототипов функций является включаемый файл
(include file). Вы можете поместить в этот файл описание
части используемых функций и структур данных и
включаете его в начале вашей программы на
Ассемблере.
Вызов функции в программе
осуществляется с помощью ключевого словa INVOKE:
INVOKE ExitProcess, 0
INVOKE является особым типом вызова
функции. При его использовании проверяется
количество и тип передаваемых аргументов, и они
передаются в стек согласно выбранному по
умолчанию способу вызова функций (в нашем случае
используется stdcall). Используя INVOKE вместо обычного
вызова, вы освободитесь от стековых ошибок при
неверной передаче аргументов. Это очень удобно!
Для создания окна
сообщения используется фукнция MessageBoxA.
Определение этой функции выглядит следующим
образом:
int WINAPI MessageBoxA(HWND hwnd, LPCSTR lpText, LPCSTR lpCaption,
UINT uType),
где hwnd - указатель на родительское
окно
lpText - указатель на строку текста, который Вы
хотите вывести в области отображения окна
сообщения
lpCaption - указатель на текст заголовка окна
сообщения
uType определяет тип иконки, а также
количество и вид кнопок в окне сообщения
В Win32 HWND, LPCSTR и UINT имеют
разрядность 32 бит.
Давайте изменим файл msgbox.asm включив в
него вызов функции MessageBoxA:
.386
.model flat, stdcall
ExitProcess PROTO ,:DWORD
MessageBoxA PROTO ,:DWORD, :DWORD, :DWORD, :DWORD
.data
MsgBoxCaption db "Пример окна сообщения",0
MsgBoxText db "Программировать
на Ассемблере под Win32 очень просто!",0
.const
NULL equ 0
MB_OK equ 0
.code
Main:
INVOKE MessageBoxA, NULL, ADDR MsgBoxText, ADDR
MsgBoxCaption, MB_OK
INVOKE ExitProcess, NULL
end Main
Скомпилируйте программу,
как мы это уже сделали выше. Но в данном случае
при линковке необходимо в конце строки добавить
user32.lib, т.к. описание функции MessageBoxA находится
именно в этом файле. После компиляции, запустите
программу - Вы увидите окно сообщения с текстом
"Программировать на Ассемблере под Win32 очень
просто!".
Вернемся опять к исходному
тексту программы. В секции .data мы определили две
строки, закрытые нулем. Запомните: в Windows все
строки должны быть закрыты нулем (двоичным, т.е.
числом 0, а не символом "0"). В секции .const мы
определили две константы. Мы использовали
константы для большей понятности программы (константа
MB_OK больше говорит о типе окна, чем число 1,
хотя по сути это одно и тоже).
И на последок о аргументах функции MessageBoxA.
Первый аргумент - NULL, говорит о том, что окно
сообщения не имеет родителя. Оператор ADDR
используется, когда необходимо передать не
значение переменной, а ее адрес. Последний
аргумент - MB_OK, сообщает о том, что окно будет
иметь иконку в виде восклицательного знака и
кнопку OK. |