[Назад] [Далее]

7.3.4. Диалоги

Графические программы для Windows практически никогда не ограничиваются одним меню, потому что меню не позволяет ввести никакой реальной информации — только выбрать тот или иной пункт из предложенных. Конечно, можно в цикле после GetMessage или PeekMessage обрабатывать события передвижения мыши и нажатия клавиш, и так делают в интерактивных программах, например в играх, но, если требуется ввод текста с возможностью его редактирования, выбор файла на диске и любые другие нетривиальные действия, основным средством ввода информации в программах для Windows оказываются диалоги.

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

// windlg.rc
// Файл ресурсов, описывающий диалог, используемый в программе windlg.asm.
// Все следующие определения можно заменить на #include
// <winuser.h>, если он есть:

// стили для диалогов
#define DS_CENTER        0x0800L
#define DS_MODALFRAME    0x80L
#define DS_3DLOOK        0x0004L

// стили для окон
#define WS_MINIMIZEBOX   0x00020000L
#define WS_SYSMENU       0x00080000L
#define WS_VISIBLE       0x10000000L
#define WS_OVERLAPPED    0x00000000L
#define WS_CAPTION       0xC00000L

// стили для редактора
#define ES_AUTOHSCROLL   0x80L
#define ES_LEFT          0
#define ZDLG_MENU        7

// идентификаторы контролов диалога
#define IDC_EDIT         0
#define IDC_BUTTON       1
#define IDC_EXIT         2

// идентификаторы пунктов меню
#define IDM_GETTEXT      10
#define IDM_CLEAR        11
#define IDM_EXIT         12

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-приложение, демонстрирующее работу с диалогом

; идентификаторы контролов (элементов диалога)
IDC_EDIT           equ     0
IDC_BUTTON         equ     1
IDC_EXIT           equ     2

; идентификаторы элементов меню
IDM_GETTEXT        equ     10
IDM_CLEAR          equ     11
IDM_EXIT           equ     12

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

Добавления в наш user32.inc:

между ifdef _TASM_ и else:

                   extrn          DialogBoxParamA:near
                   extrn          GetDlgItem:near
                   extrn          SetFocus:near
                   extrn          GetDlgItemTextA:near
                   extrn          SetDlgItemTextA:near
                   extrn          EndDialog:near
DialogBoxParam     equ            DialogBoxParamA
GetDlgltemText     equ            GetDlgItemTextA
SetDlgltemText     equ            SetDlgItemTextA

между else и endif:

                   extrn          __imp__DialogBoxParamA@20:dword
                   extrn          __imp__GetDlgItem@8:dword
                   extrn          __imp__SetFocus@4:dword
                   extrn          __imp__GetDlgItemTextA@16:dword
                   extrn          __imp__SetDlgItemTextA@12:dword
                   extrn          __imp__EndDialog@8:dword
DialogBoxParam     equ            __imp__DialogBoxParamA@20
GetDlgItem         equ            __imp__GetDlgItem@8
SetFocus           equ            __imp__SetFocus@4
GetDlgItemText     equ            __imp__GetDlgItemTextA@16
SetDlgItemText     equ            __imp__SetDlgItemTextA@12
EndDialog          equ            __imp__EndDialog@8

Добавления к файлу def32.inc:

; из winuser.h
WM_INITDIALOG      equ         110h
WM_CLOSE           equ         10h
BN_CLICKED         equ         0

п»ї
"target=_blank><\/a>") //-->