Шаг 37.
Язык описания ресурсов. Диалоговые окна

    На этом шаге мы рассмотрим создание и работу с диалоговыми окнами.

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

#define WS_SYSMENU      0x00080000L
#define WS_MINIMIZEBOX  0x00020000L
#define WS_MAXIMIZEBOX  0x00010000L
DIAL1 DIALOG 0, 0, 240, 120
STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
CAPTION "Пример диалогового окна"
FONT 8, "Arial"
{
}

    Как видим, определение диалога начинается со строки, содержащей ключевое слово DIALOG. В этой же строке далее указывается положение и размер диалогового окна. Далее идут строки, содержащие другие свойства окна. Наконец идут фигурные скобки. В данном случае эти скобки - пустые. Это означает, что в окне нет никаких управляющих элементов. Тип окна, а также других элементов определяется константами, которые мы поместили в начале файла. Эти константы стандартны, и для языков С хранятся в файле RESOURCE.H. Мы, как и раньше, все константы будем определять непосредственно в файле ресурсов. Обращаем ваше внимание, что константы определяются согласно нотации языка С.

    Прежде чем разбирать пример из этого шага, рассмотрим особенности работы с диалоговыми окнами. Диалоговое окно очень похоже на обычное окно. Так же как обычное окно, оно имеет свою процедуру. Процедура диалогового окна имеет те же параметры, что и процедура обычного окна. Сообщений, которые приходят на процедуру диалогового окна, гораздо меньше. Но те, которые у диалогового окна имеются, в основном совпадают с аналогичными сообщениями для обычного окна. Только вместо сообщения WM_CREATE приходит сообщение WM_INITDIALOG. Процедура диалогового окна может возвращать либо нулевое, либо ненулевое значение. Ненулевое значение должно возвращаться в том случае, если процедура обрабатывает (берет на себя обработку) данное сообщение, и ноль - если предоставляет обработку системе.

    Отличия в поведении диалогового окна от обычного окна легко объяснить. Действительно, если вы создаете обычное окно, то все его свойства определяются тремя факторами:

    При создании диалогового окна все свойства заданы в ресурсах. Часть этих свойств задается, когда при вызове функции создания диалогового окна (DialogBox, DialogBoxParam и др.) неявно вызывается функция CreateWindow. Остальная же часть свойств определяется поведением внутренней функции, которую порождает система при создании диалогового окна. Если с диалоговым окном что-то происходит, то сообщение сначала приходит на внутреннюю процедуру, а затем вызывается процедура диалогового окна, которую мы создаем в программе. Если процедура возвращает 0, то внутренняя процедура продолжает обработку данного сообщения, если же возвращается ненулевое значение, внутренняя процедура не обрабатывает сообщение. Вот, вкратце, как работают механизмы, регулирующие работу диалогового окна. Рассмотрите теперь приведенную на этом шаге программу. Ниже будет дано ее разъяснение.


    Ресурсный файл dial.rc.
//Файл dial.rc.
//Определение констант.
#define WS_SYSMENU       0x00080000L
#define WS_MINIMIZEBOX   0x00020000L
#define WS_MAXIMIZEBOX   0x00010000L
//Идентификаторы.
#define STR1        1 
#define STR2        2 
#define IDI_ICON1   3 
//Определили пиктограмму.
IDI_ICON1  ICON  "help.ico" 
//Определение диалогового окна.
DIAL1 DIALOG  0, 0, 240, 120
STYLE WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
CAPTION  "Пример диалогового окна" 
FONT 8,  "Arial"
{
}
//Определение строк.
STRINGTABLE
{ 
  STR1, "Сообщение" 
  STR2, "Версия программы 1.00" 
}


    Заголовочный файл для приложения, содержащий определения констант, внешних процедур и структур (его имя pr37_1.asm).
;Константы.
;Сообщение приходит при  закрытии окна.
WM_CLOSE        equ 10h
WM_INITDIALOG   equ 110h
WM_SETICON	equ 80h
;Прототипы внешних процедур.
EXTERN  MessageBoxA@16:NEAR 
EXTERN  DialogBoxParamA@20:NEAR 
EXTERN  EndDialog@8:NEAR 
EXTERN  LoadStringA@16:NEAR 
EXTERN  LoadIconA@8:NEAR 
EXTERN  SendMessageA@16:NEAR 
EXTERN  ExitProcess@4:NEAR
EXTERN  GetModuleHandleA@4:NEAR
;Структуры
;Структура сообщения.
MSGSTRUCT  STRUC	
           MSHWND     DD ? ;Идентификатор окна, получающего сообщение.
           MSMESSAGE  DD ? ;Идентификатор сообщения.
           MSWPARAM   DD ? ;Доп. информация о сообщении.
           MSLPARAM   DD ? ;Доп. информация о сообщении.
           MSTIME     DD ? ;Время посылки сообщения.
           MSPT       DD ? ;Положение курсора во время посылки сообщения.
MSGSTRUCT ENDS	
Текст этого модуля, а также исходный файл ресурсов и файл с пиктограммой можно взять здесь.


    Основной файл приложения, содержит подключение файла pr37_1.asm (его имя pr37_2.asm).
.386P
;Плоская модель.
.MODEL FLAT, STDCALL
include pr37_1.asm
;Директивы компоновщику для подключения библиотек.
includelib c:\masm32\lib\user32.lib 
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\gdi32.lib
;------------------------------------------------
;Сегмент данных. 
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
     MSG         MSGSTRUCT <?>
     HINST       DD 0 ;Здесь хранится дескриптор приложения.
     TITLENAME   DB 'Текст в окне',0
     PA          DB 'DIAL',0
     BUF1        DB 40 DUP(0) ;Буфер для выводимого текста.
     BUF2        DB 40 DUP(0) ;Буфер для выводимого текста.
_DATA ENDS 
;Сегмент кода.
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START: 
;Получить дескриптор приложения.
     PUSH  0
     CALL  GetModuleHandleA@4
     MOV   [HINST], EAX
;------------------------ загрузить 1-ю строку
        PUSH 40           ;Длина буфера.
        PUSH OFFSET BUF1  ;Начальный адрес буфера.
        PUSH 1            ;Идентификатор строки.
        PUSH [HINST]      ;Дескриптор приложения.
        CALL LoadStringA@16 
;------------------------ загрузить 2-ю строку
        PUSH 40
        PUSH OFFSET BUF2
        PUSH 2
        PUSH [HINST]
        CALL LoadStringA@16 
;------------------------
        PUSH 0            ;MB_OK 
        PUSH OFFSET BUF1  
        PUSH OFFSET BUF2
        PUSH 0            
        CALL MessageBoxA@16
;------------------------ создать диалоговое окно
        PUSH 0            
        PUSH OFFSET WNDPROC ;Процедура окна.
        PUSH 0            
        PUSH OFFSET PA    ;Название ресурса (DIAL1).
        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 FINISH 
;Загрузить пиктограмму.
        PUSH 3           ;Идентификатор пиктограммы.
        PUSH [HINST]     ;Идентификатор процесса.
        CALL LoadIconA@8 ;Установить пиктограмму.
        PUSH EAX
        PUSH 0           ;Тип пиктограммы (маленькая).
        PUSH WM_SETICON
        PUSH DWORD PTR [EBP+08H]
        CALL SendMessageA@16 
FINISH: 
        POP  EDI 
        POP  ESI 
        POP  EBX 
        POP  EBP 
        MOV  EAX,0 
        RET  16
WNDPROC ENDP 
_TEXT   ENDS 
        END START
Текст этой программы можно взять здесь.

    Результат работы программы представлен на рисунке 1:


Рис.1. Результат работы приложения

    Рассмотрим теперь, как работает эта программа.

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

    Перед тем как вызвать диалоговое окно, демонстрируется то, как нужно работать с таким ресурсом, как строка. Как видите, это достаточно просто. При помощи функции LoadString строка загружается в буфер, после чего с ней можно работать, как с обычной строкой.

    Вызов диалогового окна достаточно очевиден, так что перейдем сразу к процедуре диалогового окна. Начнем с сообщения WM_INITDIALOG. Это сообщение, как и сообщение WM_CREATE для обычного окна, приходит один раз при создании окна. Это весьма удобно для выполнения некоторых операций в самом начале программы, иными словами, для инициализации. Мы используем эту возможность для определения пиктограммы диалогового окна. В начале загружаем пиктограмму, а далее посылаем сообщение установить пиктограмму для данного окна (WM_SETICON).

    Вторым сообщением, которое мы обрабатываем, является WM_CLOSE. Это сообщение приходит, когда происходит щелчок мышью по крестику в правом верхнем углу экрана. По получении этого сообщения выполняется функция EndDialog, что приводит к удалению диалогового окна из памяти, выходу из функции DialogBoxParamA и в конечном итоге - к выходу из программы.

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

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




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