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

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


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


Итак, в предидущем выпуске рассылки мы закончили обсуждать нашу программу на создании и выводе на дисплей нашего окна. Продолжим:
mov edi,offset msg_
Этой строкой мы заносим адрес переменной сообщений в edi, так как при обработке сообщений нам придётся несколько раз помещать этот адрес в стек, а "push edi" быстрее и компактнее команды "push offset msg_".
main_:
Начало нашего главного цикла обработки сообщений.
push ebx
push ebx
push ebx
push edi
call GetMessage
При вызове GetMessage эта процедура забирает из стека 4 параметра и использует их как первое сообщение, второе сообщение, объект, к которому принадлежат сообщения и адрес структуры сообщений. Мы используем её с параметрами 0,0,0,offset msg_, то есть принимаем все сообщения от всех объектов данной задачи в структуру msg_!
test eax,eax
jz exit_
Если пользователь закрывает приложение, то эта процедура возвращает 0, поэтому стоит устроить проверку на 0, и в случае равенства выхода.
push edi
call TranslateMessage
push edi
call DispatchMessage
Эти две процедуры расшифровывают сообщение, при входе надо поместить в стек адрес структуры сообщения.
jmp main_
Этой строкой мы зацикливаем процесс обработки сообщений, то есть сообщения будут непрерывно, или насколько позволит скорость процессора, повторятся. Наверняка у многих из Вас появился вопрос, а как работает процедура окна, когда о ней даже не упоминается. Спешу ответить на этот вопрос, в Windows каждая часть приложения выполняется как отдельная задача. Заглянем назад, при инициализации переменных и структур мы записали в структуру нашего окна адрес процедуры окна! В связи с этим при создании окна Windows создал отдельную задачу, в которую поместил нашу процедуру обработки окна. Наша программа и процедура окна выполняются параллельно, а так как обе ссылаются на одну и ту же структуру сообщений, то сообщения передаются через неё. При этом при завершении программы мы должны отдельно закрыть каждую часть нашего приложения.
exit_:
push ebx
call ExitProcess
Вот и само завершения программы, но не процедура окна. Единственное, что хотел бы здесь отметить, что в отличии от DOS в Windows программа завершается не директивой ret, которая передаёт управление программе терминатору, а вызовом процедуры терминатора напрямую.
win_proc proc
  push ebp
  mov ebp,esp
wp_hWnd   equ dword ptr [ebp+08h]
wp_uMsg   equ dword ptr [ebp+0Ch]
wp_wParam equ dword ptr [ebp+10h]
wp_lParam equ dword ptr [ebp+14h]
  cmp wp_uMsg,WM_DESTROY
  jne not_
  push 0
  call PostQuitMessage
  jmp end_
not_:
  leave
  jmp DefWindowProc
end_:
  leave
  ret 16
win_proc endp
  end _start

А это наша процедура окна! Рассмотрим подробно каждую её строку!

push ebp
mov ebp,esp
Этими двумя командами мы создаём так называемый стековой кадр для нашей процедуры. На самом деле мы помещаем в стек значение ebp, а потом сохраняем размер стека в ebp, после этого мы спокойно можем использовать стек, с условием, что перед завершением процедуры мы загрузим размер стека обратно в esp! Следовательно, мы не должны изменять ebp в течении процедуры!
wp_hWnd   equ dword ptr [ebp+08h]
wp_uMsg   equ dword ptr [ebp+0Ch]
wp_wParam equ dword ptr [ebp+10h]
wp_lParam equ dword ptr [ebp+14h]
Эти четыре строки в программе не участвуют, но существенно облегчают нашу работу!
cmp wp_uMsg,WM_DESTROY
jne not_
Так как в этом примере наше окно ничего не делает, то единственное действие, которое может произойти с ним это закрытие. Поэтому стоит поставить проверку на закрытие (сообщение windows wm_destroy). Если окно не было закрыто, то переходим на метку not_,
push 0
call PostQuitMessage
jmp end_
Если же оно было закрыто, то помещаем в стек 0 и сообщаем об этом Windows при помощи процедуры PostQuitMessage.
not_:
leave
jmp DefWindowProc
Если сообщение не получено, то управление передаётся на эту метку. Команда leave эквивалентна двум командам, mov esp,ebp и pop ebp, то есть она возвращает esp и ebp их изначальные значения! Так как полная обработка окна (вырисовка на экран и т.д.) выполняется Windows, то надо не завершить программу, а передать управление на процедуру окна, jmp DefWindowProc.
end_:
leave
ret 16
А при завершении процедуры нам нужно ещё убрать структуру сообщений из стека, а так как она занимает 4 двойных слова, что равно 16 байтам, это за нас сделает директива ret 16!

Вот вроде и всё! Если есть какие вопросы, пишите, обязательно отвечу. В следующем уроке мы повторим MessageBox и полатаем нашу программу! Пишите, Dark_Lord@RusFAQ.ru или Dark_Lord@land.ru

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