Шаг 38.
Язык описания ресурсов. Меню
На этом шаге мы рассмотрим создание и использование меню.
Меню также может быть задано в файле ресурсов. Как и диалоговое окно, в программе оно определяется по имени
(строке). Меню можно задать и в обычном окне, и в диалоговом окне. Для обычного окна при регистрации класса следует просто заменить строку:
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 "Трети&й", 3
MENUITEM "Четверт&ый", 4
POPUP "Еще подмен&ю"
{
MENUITEM "Десятый пунк&т", 6
}
}
MENUITEM "Вы&ход", 5
}
Внимательно рассмотрите текст меню. Как видите, пункты меню имеют идентификаторы, по которым в программе можно
определить, какой пункт меню выбран. Можно заметить, что выпадающее меню может содержать еще и подменю.
Далее представлена программа, демонстрирующая меню на диалоговом окне.
Ресурсный файл
menu.rc.
//Файл 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"
{
}
Заголовочный файл для приложения, содержащий определения констант, внешних процедур и структур (его имя
pr38_1.asm).
;Константы.
;Сообщение приходит при закрытии окна.
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
Текст этого модуля, а также исходный файл ресурсов и файл с пиктограммой можно взять
здесь.
Основной файл приложения, содержит подключение файла
pr38_1.asm (его имя
pr38_2.asm).
.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 дескриптора кнопки.
Как видите, идентифицировать элемент, расположенный на диалоговом окне, можно и по дескриптору, и по идентификатору ресурса.
Вернемся к меню. Пункты меню могут содержать дополнительные параметры, которые определяют дополнительные свойства этих пунктов.
Вот эти свойства, понимаемые компилятором ресурсов:
- CHECKED - пункт отмечен галочкой;
- GRAYED - элемент недоступен (имеет серый цвет);
- HELP - элемент может быть связан с помощью. Редакторы ресурсов дополнительно создают ресурс - строку. При этом идентификатор
строки совпадает с идентификатором пункта меню;
- MENUBARBREAK - для горизонтального пункта это означает, что начиная с него горизонтальные пункты
располагаются в новой строке. Для вертикального пункта - то, что начиная с него пункты расположены в новом столбце.
При этом проводится разделительная линия;
- MENUBREAK - аналогично предыдущему, но разделительная линия не проводится;
- INACTIVE - пункт не срабатывает;
- SEPARATOR - создает в меню разделитель. При этом идентификатор не ставится.
Заканчивая рассуждение о меню, заметим, что у Windows есть обширнейший набор функций, с помощью которых
можно менять свойства меню (удалять и добавлять пункты меню, менять их свойства). В следующих шагах приводятся примеры некоторых функций этой группы.
На следующем шаге мы введем понятие акселератора.
Предыдущий шаг
Содержание
Следующий шаг