Ассемблер под Windows №3

Приложение Windows, почему и зачем!


Доброго время суток, уважаемые подписчики. В этом выпуске я постараюсь подробно объяснить что мы делали в первой программе.


Вначале давайте разберёмся с архивами, которые я предлагаю вам скачать! Они не битые, просто запакованы WinRaR 3.0 и раннии версии их не открывают, могу конечно перейти и на zip, однако лучше скачайте WinRaR3, так как он архивирует лучше и скоро все будут пользоваться только им!


Как я уже говорил, код программы кажется полной глупостью, однако, если получше присмотреться, можно понять что зачем и почему! Я думаю зачем нужны файлы kernel32.inc и user32.inc все уже и так поняли, они содержат ссылки на процедуры одноимённых библиотек windows, поэтому первым будет подвержен детальному рассмотрению файл def.inc. Скорее всего многим из Вас уже знакома дериктива equ. При помощи этой дериктивы можно присвоить какому-то слову или выражению опредилённое значение. Это может быть очень полезно если программа общается с операционной системой при помощи закодированных сообщений. В этом случае нет нужды помнить какому номеру соответствует какое сообщение. В Windows общение програм происходит как раз путём передачи закодированных сообщений, поэтому мы каждому такому сообщению присвоим виртуальное название, а чтобы компилятор знал что мы имеем ввиду мы должны ему указать что есть что. Для этого мы и создаём файл def32.inc и записоваем как мы будем что называть. В случае Windows, Microsoft облегчила нам эти описания, официально назвав все значения. Нам остаётся только записать нужные нам значения.

IDI_APPLICATION equ 32512
IDC_ARROW       equ 32512
Мы используем это при инициализации мышки и иконки в нашем приложении
WM_DESTROY equ 2
Сообщение о закрытии окна
CS_HREDRAW          equ 2
CS_VREDRAW          equ 1
CW_USEDEFAULT       equ 80000000h
WS_OVERLAPPEDWINDOW equ 0CF0000h
SW_SHOWNORMAL       equ 1
Параметры окна
СOLOR_WINDOW equ 5
Цвет окна
WNDCLASSEX struc
 cbSize        dd ? ;размер структуры
 style         dd ? ;стиль класса
 lpfnWndProc   dd ? ;смещение процедуры окна
 cbClsExtra    dd ? ;дополнительные байты класса
 cbWndExtra    dd ? ;дополнительные файлы для каждого окна класса
 hInstance     dd ? ;handle модуля с которым будет связать класс
 hIcon         dd ? ;иконка окон класса
 hCursor       dd ? ;курсор окон класса
 hbrBackground dd ? ;цвет окон класса
 lpszMenuName  dd ? ;идентификатор меню в ресурсах
 lpszClassName dd ? ;имя класса
 hIconSm       dd ? ;маленькая иконка
WNDCLASSEX ends
А вот это структура нашего окна.
MSG struc
 hwnd    dd ? ;окно - получатель сообщения
 message dd ? ;сообщение
 wParam  dd ? ;параметр сообщения
 lParam  dd ? ;параметр сообщения
 time    dd ? ;время отправки сообщения
 pt      dq ? ;позиция мышки в момент посылки сообщения
MSG ends
И структура сообщения, которое получает наше окно!
Надеюсь понятно, о структурах поговорим при разборе кода, который следует прямо сейчас!
В начале мы добавляем файлы дополнения,
include def32.inc
include user32.inc
include kernel32.inc
Следующей строкой, .386, даём компилятору знать, что мы собираемся использовать возможности появившиеся в 386 - ом процессоре!
Указываем модель памяти, которая для Windows почти всегда flat.
   .model flat
Указываем константы,
   .const
class db "window class 1",0
name_ db "Da window!",0
Указываем переменные,
   .data
wc  WNDCLASSEX<48,CS_HREDRAW or CS_VREDRAW,offset win_proc,0,0,?,?,?,color_window+1,0,offset class,0>
   .data?
msg_ MSG <?,?,?,?,?,?>
При указании структуры, после вида структуры в трехугольных скобках указываются параметры структуры. Рассмотрим структуру окна, первая переменная, размер окна, в нашем случае это 12 переменных по 4 байта каждая, поэтому размер структуры равен 48 или 12*4. Далее идёт стиль окна, в этой переменной каждый бит опредилят нужность/ненужность опредилённого действия, мы выставляем первый и второй биты. Следующая переменная опредиляет адрес процедуры, которая будет обрабатывать все сообщения Windows, эту процедуру мы должны написать сами! Следующие два параметра нам пока не понадобятся, поэтому оставим их на будущее. Следующие 3 двойных слова: указатель на программу-создателя, иконка и мышь. Они нам пока не известны, они будут заполнены в процессе выполнения программы. дальше двойное слово - цвет окна, 6! далее, имя меню, пока у нас нет меню, 0. Следующие два двойных слова: Имя окна, класс окна. Последнее на ненужен(пока;). Сообщения к нашему окну начнут приходить после его инициализации, поэтому все переменные этой структуры нам ещё не известны!
Далше начинаетсясам код программы, начинается он строкой .code!
Сразу после следует начальная метка, _start:. Отсюда и начинается программа.
Следующей строчкой, xor ebx,ebx мы обнуляем ЕВХ, для того чтоб когда нам понадобится записать команду "push 0", мы сможем заменить её командой "push ebx", которая в два раза короче и выполняется в 4 раза быстрее!
следующие две строки:
push ebx
call GetModuleHandle
В Windows все параметры процедурам передаются через стек, тоесть мы должны поместить в стек все параметры и вызвать процедуру, она их обработает и удалит при выходе. Процедура GetModuleHandle при входе должна получать номер модуля и возвращает так называемый указатель, при помощи которого можно работать с объектом в среде Windows. Если номер модуля 0, то процедура GetModuleHandle возвращает указатель на текущий модуль. Это мы и делаем, при этом получая указатель на нашу программу.
Следующим же шагом мы помещаем его в esi, mov esi,eax, чтобы потом можно было использовать esi как указатель на нашу программу.Следует заметить, что процедуры Windows обычно возвращают значение в eax!

Теперь начинаем создание нашего окна! В начале надо дополнить нашу структуру окна, вернее те её пункты, которые не всегда одинаковы.
В начале всего установим создателем окна нашу программу, mov dword ptr wc.hInstance,eax.
Затем загружаем обычную иконку Windows, и определяем её окну
push IDI_APPLICATION
push ebx
call LoadIcon
mov wc.hIcon,eax
Также мы поступаем с курсором,
push idc_arrow
push ebx
call LoadCursor
mov wc.hCursor,eax
Теперь наша структура готова, регистрируем её,
push offset wc
call RegisterClassEx
Для регистрации помещаем в стек адрес нашей структуры параметров окна и вызываем RegisterClassEx.
Далее надо создать окно. Так как это всего лишь показательнаю программа, не будем особо мучится и выставим позицию и размеры окна по умолчанию, сейчас они не имеют для нас особого значения. Чтобы выставить параметр по умолчанию надо за место параметра послать значение, которое мы назвали CW_USEDEFAULT, и так как нам придётся несколько раз подрят помещать это значение в стек, то лучше поместить это значение в регистр, например ecx, а потм уже помещать в стек этот регистр! Итак помещаем в стек параметры окна и создаём его,
mov ecx,CW_usedefault
push ebx
push esi
push ebx
push ebx
push ecx
push ecx
push ecx
push ecx
push ws_overlappedwindow
push offset name_
push offset class
push ebx
call CreateWindowEx
После вызова CreateWindowEx мы получаем в eax указатель на наше окно.
А теперь мы совершаем маленький трюк, используя то, что параметры передаются процедурам через стек. Код следующий:
push eax
push SW_SHOWNORMAL
push eax
call ShowWindow
call UpdateWindow
Итак, вначале мы помещаем в стек указатель на наше окно, потом параметр показа окна, потом ещё раз указатель, и вызываем две процедуры, при этом каждая из них получает указатель на окно. Я Вас ещё не окончательно запутал!? А теперь давайте посмотрим, мы помещаем в стек три параметра, а процедура ShowWindow использует (и в конце удаляет) только 2, тоесть третий остаётся нетронутым и блогополучно передаётся процедуре UpdateWindow


Дальше продолжим в следующий раз. Если что пишите, Dark_Lord@land.ru, Dark_Lord@RusFAQ.ru.

Сайт управляется системой uCoz