7.3.2. Окна

Теперь, когда мы знаем, как просто выводится окно с предопределенным клас­сом, возьмемся создание собственного окна - процедуры, на будут базироваться все последующие примеры, и познакомимся с понятием сообщения. В DOS основным средством передачи управления программам в различных ситу­ациях служат прерывания. В Windows прерывания используются для системных нужд, а для приложений существует аналогичный механизм - механизм событий. Так, нажатие клавиши на клавиатуре, если эта клавиша не используется Windows, генерирует сообщение WM KEYDOWN или WM_KEYUP, которое можно пере­хватить, добавив в цепь обработчиков события собственное при помощи SetWindowHookEx. Затем события преобразуются в сообщения, которые рассы­лаются функциям - обработчикам сообщений - и которые можно прочитать из

основной программы при помощи вызовов GetMessage и PeekMessage.

Для начала нам потребуется только обработка сообщения закрытия окна (WM DESTROY и WM_QUIT), по которому программа будет завершаться.

; window.asm

;   Графическое мап32-приложение,   демонстрирующее базовый вывод окна.

include def32.inc include kerne!32.inc

include user32.inc

.38.6

.model flat .data

class.name' db "window class V,0

window_name        db "Win32 assembly example"

; Структура, описывающая класс окна.

О

wc

WNDCLASSEX <4*12,CS_HREDRAW or CSJ/REDRAW, of fset win_proc, 0,0, ?, ?,?,\ COL0R_WINDOW+1,0,offset class_name,0>

Здесь находятся следующие поля: wc.cbSize = 4*12 - размер этой структуры wc. style - стиль окна (перерисовывать при изменении размера) wc.lpfnWndProc - обработчик событий окна (win_proc) wc.cbClsExtra - число дополнительных байтов после структуры (0) wc.cbWndExtra - число дополнительных байтов после окна (0) wc.hlnstance - идентификатор нашего процесса (?) wc.hlcon - идентификатор иконки (?) wc.hCursor - идентификатор курсора (?)

wc.hbrBackground - идентификатор кисти или цвет фона + 1 (C0L0R_WIND0W+1)

wc. IpszMenuName - ресурс с основным меню (в этом примере - 0) wc.lpszClassName - имя класса (строка class.name) wc.hlconSm - идентификатор маленькой иконки (только в Windows 95, для NT должен быть О).

msg_

_start:

.data? MSG

.code

<??????>

А это - структура, в которую сообщение после GetMessage.

возвращается

В EBX будет О для команд push 0.

xor ebx.ebx

Определить идентификатор нашей программы

push ebx

call GetModuleHandle

и сохранить Заполнить и зарегистрировать класс.

mov      dword ptr wc.hlnstance,еах ; Идентификатор предка.

Выбрать иконку.

его в ESI.

push

push call

mov

Выбрать форму

push

push

call

mov push

call

Создать окно.

mov

push push

push

IDI_APPLICATION ;

ebx ; Loadlcon

wc.hlcon,eax ; курсора. IDC_ARROW ebx ; LoadCursor wc. hCursor, eax ;

offset

RegisterClassEx

ecx.CWJJSEDEFAULT;

ebx ;

esi ebx

Стандартная иконка приложения. Идентификатор модуля с иконкой.

Идентификатор

иконки        нашего класса.

Стандартная стрелка. Идентификатор модуля

с курсором.

Идентификатор курсора для нашего класса.

Зарегистрировать класс.

push ecx короче push    в пять раз. Адрес структуры CREATESTRUCT  (здесь NULL)'. Идентификатор процесса, который будет получать, сообщения от окна  (то есть наш). Идентификатор меню или окна-потомка.

push ebx

push ecx

push ecx

push ecx

.push ecx

push WSJWERLAPPEDWINDOW

push ' offset window_name

push offset class_name;

push ebx

call CreateWindowEx

push eax

push SW_SH0WN0RMAL push eax

нам

Больше идентификатор окна

call ShowWindow call UpdateWindow

не

Идентификатор окна-предка. Высота (СИ_и8ЕОЕРАШ.Т - по умолчанию) . Ширина (по умолчанию).

У-координата (по умолчанию). Х-координата   (по умолчанию).

;  Стиль окна. ;   Заголовок окна. Любой зарегистрированный класс. Дополнительный стиль. Создать окно   (еах - идентификатор Идентификатор   для Прбабе1А)1пНш. Тип показа для для 81юи1Дпбои. Идентификатор для ЭпоиИ^ои. потребуется. Показать окно

и послать ему сообщение ИМ.РАШ.

окна).

Основной цикл - проверка сообщений от окна и выход по

mov

message_loop:

push push push push call

test

jz

-push call push call

edi, offset msg_ і push edi короче push N в 5 раз.

ebx ebx ebx edi

GetMessage

eax,eax ; exit_msg_loop ;

edi

TranslateMessage ;

edi

DispatchMessage ;

short

любое наше окно).

Последнее сообщение. Первое сообщение. Идентификатор окна (О Адрес структуры MSG.

Получить сообщение от окна с ожиданием -не забывайте использовать PeekMessage, если нужно в этом цикле что-то выполнять. Если получено WM_QUIT, выйти.

Иначе - преобразовать сообщения типа WM_KEYUP в сообщения типа WM_CHAR

и послать их процедуре окна  (иначе его просто

нельзя будет закрыть). ; Продолжить цикл.

exit_msg_loop:

і Выход из программы. push ebx

call ExitProcess і Процедура

; Вызывается окном каждый раз, когда оно получает какое-нибудв сообщение.. ;   Именно здесв будет происходитв вся работа программы.'

і  Процедура не должна изменять регистры EBP.EDI, ESI и ЕВХ! .

в стеке, построить стековый кадр.

win_proc ргос і   Так как мы получаем параметры

push ebp

mov ebp.esp'

; Процедура типа WindowProc вызывается со следующими параметрами:шшт

wp_hWnd     equ dword ptr  [ebp+08h] ; идентификатор окна,

wp_uMsg    equ dword ptr' [ebp+OCh]; номер сообщения,

wp_wParam equ dword ptr  [ebp+lOh]; первый параметр,

wp_lParam equ dword ptr  [ebp+14h] ; второй параметр. ; Если мы получили сообщение WM_DESTROY (оно означает,   что окно уже удалили ; с экрана,  нажав Alt-F4 или кнопку в верхнем правом углу), ; то отправим основной программе сообщение WM_QUIT.

сглр       wp_uMsg,WM_ DESTROY

jne not_wm_destroy

push      0 ■ ; Код выхода.-

call      PostQuitMessage ; Послатв WM_0UIT

jmp       short end wm_check ; и выйти из процедуры.

not_wm_destroy:

;   Если мы получили другое сообщение - вызвать его обработчик по умолчанию. leave ; Восстановить ebp

jmp       DefWindowProc      ; и вызватв DefWindowProc с нашими параметрами

и адресом возврата в стеке.

end_wm_check:

leave ; Восстановить ebp

ret        16 ; и вернуться самим, очистив стек от параметров.

win_proc endp

end _start

Необходимые добавления в файл def32.inc:

; Из winuser. h.

MISAPPLICATION

equ

32512

WM_DESTROY.

equ

2

CS_HREDRAW

equ

2

CS_VREDRAW

equ

1

CW_USEOEFAULT

■ equ

80000000П

WS_OVERLAPPEDWINDOW

equ

OCFOOOOh

IDC_ARR0W

equ

32512

Sw_SH0WN0RMAL

equ

1

COLOR_WINDOW

equ

5

WNDCLASSEX

struc

 

cbSize

dd

7

• style

dd

?

lpfnWndProc

dd

?

cbClsExtra

dd

7

cbWndExtra

dd

?

hlnstance

dd

7

hlcon

dd

7

hCursor

dd

?

hbrBackground

dd

7

IpszMenuName

dd

7

IpszClassName

dd

7

hlconSm

dd

7

WNDCLASSEX

ends

 

MSG

struc

 

. hwnd

dd

?

message

wParam

lParam

time

Pt

MSG

dd dd dd dd

dd ends

Добавления в файл между ifdef _TASM_ и

DispatcnMessage

GetMessage

Loadlcon

CreateWindowEx

DefWindowProc

RegisterClassEx

LoadCursor

между else и endif:

user32.inc: else:

extrn

extrn

extrn

extrn

extrn

extrn

extrn

extrn

extrn

extrn

extrn

equ

DispatcnMessage

TranslateMessage

GetMessage

Loadlconequ

UpdateWindow

ShowWindow

CreateWindowEx

DefWindowProc

PostQuitMessage

RegisterClassEx

LoadCursor

equ .

equ equ equ equ equ

extrn extrn ext rn extrn extrn extrn ext rn ext rn extrn extrn extrn equ

equ_

equ

DispatchMessageA:near TranslateMessage:near GetMessageA:near LoadlconA:near UpdateWindow:near ShowWindow:near CreateWindowExA:near DefWindowProcA:near PostQuitMessage:near RegisterClassExA:near LoadCursorA:near DispatchMessageA GetMessageA LoadlconA CreateWindowExA DefWindowProcA RegisterClassExA LoadCursorA

imp_DispatchMessageA@4: dword imp_TranslateMessage@4: dword imp_GetMessageA<al6:dwo rd imp_LoadIconA@8: dword imp_UpdateWindow§4: dword imp_ShowWindow@8: dword imp_CreateWindowExA@48:dwo rd imp_DefWindowProcA@l6: dwo rd imp_Post0uitMessage@4: dword imp_RegisterClassExA@4: dword imp_LoadCursorA<a8: dword imp_DispatchMessageA@4 imp_TranslateMessage@4 imp_GetMessageA@16

_imp_LoadlconA@8

equ_imp_updateWindow(3>4

equ_imp__ShowWindow@8

equ_imp_CreateWindowExA@48

equ_imp__DefWindowProcA@16

equ_imp_PostQuitMessage@4

equ_imp_RegisterClassExA@4

equ_imp_LoadCursorA@8

и в файл kernel32.inc между ifdef _TASM_ и else:

extrn GetModuleHandleA:near equ

и между else и endif:

,       extrn_imp_GetModuleHandleA@4:dword

GetModuleHandle equ_imp_GetModuleHandleA<s4

В начале раздела что программировать под Windows просто, а в то

же время текст обычной программы вывода пустого окна на экран уже занимает больше места, чем, например, текст программы проигрывания wav-файла из раз­дела 5.10.8. Где же обещанная простота? Так вот, оказывается, что, написав window.asm, мы уже создали значительную часть всех последующих программ, а когда мы дополним этот текст полноценным диалогом, обнаружится, что боль­ше не нужно писать все эти громоздкие конструкции, достаточно лишь копиро­вать отдельные участки текста.