Графические программы для Windows практически никогда не ограничиваются одним меню, потому что меню не позволяет ввести никакой реальной информации — только выбрать тот или иной пункт из предложенных. Конечно, можно в цикле после GetMessage или PeekMessage обрабатывать события передвижения мыши и нажатия клавиш, и так делают в интерактивных программах, например в играх, но, если требуется ввод текста с возможностью его редактирования, выбор файла на диске и любые другие нетривиальные действия, основным средством ввода информации в программах для Windows оказываются диалоги.
Диалог описывается, так же как и меню, в файле ресурсов, но, если меню было очень просто написать вручную, ради диалогов, скорее всего, придется пользоваться каким-нибудь редактором диалогов, идущим в комплекте с вашим любимым компилятором, если, конечно, вы не знаете в точности, по каким координатам будет располагаться каждый контрол (активный элемент диалога).
// windlg.rc // Файл ресурсов, описывающий диалог, используемый в программе windlg.asm. // Все следующие определения можно заменить на #include // <winuser.h>, если он есть:
ZZZ_Dialog DIALOG 10,10,205,30 // x, у, ширина, высота STYLE DS_CENTER | DS_MODALFRAME | DS_3DLOOK | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED CAPTION "Win32 assembly dialog example" // заголовок MENU ZDLG_MENU // меню BEGIN // начало списка контролов EDITTEXT IDC_EDIT,15,7,111,13,ES_AUTOHSCROLL | ES_LEFT PUSHBUTTON "E&xit",IDC_EXIT,141,8,52,13 END
ZDLG_MENU MENU // меню BEGIN POPUP "Test" BEGIN MENUITEM "Get Text",IDM_GETTEXT MENUITEM "Clear Text",IDM_CLEAR MENUITEM SEPARATOR MENUITEM "E&xit",IDM_EXIT END END
В качестве простого примера использования диалога покажем, как можно его применять, даже не регистрируя новый класс. Для этого надо просто создать диалог командой CreateDialog или одним из ее вариантов, не создавая никакого окна-предка. Все сообщения от диалога и окон, которые он создает, будут посылаться в процедуру-обработчик типа DialogProc, аналогичную процедуре WindowProc.
; windlg.asm ; Графическое win32-приложение, демонстрирующее работу с диалогом
include def32.inc include kernel32.inc include user32.inc
.386 .model flat .data dialog_name db "ZZZ_Dialog",0 ; имя диалога в ресурсах .data? buffer db 512 dup(?) ; буфер для введенного текста .code _start: xor ebx,ebx ; в EBX будет 0 для команд push 0 ; (короче в 2 раза) ; определить идентификатор нашей программы push ebx call GetModuleHandle ; запустить диалог push ebx ; значение, которое перейдет как параметр WM_INITDIALOG push offset dialog_proc ; адрес процедуры типа DialogProc push ebx ; идентификатор окна-предка (0 - ничей диалог) push offset dialog_name ; адрес имени диалога в ресурсах push eax ; идентификатор программы, в ресурсах которой ; находится диалог (наш идентификатор в ЕАХ) call DialogBoxParam ; выход из программы push ebx call ExitProcess ; ; процедура dialog_proc ; вызывается диалогом каждый раз, когда в нем что-нибудь происходит ; именно здесь будет происходить вся работа программы ; ; процедура не должна изменять регистры EBP,EDI,ESI и ЕВХ! ; dialog_proc proc near ; так как мы получаем параметры в стеке, построим стековый кадр push ebp mov ebp,esp ; процедура типа DialogProc вызывается со следующими параметрами: dp_hWnd equ dword ptr [ebp+08h] ; идентификатор диалога dp_uMsg equ dword ptr [ebp+0Ch] ; номер сообщения dp_wParam equ dword ptr [ebp+10h] ; первый параметр dp_lParam equ dword ptr [ebp+14h] ; второй параметр
mov ecx,dp_hWnd ; ECX будет хранить идентификатор диалога, mov eax,dp_uMsg ; a EAX - номер сообщения, cmp eax,WM_INITDIALOG ; если мы получили WM_INITDIALOG jne not_initdialog push IDC_EDIT push dp_hWnd call GetDlgItem ; определить идентификатор push eax ; окна редактирования текста call SetFocus ; и передать ему фокус, not_initdialog: cmp eax,WM_CLOSE ; если мы получили WM_CLOSE, jne not_close push 0 push ecx call EndDialog ; закрыть диалог, not_close: cmp eax,WM_COMMAND ; если мы получили WM_COMMAND, jne not_command mov eax,dp_wParam ; EAX = wParam (номер сообщения), cmp dp_lParam,0 ; если lparam ноль - сообщение от меню, jne lParam_not_0 cmp ax,IDM_GETTEXT ; если это пункт меню Get Text jne not_gettext push 512 ; размер буфера push offset buffer ; адрес буфера push IDC_EDIT ; номер конрола редактирования push ecx call GetDlgItemText ; считать текст в buffer push MB_OK push offset dialog_name push offset buffer push dp_hWnd call MessageBox ; и показать его в MessageBox, not_gettext: cmp eax,IDM_CLEAR ; если это пункт меню Clear jne not_clear push 0 ; NULL push IDC_EDIT ; номер контрола push ecx call SetDlgItemText ; установить новый текст, not_clear: cmp eax,IDM_EXIT ; если это пункт меню Exit jne not_exit push 0 ; код возврата push ecx ; идентификатор диалога call EndDialog ; закрыть диалог lParam_not_0: ; lParam не ноль - сообщение от контрола, cmp eax,IDC_EXIT ; если сообщение от кнопки Exit, jne not_exit shr eax,16 cmp eax,BN_CLICKED ; если ее нажали push 0 ; код возврата push ecx ; идентификатор диалога call EndDialog ; закрыть диалог not_exit: xor eax,eax ; после обработки команды inc eax ; DialogProc должен возвращать TRUE (eax = 1) leave ret 16 ; конец процедуры not_command: ; сюда передается управление, если мы получили ; какое-то незнакомое сообщение xor еах,еах ; код возврата FALSE (eax = 0) leave ret 16 ; конец процедуры dialog_proc endp end _start