Ассемблер для Windows

       

Пример простейших манипуляций с меню



I

Читатель, наверное, обращал внимание, что во многих программах меню может динамически меняться во время работы: исчезают и добавляются некоторые пункты, одно меню встраивается в другое. Пример простейших манипуляций с меню приведен на Рисунок 2.4.1.

Программа открывает окно с кнопкой и меню. При нажатии кнопки текущее меню заменяется другим. Если нажать еще раз, то меню исчезает. Следующее нажатие приводит к появлению первого меню и так далее, по кругу. Кроме того, в первом меню имеется пункт, который приводит к такому же результату, что и нажатие кнопки. Наконец, для этого пункта установлена акселераторная клавиша - F5. При передвижении по меню название пунктов меню и заголовков выпадающих (POPUP) подменю отображается в заголовке окна. Вот, вкратце, как работает программа. Механизмы работы программы будут подробно разобраны ниже.

// файл menu2.rc

// виртуальная клавиша F5 #define VK_F5 0х74

// ************** MENUP **************

MENUP MENU { POPUP "&Первый пункт" { MENUITEM "&Первый",1 MENUITEM "В&торой",2 }

POPUP "&Второй пункт" { MENUITEM "Трети&й",3 MENUITEM "Четверт&ый\tF5",4 MENUITEM SEPARATOR POPUP "Еще подмен&ю" { MENUITEM "Дополнительный пу&нкт",6 }

}

MENUITEM "Вы&ход",5 }

//**************** MENUC ******************** MENUC MENU { POPUP "Набор первый" { MENUITEM "Белый",101 MENUITEM "Серый",102 MENUITEM "Черный",103 } POPUP "Набор второй" { MENUITEM "Красный",104 MENUITEM "Синий",105 MENUITEM "Зеленый",106 } }

// таблица акселераторов // определен один акселератор для вызова // пункта из меню MENUP MENUP ACCELERATORS { VK_F5, 4, VIRTKEY, NOINVERT }

; файл menu2.inc ; константы ; сообщение приходит при закрытии окна WM_DESTROY equ 2 ; сообщение приходит при создании окна WM_CREATE equ 1 ; сообщение при щелчке левой кнопкой мыши в области окна WM_COMMAND equ 111h WM_MENUSELECT equ 11Fh WM_SETTEXT equ 0Ch MIIM_TYPE equ 10h MF_STRING equ 0h MF_POPUP equ 10h




; свойства окна CS_VREDRAW equ 1h CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_OVERLAPPEDWINDOW equ 000CF0000H STYLE equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS BS_DEFPUSHBUTTON equ 1h WS_VISIBLE equ 10000000h WS_CHILD equ 40000000h STYLBTN equ WS_CHILD+BS_DEFPUSHBUTTON+WS_VISIBLE ; идентификатор стандартной иконки IDI_APPLICATION equ 32512 ; идентификатор курсора IDC_ARROW equ 32512 ; режим показа окна - нормальный SW_SHOWNORMAL equ 1 SW_HIDE equ 0 SW_SHOWMINIMIZED equ 2
; прототипы внешних процедур EXTERN wsprintfA:NEAR EXTERN GetMenuItemInfoA@16:NEAR EXTERN LoadMenuA@8:NEAR EXTERN SendMessageA@16:NEAR EXTERN MessageBoxA@16:NEAR EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4:NEAR EXTERN UpdateWindow@4:NEAR EXTERN TranslateAcceleratorA@12:NEAR EXTERN LoadAcceleratorsA@8:NEAR EXTERN GetMenu@4:NEAR EXTERN DestroyMenu@4:NEAR EXTERN SetMenu@8:NEAR
; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS
;----структура класса окон WNDCLASS STRUC CLSSTYLE DD ? CLWNDPROC DD ? CLSCBCLSEX DD ? CLSCBWNDEX DD ? CLSHINST DD ? CLSHICON DD ? CLSHCURSOR DD ? CLBKGROUND DD ? CLMENNAME DD ? CLNAME DD ? WNDCLASS ENDS
MENINFO STRUCT cbSize DD ? fMask DD ? fType DD ? fState DD ? wID DD ? hSubMenu DD ? hbmpChecked DD ? hbmpUnchecked DD ? dwItemData DD ? dwTypeData DD ? cch DD ? MENINFO ENDS
; файл menu2.asm .386P ; плоская модель .MODEL FLAT, stdcall include menu2.inc ; директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ; ------------------------------------------------------------
; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' SPACE DB 30 dup(32),0 MENI MENINFO <0> NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> HINST DD 0 ; дескриптор приложения CLASSNAME DB 'CLASS32',0 CPBUT DB 'Кнопка',0 ; выход CLSBUTN DB 'BUTTON',0 HWNDBTN DD 0 CAP DB 'Сообщение',0 MES DB 'Конец работы программы',0 MEN DB 'MENUP',0 MENC DB 'MENUC',0 ACC DD ? HMENU DD ? PRIZN DD ? BUFER DB 100 DUP(0) _DATA ENDS


; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; инициализировать счетчик MOV PRIZN, 2 ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; заполнить структуру окна ; стиль MOV [WC.CLSSTYLE], STYLE ; процедура обработки сообщений MOV [WC.CLWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEX], 0 MOV [WC.CLSCBWNDEX], 0 MOV EAX, [HINST] MOV [WC.CLSHINST], EAX ; ----------иконка окна PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ; ----------курсор окна PUSH IDC_ARROW PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ; ——————---- MOV [WC.CLBKGROUND], 17 ; цвет окна MOV DWORD PTR [WC.CLMENNAME], OFFSET MEN MOV DWORD PTR [WC.CLNAME], OFFSET CLASSNAME PUSH OFFSET WC CALL RegisterClassA@4 ; создать окно зарегистрированного класса PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH 400 ; DY - высота окна PUSH 400 ; DX - ширина окна PUSH 100 ; Y PUSH 100 ; X PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET SPACE ; имя окна PUSH OFFSET CLASSNAME ; имя класса PUSH 0 CALL CreateWindowExA@48 ; проверка на ошибку CMP EAX, 0 JZ _ERR MOV [NEWHWND], EAX ; дескриптор окна ; определить идентификатор меню PUSH EAX CALL GetMenu@4 MOV HMENU, EAX ; загрузить акселераторы PUSH OFFSET MEN PUSH [HINST] CALL LoadAcceleratorsA@8 MOV ACC, EAX ; --———————————————————- PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ; показать созданное окно ; ------------------------- PUSH [NEWHWND] CALL UpdateWindow@4 ; команда перерисовать видимую ; часть окна, сообщение WM_PAINT ; петля обработки сообщений MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP EAX, 0 JE END_LOOP PUSH OFFSET MSG PUSH [ACC] PUSH [NEWHWND] CALL TranslateAcceleratorA@12 CMP EAX, 0 JNE MSG_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ; выход из программы (закрыть процесс) PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP
;---------------------------------------- ; процедура окна ; расположение параметров в стеке ; [EBP+014Н] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP,ESP PUSH EBX PUSH ESI PUSH EDI ; сообщение WM_DESTROY - при закрытии окна CMP DWORD PTR [EBP+0CH], WM__DESTROY JE WMDESTROY ; сообщение WM CREATE - при создании окна CMP DWORD PTR [EBP+0CH], WM_CREATE JE WMCREATE ; сообщение WM COMMAND - при событиях с элементами на окне CMP DWORD PTR [EBP+0CH], WM_COMMAND JE WMCOMMND ; сообщение WM_MENUSELECT - события, связанные с меню CMP DWORD PTR [EBP+0CH], WM_MENUSELECT JE WMMENUSELECT ; остальные события возвращаем обратно JMP DEFWNDPROC WMMENUSELECT: ; пропускаем первое сообщение при обращении к меню CMP WORD PTR [EBP+14Н],0 JE FINISH ; проверяем, что активизировано - пункт меню ;или заголовок выпадающего меню MOV EDX, 0 TEST WORD PTR [EBP+12H],MF_POPUP SETNE DL ; заполнение структуры для вызова функции ; GetMenuItemInfo MOVZX EAX,WORD PTR [EBP+10H] ; идентификатор MOV MENI.cbSize,48 MOV MENI.fMask, MIIM_TYPE MOV MENI.fType, MF_STRING MOV EBX, DWORD PTR [EBP+14H] MOV MENI.hSubMenu, EBX MOV MENI.dwTypeData, OFFSET BUFER MOV MENI.cch, 100 ; получить информацию о выбранном пункте меню PUSH OFFSET MENI PUSH EDX PUSH EAX PUSH DWORD PTR [EBP+14H] CALL GetMenuItemInfoA@16 ; проверить результат выполнения функции CMP EAX, 0 JE FINISH ; вывести название пункта меню PUSH MENI.dwTypeData PUSH 0 PUSH WM_SETTEXT PUSH DWORD PTR [EBP+0BH] CALL SendMessageA@16 MOV EAX, 0 JMP FINISH WMCOMMND: MOV EAX, HWNDBTN ; проверить, не нажата ли кнопка CMP DWORD PTR [EBP+14Н], EAX JE YES_BUT ; проверить, не выбран ли пункт меню MENUC - Выход CMP WORD PTR [EBP+10Н],5 JE WMDESTROY ; проверить, не выбран ли пункт меню с идентификатором 5 CMP WORD PTR [EBP+10Н], 4 JNE L00 JMP YES_BUT L00: MOV EAX, 0 JMP FINISH YES_BUT: ; здесь обработка нажатия кнопки ; вначале стереть надпись в заголовке PUSH OFFSET SPACE PUSH 0 PUSH WM_SETTEXT PUSH DWORD PTR [EBP+08H] CALL SendMessageA@16 ; проверить загружено или нет меню CMP PRIZN, 0 JE L1 CMP PRIZN, 1 JE L2 ; загрузить меню MENC PUSH OFFSET MENC PUSH [HINST] CALL LoadMenuA@8 ; установить меню MOV HMENU, EAX PUSH EAX PUSH DWORD PTR [EBP+08H] CALL SetMenu@8 ; установить признак MOV PRIZN,0 MOV EAX,0 JMP FINISH L2: ; загрузить меню MENUP PUSH OFFSET MEN PUSH [HINST] CALL LoadMenuA@8 ; установить меню MOV HMENU, EAX PUSH EAX PUSH DWORD PTR [EBP+08H] CALL SetMenu@8 ; установить признак MOV PRIZN, 2 MOV EAX,0 JMP FINISH L1: ; удалить меню PUSH HMENU CALL DestroyMenu@4 ; обновить содержимое окна PUSH SW_SHOWMINIMIZED PUSH DWORD PTR [EBP+08H] CALL ShowWindow@8 PUSH SW_SHOWNORMAL PUSH DWORD PTR [EBP+08H] CALL ShowWindow@8 MOV PRIZN,1 MOV EAX, 0 JMP FINISH WMCREATE: ; создать окно-кнопку PUSH 0 PUSH [HINST] PUSH 0 PUSH DWORD PTR [EBP+08H] PUSH 20 ; DY PUSH 60 ; DX PUSH 10 ; Y PUSH 10 ; X PUSH STYLBTN ; имя окна (надпись на кнопке) PUSH OFFSET CPBUT PUSH OFFSET CLSBUTN ; имя класса PUSH 0 CALL CreateWindowExA@48 MOV HWNDBTN, EAX ; запомнить дескриптор кнопки MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: PUSH 0 ; MB_OK PUSH OFFSET CAP PUSH OFFSET MES PUSH DWORD PTR [EBP+08H] ; дескриптор окна CALL MessageBoxA@16 PUSH 0 CALL PostQuitMessage@4 ; сообщение WM_QUIT MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START

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