Шаг 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 дескриптора кнопки. Как видите, идентифицировать элемент, расположенный на диалоговом окне, можно и по дескриптору, и по идентификатору ресурса.

    Вернемся к меню. Пункты меню могут содержать дополнительные параметры, которые определяют дополнительные свойства этих пунктов. Вот эти свойства, понимаемые компилятором ресурсов:

    Заканчивая рассуждение о меню, заметим, что у Windows есть обширнейший набор функций, с помощью которых можно менять свойства меню (удалять и добавлять пункты меню, менять их свойства). В следующих шагах приводятся примеры некоторых функций этой группы.

    На следующем шаге мы введем понятие акселератора.




Предыдущий шаг Содержание Следующий шаг