На этом шаге мы рассмотрим создание и использование меню.
Меню также может быть задано в файле ресурсов. Как и диалоговое окно, в программе оно определяется по имени (строке). Меню можно задать и в обычном окне, и в диалоговом окне. Для обычного окна при регистрации класса следует просто заменить строку:
MOV DWORD PTR [WC.CLMENNAME], 0 на MOV DWORD PTR [WC.CLMENNAME], OFFSET MENS
Меню на диалоговое окно устанавливается другим способом, который, разумеется, подходит и для обычного окна. В начале меню загружается при помощи функции LoadMenu, а затем устанавливается функцией SetMenu.
А теперь обо всем подробнее. Рассмотрим структуру файла ресурсов, содержащего определение меню. Ниже представлен текст файла, содержащего определение меню.
MENUP MENU { POPUP "&Первый пункт" { MENUITEM "&Первый", 1 MENUITEM "В&торой", 2 } POPUP "&Второй пункт" { MENUITEM "Трети&й", 3 MENUITEM "Четверт&ый", 4 POPUP "Еще подмен&ю" { MENUITEM "Десятый пунк&т", 6 } } 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 "help.ico" //Определение диалогового окна. DIAL1 DIALOG 0, 0, 240, 120 STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX CAPTION "Пример диалогового окна" FONT 8, "Arial" { }
;Константы. ;Сообщение приходит при закрытии окна. WM_CLOSE equ 10h WM_INITDIALOG equ 110h WM_SETICON equ 80h WM_COMMAND equ 111h ;Прототипы внешних процедур. EXTERN MessageBoxA@16: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 ExitProcess@4:NEAR EXTERN SetMenu@8:NEAR ;Структуры ;Структура сообщения. MSGSTRUCT STRUC MSHWND DD ? ;Идентификатор окна, получающего сообщение. MSMESSAGE DD ? ;Идентификатор сообщения. MSWPARAM DD ? ;Доп. информация о сообщении. MSLPARAM DD ? ;Доп. информация о сообщении. MSTIME DD ? ;Время посылки сообщения. MSPT DD ? ;Положение курсора во время посылки сообщения. MSGSTRUCT ENDS
.386P ;Плоская модель. .MODEL FLAT, STDCALL include pr38_1.asm ;Директивы компоновщику для подключения библиотек. 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 ;------------------------ PUSH 0 CALL ExitProcess@4 ;Процедура диалогового окна. ;Расположение параметров в стеке: ;[ЕВР+14Н] - LPARAM; ;[ЕВР+10Н] - WPARAM; ;[ЕВР+0СН] - MES; ;[ЕВР+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+10H],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
Результат работы программы представлен на рисунке 1:
Рис.1. Результат работы приложения
Дадим небольшой комментарий к приведенной программе. Прежде всего обращаем ваше внимание на довольно прозрачную аналогию между диалоговым окном и меню. Действительно, и в том и в другом случае ресурс определяется не по идентификатору, а по имени. Далее, и диалоговое окно, и меню содержат в себе элементы, определяющиеся по их идентификаторам, которые мы задали в файле ресурсов и которые помещаются в младшее слово параметра WPARAM.
В приведенном примере мы программно загружаем меню. Можно поступить и по-другому: указать меню в опциях определения диалогового окна следующим образом.
//Определение диалогового окна. DIAL1 DIALOG 0, 0, 240, 120 STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX MENU MENUP CAPTION "Пример диалогового окна" FONT 8, "Arial" { }
Этого достаточно, чтобы меню загрузилось и отобразилось автоматически.
Хотим напомнить читателю одну вещь. На шаге 16 приводился пример кнопки, которая создавалась как дочернее окно. То, что нажата именно эта кнопка, мы определяли по содержимому LPARAM дескриптора кнопки. Как видите, идентифицировать элемент, расположенный на диалоговом окне, можно и по дескриптору, и по идентификатору ресурса.
Вернемся к меню. Пункты меню могут содержать дополнительные параметры, которые определяют дополнительные свойства этих пунктов. Вот эти свойства, понимаемые компилятором ресурсов:
Заканчивая рассуждение о меню, заметим, что у Windows есть обширнейший набор функций, с помощью которых можно менять свойства меню (удалять и добавлять пункты меню, менять их свойства). В следующих шагах приводятся примеры некоторых функций этой группы.
На следующем шаге мы введем понятие акселератора.