Ассемблер под Windows №6

Меню в среде Windows


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


Внимание! Все материалы этой рассылки, написанные до 25 августа 2002 года были написаны не мной, а Калашниковым Олегом (admin@RusFAQ.ru). Я начал писать с первого выпуска "Программирование под Windows". Просьба все письма связанные с выпусками рассылки до 25-ого числа посылать ему.


В начале, по традиции, немного теории. Windows во многом упрощает нам работу программ, он берёт на себя практически все заботы об обслуживанию нашего окна, предлагает нам диалоги типа MessageBox... Ещё одна очень полезная вещь, это так называемые ресурсы. Нам достаточно записать лишь параметры нужных нам объектов в файл ресурсов, а Windows берёт на себя задачу создания соответствующих объектов и их обработки. Один из объектов ресурсов, он же наиболее часто используемый, меню. Все ресурсы наиболее удобно писать в редакторе ресурсов, например в Borland Workshop, (Распаковывется при помощи WinRar3) однако структура меню настолько легка, что её легче сделать вручную! Для этого надо создать файл расширения .rc , давайте создадим menu.rc, и записать в него следующие:
#define IDM_TEST 0
#define IDM_OPEN 1
#define IDM_SAVE 2
#define IDM_EXIT 3

IDM_MENU MENU {
  POPUP "&Файл" {
    MENUITEM "&Сохранить",IDM_SAVE
    MENUITEM "&Загрузить",IDM_OPEN
    MENUITEM SEPARATOR
    MENUITEM "Вы&ход",IDM_EXIT
  }
  MENUITEM "&Тест",IDM_TEST
}
Прежде чем идти дальше, давайте разберёмся, что же мы записали. Заранее хочу предупредить, что файлы ресурсов пишутся не на ассемблере, именно поэтому я и советую работать с редактором ресурсов. Итак, первые четыре строчки равны следующим строкам на ассемблере:

IDM_TEST equ 1
IDM_OPEN equ 2
IDM_SAVE equ 3
IDM_EXIT equ 4
то есть мы создаём условные значения, которые при компиляции заменяются их реальными значениями. Следующйё строкой мы объявляем меню с именем IDM_MENU, объявляем одну подменю, в которой находится три пункта и разделительная полоса. и ещё одну кнопку в меню.
А теперь давайте добавим информацию о меню в window.asm:
IDM_TEST equ 0
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
include def32.inc
include user32.inc
include kernel32.inc
 .386
 .model flat

 .const
class     db "window class 1",0
name_     db "Da window!",0
sure      db "Предупреждение",0
ask       db "Вы уверены, что хотите выйти?",0
menu_name db 'IDM_MENU',0
save_msg  db 'Вы выбрали пункт Сохранить',0
open_msg  db 'Вы выбрали пункт Загрузить',0
test_msg  db 'Вы выбрали пункт Тест',0

 .data
wc   wndclassex<4*12, CS_HREDRAW or CS_VREDRAW, offset win_proc, 0, 0, ?, ?, ?, COLOR_WINDOW+1, 0, offset class, 0>

 .data?
msg_ msg <?,?,?,?,?,?>

;сам код
 .code
_start: ;начальная метка
 xor ebx,ebx
 push ebx
 call GetModuleHandle
 mov esi,eax
 mov dword ptr wc.hInstance,eax
 push IDI_APPLICATION
 push ebx
 call LoadIcon
 mov wc.hIcon,eax
 push idc_arrow
 push ebx
 call LoadCursor
 mov wc.hCursor,eax
 push offset wc
 call RegisterClassEx
 push offset menu_name
 push esi
 call LoadMenu
 mov ecx,CW_USEDEFAULT
 push ebx
 push esi
 push eax      ;Это не новая строка
 push ebx
 push ecx
 push ecx
 push ecx
 push ecx
 push WS_OVERLAPPEDWINDOW
 push offset name_
 push offset class
 push ebx
 call CreateWindowEx
 push eax
 push SW_SHOWNORMAL
 push eax
 call ShowWindow
 call UpdateWindow
 mov edi,offset msg_
main_:
 push ebx
 push ebx
 push ebx
 push edi
 call GetMessage
 test eax,eax
 jz exit_
 push edi
 call TranslateMessage
 push edi
 call DispatchMessage
 jmp main_
exit_:
 push ebx
 call ExitProcess

win_proc proc
 push ebp
 mov ebp,esp

wp_hWnd   equ dword ptr [ebp+08h]
wp_uMsg   equ dword ptr [ebp+0Ch]
wp_wParam equ dword ptr [ebp+10h]
wp_lParam equ dword ptr [ebp+14h]

 cmp wp_uMsg,WM_CLOSE
 jne not_close
 push MB_YESNO
 push offset sure
 push offset ask
 push 0
 call MessageBox
 cmp eax,IDYES
 je not_close
 mov wp_uMsg,0
 jmp not_
not_close:
 cmp wp_uMsg,WM_DESTROY
 jne not_
 push 0
 call PostQuitMessage
 jmp end_
not_:
 cmp wp_uMsg,WM_COMMAND
 jne not_all
 mov eax,wp_wParam
 jmp dword ptr menus[eax*4]

menus dd offset menu_test
      dd offset menu_open
      dd offset menu_save
      dd offset menu_exit

menu_test:
 mov eax,offset test_msg
 jmp message_
menu_open:
 mov eax,offset open_msg
 jmp message_
menu_save:
 mov eax,offset save_msg
message_:
 push MB_OK
 push offset menu_name
 push eax
 push wp_hWnd
 call MessageBox
 jmp end_
menu_exit:
 push wp_hWnd
 call DestroyWindow
end_:
 leave
 ret 16
not_all:
 leave
 jmp DefWindowProc
win_proc endp

 end _start
А теперь давайте разберёмся в новых строках. Итак, первые 4 новые строки, это создание виртуальных значений, это я думаю понятно. Дальше мы создаём 4 переменные, одну с именем меню, и три с сообщениями для messagebox. Следующие добавление, это загрузка меню из ресурсов, выполняемая процедурой LoadMenu, которая получает два параметра, имя меню и handle процесса. Далее мы заменяем одну строку при создании окна. Прошу обратить внимание, что эта строка уже существовала в прошлых примерах, просто мы помещали в стек не handle меню, а 0! Единственное, что нам осталось сделать, это добавить в процедуру нашего окна обработку событий меню. При получении сообщения от меню, главное сообщение содержит команду WM_COMMAND, а уже первый параметр сообщения содержит номер выбранного меню. То есть нам надо добавить только обработку сообщения wm_command. И только если сообщение действительно WM_COMMAND, обрабатывать первый параметр.
При этом в файл user32.inc следует дописать следующие строки:
  extrn __imp__LoadMenuA@8:dword
  extrn __imp__DestroyWindow@4:dword
LoadMenu      equ __imp__LoadMenuA@8
DestroyWindow equ __imp__DestroyWindow@4
А в файл def32.inc
MB_OK      equ 0
WM_COMMAND equ 111h
Причём, если Вы получили файлы inc от меня, то проверьте, возможно это уже записано в них! Тогда нет надобности записовать всё дважды. Если что не работает, пишите.
Следующие файлы можно скачать к этому уроку:
0006asm - код программы
0006exe - сама программа
0006inc - файлы дополнения
0006lib - библиотеки импорта
К тому же Вам надо будет скачать улиту компиляции ресурсов, например, отсюда, формат WinRaR.И дописать строку в doit.bat:
ml /c /coff window.asm
rc /r menu.rc
link32 window.obj menu.res /subsystem:windows


Вот вроде и всё! Если есть какие вопросы, пишите, обязательно отвечу. На счёт следующего урока, даже не знаю на счёт чего писать, у меня есть две, на мой взгляд, одинаково интересные темы, "Работа с файлами в Windows" и "Значение Диологов в Windows". Напишите мне, что Вам кажется более интересным. Пишите, Dark_Lord@RusFAQ.ru, Dark_Lord@land.ru.

Сайт управляется системой uCoz