На этом шаге мы рассмотрим создание и работу с диалоговыми окнами.
Диалоговые окна являются наиболее сложными элементами ресурсов. В отличие от ресурсов, которые мы до сих пор рассматривали, для диалога не задается идентификатор. Обращение к диалогу происходит по его имени (строке).
#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. //Определение констант. #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" }
;Константы. ;Сообщение приходит при закрытии окна. 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
.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 и в конечном итоге - к выходу из программы.
Ранее было сказано, что процедура диалогового окна должна возвращать ненулевое значение, если она берет на себя обработку данного сообщения. Как видно из данного примера, в принципе, в этом не всегда есть необходимость. В дальнейшем мы акцентируем внимание на тех случаях, когда это действительно необходимо.
Следующий шаг мы посвятим работе с меню.