Assembler - язык неограниченных возможностей
0e1cc9b4

Меню


Меню — это один из краеугольных камней идеологии 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


Содержание раздела