| [Назад] [Далее] | ![]() |
Меню — это один из краеугольных камней идеологии Windows. Похожие друг на друга меню позволяют пользоваться совершенно незнакомыми программами, не читая инструкций, и знакомиться с их возможностями, просто посмотрев содержание различных пунктов меню. Давайте добавим меню и в нашу программу window.asm.
Первое, что мы должны будем сделать, — это само меню. Меню, так же как и иконки, диалоги и другая информация (вплоть до версии программы), записывают в файлы ресурсов. Файл ресурсов имеет расширение *.RC для текстового файла или *.RES для бинарного файла, скомпилированного специальным компилятором ресурсов (RC, BRCC32 или WRC). И те, и другие файлы ресурсов можно редактировать специальными программами, входящими в дистрибутивы C/C++ или других средств разработки для Windows, но мы не будем делать слишком сложное меню и напишем RC-файл вручную, например так:
// winmenu.rc
// файл ресурсов для программы winmenu.asm
//
#define ZZZ_TEST 0
#define ZZZ_OPEN 1
#define ZZZ_SAVE 2
#define ZZZ_EXIT 3
ZZZ_Menu MENU {
POPUP "&File" {
MENUITEM "&Open",ZZZ_OPEN
MENUITEM "&Save", ZZZ_SAVE
MENUITEM SEPARATOR
MENUITEM "E&xit",ZZZ_EXIT
}
MENUITEM "&Edit",ZZZ_TEST
}
Чтобы добавить этот файл в программу, его надо скомпилировать и указать имя скомпилированного *.RES-файла для компоновщика:
MASM:
ml /c /coff /Cp winmenu.asm rc /r winmenu.rc link winmenu.obj winmenu.res /subsystem:windows
TASM:
tasm /m /ml /D_TASM_ winmenu.asm brcc32 winmenu.rc tlink32 /Tpe /aa /c /x winmenu.obj,,,,,winmenu.res
WASM:
wasm winmenu.rc wrc /r /bt=nt winmenu.rc wlink file winmenu.obj res winmenu.res form windows nt
А теперь сам текст программы. Чтобы показать, как мало надо внести изменений в программу window.asm, комментарии для всех строк, перенесенных оттуда без изменений заменены, на символ «*».
; winmenu.asm
; Графическое win32-приложение, демонстрирующее работу с меню
; звездочками отмечены строки, скопированные из файла window.asm
;
ZZZ_TEST equ 0 ; сообщения от нашего меню
ZZZ_OPEN equ 1 ; должны совпадать с определениями из winmenu.rc
ZZZ_SAVE equ 2 ; кроме того в нашем примере их номера важны
ZZZ_EXIT equ 3 ; потому что они используются как индекс для
; таблицы переходов к обработчикам
include def32.inc ;*
include kernel32.inc ;*
include user32.inc ;*
.386 ;*
.model flat ;*
.data ;*
class_name db "window class 1",0 ;*
window_name db "win32 assembly example",0 ;*
menu_name db "ZZZ_Menu",0 ; имя меню в файле ресурсов
test_msg db "You selected menu item TEST",0 ; строки для
open_msg db "You selected menu item OPEN",0 ; демонстрации работы
save_msg db "You selected menu item SAVE",0 ; меню
wc WNDCLASSEX <4*12,CS_HREDRAW or CS_VREDRAW,offset win_proc,0,0,?,?,?,\
COLOR_WINDOW+1,0,offset class_name,0> ;*
.data? ;*
msg_ MSG <?,?,?,?,?,?> ;*
.code ;*
_start: ;*
xor ebx,ebx ;*
push ebx ;*
call GetModuleHandle ;*
mov esi,eax ;*
mov dword ptr wc.hInstance,eax ;*
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 ;*
push offset menu_name ; имя меню
push esi ; наш идентификатор
call LoadMenu ; загрузим меню из ресурсов
mov ecx,CW_USEDEFAULT ;*
push ebx ;*
push esi ;*
push eax ; идентификатор меню или окна-потомка
push ebx ;*
push ecx ;*
push ecx ;*
push ecx ;*
push ecx ;*
push WS_OVERLAPPEDWINDOW ;*
push offset window_name ;*
push offset class_name ;*
push ebx ;*
call CreateWindowEx ;*
push eax ;*
push SW_SHOWNORMAL ;*
push eax ;*
call ShowWindow ;*
call UpdateWindow ;*
mov edi,offset msg_ ;*
message_loop: ;*
push ebx ;*
push ebx ;*
push ebx ;*
push edi ;*
call GetMessage ;*
test eax,eax ;*
jz exit_msg_loop ;*
push edi ;*
call TranslateMessage ;*
push edi ;*
call DispatchMessage ;*
jmp short message_loop ;*
exit_msg_loop: ;*
push ebx ;*
call ExitProcess ;*
; процедура win_proc
; вызывается окном каждый раз, когда окно получает какое-нибудь сообщение
; именно здесь будут происходить вся работа программы
;
; процедура не должна изменять регистры EBP,EDI,ESI и EBX !
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_wm_destroy ;*
push 0 ;*
call PostQuitMessage ;*
jmp short end_wm_check ;*
not_wm_destroy: ;*
cmp wp_uMsg,WM_COMMAND ; если мы получили WM_COMMAND
jne not_wm_command ; это от нашего меню
mov eax,wp_wParam ; и в wParam лежит наше подсообщение
jmp dword ptr menu_handlers[eax*4] ; косвенный переход
; (в 32-битном режиме можно делать переход по любому регистру)
menu_handlers dd offset menu_test,offset menu_open
dd offset menu_save,offset menu_exit
; обработчики событий test, open и save выводят MessageBox
; обработчик exit выходит из программы
menu_test:
mov eax,offset test_msg ; сообщение для MessageBox
jmp short show_msg
menu_open:
mov eax,offset open_msg ; сообщене для MessageBox
jmp short show_msg
menu_save:
mov eax,offset save_msg ; сообщение для MessageBox
show_msg:
push MB_OK ; стиль для MessageBox
push offset menu_name ; заголовок
push eax ; сообщение
push wp_hWnd ; идентификатор окна-предка
call MessageBox ; вызов функции
jmp short end_wm_check ; выход из win_proc
menu_exit: ; если выбрали пункт EXIT
push wp_hWnd
call DestroyWindow ; уничтожим наше окно
end_wm_check:
leave ;*
xor eax,eax ; вернем 0 как результат работы процедуры
ret 16 ;*
not_wm_command: ; not_wm_command, чтобы избавиться от лишнего jmp
leave ;*
jmp DefWindowProc ;*
win_proc endp ;*
end _start ;*
Итого: из 120 строк программы новыми оказались всего 36, в то время как программа, с точки зрения пользователя, стала намного сложнее. Так и выглядит все программирование под Windows на ассемблере — берется одна написанная раз и навсегда шаблонная программа, модифицируются ресурсы и пишутся обработчики для различных событий меню и диалогов. Фактически все программирование оказывается сосредоточенным именно в этих процедурах-обработчиках.
Добавления к включаемым файлам в этом примере тоже оказываются незначительными по сравнению с window.asm.
В user32.inc между ifdef _TASM_ и else:
extrn LoadMenuA:near
extrn DestroyWindow:near
LoadMenu equ LoadMenuA
и между else и endif:
extrn __imp__LoadMenuA@8:dword
extrn __imp__DestroyWindow@4:dword
LoadMenu equ __imp__LoadMenuA@8
DestroyWindow equ __imp__DestroyWindow@4
и в def32.inc:
; из winuser.h WM_COMMAND equ 111h MB_OK equ 0