Меню
II
6. Меню. Меню также может быть задано в файле ресурсов. Как и диалоговое окно, в программе оно определяется по имени (строке). Меню можно задать и в обычном окне, и в диалоговом окне. Для обычного окна при регистрации класса следует просто заменить строку
MOV DWORD PTR [WC.CLMENNAME],0
на
MOV DWORD PTR [WC.CLMENNAME], OFFSET MENS
Здесь MENS - имя, под которым меню располагается в файле ресурсов. Меню на диалоговое окно устанавливается другим способом, который, разумеется, подходит и для обычного окна. В начале меню загружается при помощи функции LoadMenu, а затем устанавливается функцией SetMenu.
А теперь обо всем подробнее. Рассмотрим структуру файла ресурсов, содержащего определение меню. Ниже представлен текст файла, содержащего определение меню.
Далее представлена программа, демонстрирующая меню на диалоговом окне.
MENUP MENU { POPUP "&Первый пункт" { MENUITEM "&Первый",1 MENUITEM "В&торой",2 POPUP "Подмен&ю" { MENUITEM "Десятый пунк&т",6 } } POPUP "&Второй пункт" { MENUITEM "Трети&й",3 MENUITEM "Четверт&ый",4 } MENUITEM "Вы&ход",5 }
Внимательно рассмотрите текст меню. Как видите, пункты меню имеют идентификаторы, по которым в программе можно определить, какой пункт меню выбран. Можно заметить, что выпадающее меню может содержать еще и подменю.
//файл menu.rc //определение констант #define WS_SYSMENU 0x00080000L #define WS_MINIMIZEBOX 0x00020000L #define WS_MAXIMIZEBOX 0x00010000L #define WS_POPUP 0x80000000L #define WS_CAPTION 0x00C00000L
MENUP MENU { POPUP "&Первый пункт" { MENUITEM "&Первый",1 MENUITEM "В&торой",2 } POPUP "&Второй пункт" { MENUITEM "Трети&й",3 MENUITEM "Четверт&ый",4
POPUP "Еще подмен&ю" {
MENUITEM "Десятый пунк&т", 6 } } MENUITEM "Вы&ход", 5 }
// идентификаторы #define IDI_ICON1 100
; определили иконку IDI_ICON1 ICON "ico1.ico"
// определение диалогового окна DIAL1 DIALOG 0, 0, 240, 120
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
CAPTION "Пример диалогового окна" FONT 8, "Arial" { }
; файл menu.inc ; константы ; сообщение приходит при закрытии окна
WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_SETICON equ 80h WM_COMMAND equ 111h
; прототипы внешних процедур EXTERN MessageBoxA@16:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN DialogBoxParamA@20:NEAR EXTERN EndDialog@8:NEAR EXTERN LoadStringA@16:NEAR EXTERN LoadIconA@8:NEAR EXTERN LoadMenuA@8:NEAR EXTERN SendMessageA@16:NEAR EXTERN SetMenu@8:NEAR
; структуры ; структура сообщения MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS
; файл menu.asm .386P
; плоская модель .MODEL FLAT, stdcall
include menu. inc
; директивы компоновщику для подключения библиотек includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ;------------------------------------------------------------
; сегмент данных _DATA SEGMENT DWORD PUBLIC USE32 'DATA' MSG MSGSTRUCT <?> HINST DD 0 ; дескриптор приложения PA DB "DIAL1",0 PMENU DB "MENUP",0 STR1 DB "Выход из программы",0 STR2 DB "Сообщение",0 _DATA ENDS
; сегмент кода _TEXT SEGMENT DWORD PUBLIC USE32 'CODE' START: ; получить дескриптор приложения PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX ;--------------------------------- PUSH 0 PUSH OFFSET WNDPROC PUSH 0 PUSH OFFSET PA PUSH [HINST] CALL DialogBoxParamA@20 CMP EAX,-1 JNE KOL KOL: PUSH 0 CALL ExitProcess@4
; процедура окна ; расположение параметров в стеке
; [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 ;----------------- CMP DWORD PTR [EBP+0CH],WM_CLOSE JNE L1 ; закрыть диалоговое окно PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 JMP FINISH L1: CMP DWORD PTR [EBP+0CH],WM_INITDIALOG JNE L2 ; загрузить иконку PUSH 100 ; идентификатор иконки PUSH [HINST] ; идентификатор процесса CALL LoadIconA@8 ; установить иконку PUSH EAX PUSH 0 ; тип иконки (маленькая) PUSH WM_SETICON PUSH DWORD PTR [EBP+08H] CALL SendMessageA@16 ; загрузить меню PUSH OFFSET PMENU PUSH [HINST] CALL LoadMenuA@8 ; установить меню PUSH EAX PUSH DWORD PTR [EBP+08H] CALL SetMenu@8 JMP FINISH L2: ; проверяем, не случилось ли чего с управляющими элементами ; на диалоговом окне, в нашем случае имеется единственный ; управляющий элемент - это меню CMP DWORD PTR [EBP+0CH], WM_COMMAND JNE FINISH ; здесь определяем идентификатор, в данном случае ; это идентификатор пункта меню CMP WORD PTR [EBP+10Н],5 JNE FINISH ; сообщение PUSH 0 ; МВ_ОК PUSH OFFSET STR2 PUSH OFFSET STR1 PUSH 0 CALL MessageBoxA@16 ; закрыть диалоговое окно PUSH 0 PUSH DWORD PTR [EBP+08H] CALL EndDialog@8 FINISH: MOV EAX, 0 POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP _TEXT ENDS END START
Рисунок 2.3.3. Пример программы с меню.
Дадим небольшой комментарий к программе на Рисунок 2.3.3. Прежде всего обращаю ваше внимание на довольно прозрачную аналогию между диалоговым окном и меню. Действительно, и в том и в другом случае ресурс определяется не по идентификатору, а по имени. Далее, и диалоговое окно, и меню содержат в себе элементы, определяющиеся по их идентификаторам, которые мы задали в файле ресурсов и которые помещаются в младшее слово параметра WPARAM. В программе на Рисунок 2.3.3 мы программно загружаем меню. Можно поступить и по другому: указать меню в опциях определения диалогового окна следующим образом.
//определение диалогового окна DIAL1 DIALOG 0, 0, 240, 120
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX MENU MENUP
CAPTION "Пример диалогового окна" FONT 8, "Arial" { }
Этого достаточно, чтобы меню загрузилось и отобразилось автоматически.
Хочу напомнить читателю одну вещь. В главе 1.3 приводился пример кнопки, которая создавалась как дочернее окно. То, что нажата имение эта кнопка, мы определяли по содержимому LPARAM, который содержал дескриптор кнопки.
Как видите, идентифицировать элемент, расположенный на диалоговом окне, можно и по дескриптору, и по идентификатору ресурса.
Вернемся к меню. Пункты меню могут содержать дополнительные параметры, которые определяют дополнительные свойства этих пунктов.
Вот эти свойства, понимаемые компилятором ресурсов:
CHECKED - пункт отмечен "птичкой".
GRAYED - элемент недоступен (имеет серый цвет).
HELP - элемент может быть связан с помощью. Редакторы ресурсов дополнительно создают ресурс - строку. При этом идентификатор строки совпадает с идентификатором пункта меню.
MENUBARBREAK - для горизонтального пункта это означает, что начиная с него горизонтальные пункты располагаются в новой строке. Для вертикального пункта - то, что начиная с него пункты расположены в новом столбце. При этом проводится разделительная линия.
MENUBREAK - аналогично предыдущему, но разделительная линия не проводится.
INACTIVE - пункт не срабатывает.
SEPARATOR - создает в меню разделитель. При этом идентификатор не ставится.
Заканчивая рассуждение о меню, замечу, что у Windows есть обширнейший набор функций, с помощью которых можно менять свойства меню (удалять и добавлять пункты меню, менять их свойства). В следующей главе приводятся примеры некоторых из этой группы.