Автор: Роман Панышев (irrona)


Диалоговые окна

      Win32 API содержит множество функций, сообщений и встроенных элементов управления, помогающих разработчикам создавать и сопровождать диалоговые окна. Данная статья содержит следующие разделы, посвящённые диалоговым окнам в WIndows:

Когда следует использовать диалоговые окна

      Во многих приложениях диалоговые окна используются в качестве вспомогательных окон, обеспечивающих взаимодействие пользователя с программой. Они служат:

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

      Для создания любого типа диалоговых окон нужен шаблон диалогового окна, в котором указаны стили окна и перечислены элементы управления, находящиеся в окне (для всех элементов управления указываются их названия, подписи, размеры, стили и координаты). Кроме этого нужна оконная процедура диалогового окна, в которой можно будет отлавливать и обрабатывать сообщения, поступающие от системы и элементов управления. Шаблон диалогового окна можно создать вручную (для этого необходимо знать синтаксис - если интересует загляните в файл справки rc.hlp в каталоге bin установленного masm32), либо с помощью редакторов ресурсов (некоторые средства разработки содержат встроенные редакторы ресурсов, например в WinAsm и RadAsm имеются таковые). Созданный текстовой файл сохраняется с расширением RC и затем, с помощью утилиты rc.exe компилируется в бинарный (двоичный) ресурсный файл с расширением RES. Полученный таким образом шаблон диалогового окна можно слинковать с исполняемым файлом приложения, либо загружать в память во время работы приложения. Процедура диалогового окна - это функция обратного вызова, которую операционная система вызывает при взаимодействии с диалоговым окном конечного пользователя. Хотя процедура окна диалога и схожа с процедурой основного окна приложения, некоторые отличия всё же имеются. Но об этом ниже.

      Создать диалоговое окно в приложении можно используя либо функцию DialogBox,

   invoke DialogBox, hInstance, lpTemplate, hWndParent, lpDialogFunc    ; где lpTemplate - указатель на имя
                                                    ; или идентификатор шаблона диалогового окна,
                                                    ; hWndParent - идентификатор окна-владельца,
                                                    ; lpDialogFunc - точка входа в процедуру диалогового окна.

создающую модальное диалогове окно, либо функцию CreateDialog,

    invoke CreateDialog, hInstance, lpTemplate, hWndParent, lpDialogFunc    ; описание параметров см. выше

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

      Операционная система использует встроенный специальный класс окна для создания диалоговых окон. Также, после создания окна диалога, система создает специальную оконную процедуру обработки сообщений для этого окна. Изменить внутренние (системные) стили и процедуру Вы не можете. Единственный путь взаимодействия с диалоговым окном - это использование процедуры диалогового окна, написанной вами. Правда есть одно утешение - этой процедуры вам должно хватить с лихвой, как для управления самим окном, так и для управления элементами управления, расположенными на нем.

Окно-владелец диалогового окна

      Большинство диалоговых окон имеют окно-владельца (проще, родителя). Как вы помните, при создании диалога необходимо указывать идентификатор окна-владельца. Система использует этот идентификатор для установки позиции диалогового окна и размещения его поверх (впереди) родительского окна. Кроме этого, система отсылает родительскому окну сообщения о событиях, происходящих в диалоговом окне.

      Операционная система автоматически скрывает или уничтожает диалоговое окно при скрытии или уничтожении его владельца. Для нас это означает одно - нет необходимости отслеживать состояние родительского окна, за этим приглядывает сама система. Как говорится, баба с возу...

      Теоретически, конечно, можно создать диалоговое окно без указания родителя. Но фирма Microsoft настоятельно не рекомендует этого делать. Поскольку в таком случае система не несет никакой ответственности за поведение окна диалога, вам придется самим обеспечивать это поведение. А это, мне кажется, довольно большая и нудная работа. Хотя... может быть для каких-то особых целей вам сможет это понадобиться... дерзайте.

Окна сообщений

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

    invoke MessageBox, hWnd, lpText, lpCaption, uType    ; где hWnd - идентификатор родителя,
                                                         ; lpText - текст сообщения,
                                                         ; lpCaption - текст заголовка,
                                                         ; uType - иконки+кнопки
    invoke MessageBoxEx, hWnd, lpText, lpCaption, uType, wLanguageId    ; идентификатор языка

      Для указания кнопок окна сообщения используют следующие битовые константы:

Константа Значение (Hex) Описание
MB_OK 0 Окно сообщения содержит только одну кнопку OK. Это стиль по-умолчанию.
MB_OKCANCEL 1 Кнопки OK и Cancel (Отмена).
MB_ABORTRETRYIGNORE 2 Кнопки Abort (Прервать), Retry (Повторить), Ignore (Игнорировать).
MB_YESNOCANCEL 3 Кнопки Yes (Да), No (Нет), Cancel (Отмена).
MB_YESNO 4 Кнопки Yes (Да) и No (Нет).
MB_RETRYCANCEL 5 Кнопки Retry (Повторить) и Cancel (Отмена).
MB_CANCELTRYCONTINUE 6 Кнопки Cancel (Отмена), Try (Повторить), Continue (Продолжить). Используйте вместо MB_ABORTRETRYIGNORE.
MB_HELP 4000 Добавляет к существующим кнопкам кнопку Help (Помощь). При нажатии кнопки Help (Помощь) система посылает родительскому окну сообщение WM_HELP.

      Для указания иконок окна сообщения используют следующие битовые константы:

Константа Значение (Hex) Описание
MB_ICONSTOP
MB_ICONERROR
MB_ICONHAND
10 Позволяет вставить в окно соощения иконку знака "Стоп".
MB_ICONQUESTION 20 Иконка вопросительного знака.
MB_ICONEXCLAMATION
MB_ICONWARNING
30 Иконка восклицательного знака.
MB_ICONINFORMATION
MB_ICONASTERISK
40 Иконка с буквой i в круге.

      Для указания выделенной по-умолчанию кнопки используют следующие битовые константы:

Константа Значение (Hex) Описание
MB_DEFBUTTON1 0 Выделена первая кнопка.
MB_DEFBUTTON2 100 Выделена вторая кнопка.
MB_DEFBUTTON3 200 Выделена третья кнопка.
MB_DEFBUTTON4 300 Выделена четвертая кнопка.

      Для указания модальности окна сообщения используют следующие битовые константы:

Константа Значение (Hex) Описание
MB_APPLMODAL 0 Устанавливается по-умолчанию. Пользователь должен закрыть окно сообщения, чтобы продолжить работу с родительским окном. Пользователь может взаимодействовать с окнами других процессов. Если это позволяет иерархия окон приложения, в некоторых случаях пользователь может взаимодействовать с другими окнами текущего приложения. Все дочерние окна родительского окна неактивны, но всплывающие окна - остаются активными.
MB_SYSTEMMODAL 1000 Поведение такое же, как у MB_APPLMODAL. Отличие состоит в том, что при создании окна применяется стиль WS_EX_TOPMOST, что позволяет выводить окно поверх всех открытых окон системы. Используйте этот стиль в тех случаях, когда необходимо сообщить пользователю о серьёзных ошибках, требующих его срочного вмешательства.
MB_TASKMODAL 2000 Поведение такое же, как у MB_APPLMODAL. отличие состоит в том, что в случае отсутствия идентификатора родителя, деактивируются все окна верхнего уровня, принадлежащие данному процессу. Удобно использовать, например при написании библиотек. Поскольку в библиотеке родительского окна нет, применение такого флага позволяет деактивировать окна приложения, использующего данную библиотеку.

      Дополнительные стили окна сообщения устанавливаются следующими битовыми константами:

Константа Значение (Hex) Описание
MB_SETFOREGROUND 10000 Окно сообщения выводится на передний план. На самом деле ОС вызывает функцию SetForegroundWindow.
MB_DEFAULT_DESKTOP_ONLY 20000 В Windows 95/98 этот флаг эффекта не имеет. В Windows NT 4.0 и более поздних этот флаг работает наподобие флага MB_SERVICE_NOTIFICATION, с оговоркой, что система выводит окно сообщения на рабочий стол, принятый по-умолчанию.
MB_SERVICE_NOTIFICATION_NT3X 40000 Работает только в Windows NT 4.0 и более поздних. Данный флаг связан с флагом MB_SERVICE_NOTIFICATION, используемым в Windows NT 3.51.
MB_TOPMOST 40000 Окно сообщения создается с использованием стиля WS_EX_TOPMOST.
MB_RIGHT 80000 Текст в окне сообщения выравнивается по правому краю.
MB_RTLREADING 100000 Выводит текст заголовка и сообщения в формате чтения справа-навлево (RTL) на системах с арабским языком и ивритом.
MB_SERVICE_NOTIFICATION 200000 в Windows NT 4.0 и более поздних данный флаг используется в окнах сообщений, вызываемых из сервисов Windows. Окно сообщения выводится на активный рабочий стол системы, даже если пользователь не входил в систему.
При создании окна сообщения установите параметр hWnd в NULL, поскольку диалог может выводится на рабочем столе, заданном иным идентификатором, отличным от активного.

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

      В то же время, поскольку окно сообщения все-таки является диалоговым окном, система (если указан параметр hWnd) посылает родительскому окну сообщения WM_CANCELMODE (1Fh) и WM_ENABLE (0Ah). Соответственно Вы получаете возможность обрабатывать эти сообщения.

Модальные диалоговые окна

      Модальное окно - это всплывающее окно с системным меню, заголовком и тонкой границей. Это значит, что шаблон окна должен содержать стили WS_POPUP (80000000h), WS_SYSMENU (80000h), WS_CAPTION (0C00000h) и DS_MODALFRAME (0080h). Стиль WS_VISIBLE (10000000h) можно не указывать, поскольку система сделает диалог видимым независимо от того, указан данный стиль или нет. Модальный диалог не может иметь стиль WS_CHILD (40000000h), иначе после создания диалог будет заблокирован и получить доступ к нему будет невозможно.

      Модальное диалоговое окно можно создать функциями DialogBox или DialogBoxIndirect.

    invoke DialogBox, hInstance, lpTemplate, hWndParent, lpDialogFunc
    invoke DialogBoxIndirect, hInstance, lpTemplate, hWndParent, lpDialogFunc

      Отличие последней состоит в том, что параметр lpTemplate должен быть не указателем на шаблон диалогового окна, а указателем на глобальную переменную в памяти компьютера, которая содержит загруженный шаблон диалогового окна. Шаблон диалога для этой функции должен содержать заголовок, описывающий окно диалога, за которым следуют дополнительные блоки данных, описывающие элементы управления, располагаемые в диалоговом окне. Можно использовать стандартный формат шаблона или расширенный.

      Стандартный формат включает две основных структуры DLGTEMPLATE для заголовка и DLGITEMTEMPLATE для элементов управления.

      Расширенный формат также включает две основных структуры DLGTEMPLATEEX для заголовка и DLGITEMTEMPLATEEX для элементов управления.

      Рассмотрением этих структур займемся ниже. Функции DialogBoxParam и DialogBoxIndirectParam также создают модальные диалоговые окна. Они идентичны функциям, указанным выше, но имеют один дополнительный параметр, в котором можно указать какое-нибудь значение, которое система при создании окна диалога перешлет в конную процедуру диалогового окна. Получить это значение можно в параметре lParam сообщения WM_INITDIALOG (110h). Разница между этими функциями также состоит во втором параметре, касающемся шаблона диалогового окна.

    invoke DialogBoxParam, hInstance, lpTemplateName, hWndParent, lpDialogFunc, dwInitParam
    invoke DialogBoxIndirectParam, hInstance, hDialogTemplate, hWndParent, lpDialogFunc, dwInitParam

      Как Вы думаете, что возвращают эти четыре функции по окончании своей работы по созданию модального диалогового окна? Кроме идентификатора диалогового окна, как сообщает MSDN, они возвращают некое 32-битное значение nResult, которое затем становится возвращаемым значением функции EndDialog. При неверном hWndParent, они возвращают 0. А при возникновении любой другой ошибки возвращают -1.

      После создания модальное диалоговое окно становится активным окном и остается таковым пока не будет уничтожено функцией EndDialog.

    invoke EndDialog, hDlg, nResult

      Вот он и вылез, наш пресловутый nResult. Благодаря ему родительское окно может определить насколько удачно прошло создание окна диалога.

      При создании модального диалога, система посылает родительскому окну (если таковое имеется) сообщение WM_CANCELMODE (1Fh). Получив это сообщение, приложение должно перевести фокус ввода в созданное диалоговое окно. Обработка сообщений модального диалогового окна происходит в собственном цикле обработки сообщений в процедуре модального окна диалога. Если сообщение, полученное этой процедурой не имеет отношения к диалоговому окну, например сообщение WM_QUIT (12h), оно пересылается родительскому окну на обработку.

Немодальные диалоговые окна

      Немодальное окно - это всплывающее окно с системным меню, заголовком и тонкой границей. Это значит, что шаблон окна должен содержать стили WS_POPUP (80000000h), WS_SYSMENU (80000h), WS_CAPTION (0C00000h) и WS_BORDER (800000h). Если Вы не укажете стиль WS_VISIBLE (10000000h), система не отобразит диалог на экране монитора. В таком случае, после создания диалога, можно заставить его проявиться с помощью функции ShowWindow.

    invoke ShowWindow, hWnd, nCmdShow

      Немодальные диалоговые окна, также как и модальные, создаются с помощью четырех функций: DialogBox, DialogBoxIndirect, DialogBoxParam и DialogBoxIndirectParam (см. выше).

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

    invoke SendMessage, hWnd, Msg, wParam, lParam

      Уничтожить диалоговое окно можно вызовом функции DestroyWindow, Которую можно привязать, например, на нажатие кнопки "Отмена" диалога. Если же в процедуре диалогового окна отсутствует вызов этой функции, тогда родительское окно перед закрытием должно вызвать DestroyWindow для диалога. Для закрытия немодальных диалоговых окон нельзя использовать функцию EndDialog.

    invoke DestroyWindow, hWnd

Шаблоны диалоговых окон

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

      Шаблоны диалоговых окон сначала создаются в текстовом виде, согласно требований операционной системы и компилятора ресурсов. В masm32 текстовой файл шаблона ресурсов имеет расширение RC. После чего, используя компилятор ресурсов, текстовой файл ресурсов компилируется в двоичный вид. В masm32 двоичный файл ресурса имеет расширение RES.

      Для создания простого диалогового окна, создайте новый файл с расширением RC и вставьте в него следующий шаблон диалогового окна:

    #define IDD_DLG 1001
    #define IDC_BUTTON 1002

    IDD_DLG DIALOGEX 0,0,221,127
    CAPTION "Самостоятельное диалоговое окно"
    FONT 8,"MS Sans Serif"
    STYLE 0x10cc0800
    EXSTYLE 0x00000000
    BEGIN
        CONTROL "Закрыть окно",IDC_BUTTON,"Button",0x50010000,73,80,74,22,0x00000000
    END

      В этом шаблоне объявлено окно диалога с псевдоидентификатором IDD_DLG равным 1001. Окно создаем с использованием расширенного стиля DIALOGEX. Размеры окна 221x127 dbu. Заголовок окна будет содержать текст "Самостоятельное диалоговое окно". Шрифт, используемый для текста диалогового окна и элементов управления, "MS Sans Serif" размером 8 кеглей. Стиль окна 10cc0800h, что означает WS_VISIBLE or WS_CAPTION or WS_SYSMENU or WS_THICKFRAME or DS_CENTER. Расширенных стилей нет. В окне будет присутствовать один элемент управления "Кнопка (Button)" с псевдоидентификатором IDC_BUTTON равным 1002 и текстом "Закрыть окно". Кнопка будет располагаться по координатам 73 dbu от верха окна и 80 dbu слева. Размеры кнопки 74x22 dbu. Стиль 50010000h. Кстати, заметьте, что константы стилей заданы в нотации языка С - это требование компилятора ресурсов. Вместо малопонятных цифр можно использовать текстовые описания констант, но для этого необходимо подключить файл resource.h, в котором определены основные константы стилей и несколько изменить шаблон.

    #include "resource.h"      ; включаемый файл должен находиться в каталоге с файлом шаблона

    #define IDD_DLG 1001
    #define IDC_BUTTON 1002

    IDD_DLG DIALOGEX 0,0,221,127
    CAPTION "Самостоятельное диалоговое окно"
    FONT 8,"MS Sans Serif"
    STYLE WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | DS_CENTER
    BEGIN
        CONTROL "Закрыть окно",IDC_BUTTON,"Button",WS_CHILD | WS_VISIBLE | WS_TABSTOP,73,80,74,22
    END

      Думаю во всем этом единственную трудность вызывает размерность dbu. Это я сократил сочетание слов "dialog base unit" (базовая единица диалога). Дело в том, что, в отличие от пикселов, принятых при задании координат и размеров основных окон, при создании диалоговых окон используется специальная единица, которую Microsoft называет единицей диалога. Эта контекстно-независимая единица принята в качестве стандарта для того, чтобы окно диалога смотрелось одинаково при любом разрешении экрана монитора. Пересчитать эту размерность в экранные координаты (пикселы) несложно, если знать как. Эта величина зависит от размера шрифта, указанного в шаблоне диалогового окна. Вертикальная dbu равна 1/4 средней ширины символа системного шрифта. Горизонтальная dbu равна 1/8 средней высоты символа системного шрифта. Псевдокод для вычисления количества пикселей для вертикальной dbu и горизонтальной dbu соответственно приведен ниже:

количество пикселей по горизонтали = 2 * горизонтальная dbu * (средняя ширина символа диалогового шрифта 
              / средняя ширина символа системного шрифта)
количество пикселей по вертикали = 2 * вертикальная dbu * (средняя высота символа диалогового шрифта 
              / средняя высота символа системного шрифта) 

      Средние ширину и высоту системного шрифта можно получить примерно так:

.data
    avgWidth    dd 0
    avgHeight   dd 0
    alphabet    db "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 0

.data?
    hdc      HDC ?
    hFont    HFONT ?
    hFontOld dd ?
    tm       TEXTMETRIC <>
    size     SIZE <>

.code
    invoke SelectObject, hdc, hFont
    mov hFontOld, eax
    invoke GetTextMetrics, hdc, addr tm
    invoke GetTextExtentPoint32, hdc, addr alphabet, 52, addr size
    ; Вычисляем среднюю ширину символа
    ; avgWidth = (size.cx/26+1)/2
    xor edx, edx
    mov eax, size.cx
    mov ecx, 26
    div ecx
    inc eax
    shr eax, 1
    mov avgWidth, eax
    ; Средняя высота символа
    mov eax, tm.tmHeight
    mov avgHeight, eax

      Но проще всего сконвертировать dbu в пикселы можно с помощью функции MapDialogRect.

    invoke MapDialogRect, hDlg, lpRect    ; где lpRect - структура RECT
                                      ; на входе заполняете её координатами диалога;
                                      ; на выходе получаете в ней экранные координаты (пикселы)

      Ну, так, мы сильно отвлеклись. После написания шаблона диалогового окна, его необходимо перевести в двоичный вид. Используем для этого утилиту rc.exe. В командной строке необходимо написать следующее,:

    rc.exe путь_к_файлу_шаблона_диалога.rc

и нажать Ввод. В результате в том же каталоге Вы получите скомпилированный ресурс в файле с расширением RES.

      Итак, у нас есть шаблон диалога. Время написать саму программу.

.386
.model flat, stdcall
option casemap :none
	include	\masm32\include\windows.inc
	include \masm32\include\kernel32.inc
	include \masm32\include\user32.inc

	includelib \masm32\lib\kernel32.lib
	includelib \masm32\lib\user32.lib

	DialogProc proto :DWORD, :DWORD, :DWORD, :DWORD

.data?
hInstance   HINSTANCE ?
CommandLine LPSTR ?

.code
start:
    invoke	GetModuleHandle, NULL
    mov	hInstance, eax
    invoke	GetCommandLine
    mov	CommandLine, eax
    invoke	DialogBoxParam, hInstance, 1001, NULL, addr DialogProc, 0
    invoke	ExitProcess, eax

DialogProc proc hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    .if uMsg == WM_INITDIALOG
    .elseif uMsg == WM_COMMAND
        invoke SendMessage, hDlg, WM_CLOSE, 0, 0
    .elseif uMsg == WM_CLOSE
        invoke EndDialog, hDlg, 0
        ret
    .endif
    xor	eax, eax
    ret
DialogProc endp
end start

      Ассемблируем программу и линкуем с шаблоном диалога:

    ML.EXE /c /coff путь_к_файлу_программы.asm
    LINK.EXE /SUBSYSTEM:WINDOWS /OPT:NOREF путь_к_файлу_программы.obj путь_к_шаблону_диалога.res

      Всё. Исходный код с примером создания диалогового окна находится в файле sample_5_1.rar.

      На примере Вы могли увидеть, что при создании шаблона диалогового окна необходимо указать один или более стилей окна. Стили диалоговых окон могут включать как обычные оконные стили (WS_POPUP (80000000h) или WS_SYSMENU (80000h)), так и специализированные стили диалоговых окон (DS_MODALFRAME (0080h) или DS_CENTER (800h)). При создании диалогового окна система посылает указанные в параметре STYLE шаблона стили в параметр dwStyle, а стили, указанные в параметре EXSTYLE шаблона - в параметр dwExStyle функции CreateWindowEx.

      Зачастую, диалоговые окна являются всплывающими окнами с меню и заголовком. Шаблон диалога должен содержать стиль WS_BORDER (800000h) для создания немодального и стиль DS_MODALFRAME (0080h) для создания модального диалогового окна. Если шаблон будет содержать какой-либо стиль, отличный от WS_POPUP (80000000h), например WS_OVERLAPPED (0), то в результате Вы получите не диалоговое, а пользовательское окно с указанными стилями.

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

Константа Значение (Hex) Описание
DS_ABSALIGN 1 Указывает на то, что координаты диалогового окна относительны экрану, иначе они относительны клиентской области родительского окна.
DS_SYSMODAL 2 Стиль устаревший. Применялся на 16-битных системах.
DS_3DLOOK 4 Использовался только на Windows NT 3.51 для создания 3-мерной границы диалогового окна. На остальных системах такая граница создается автоматически.
DS_FIXEDSYS 8 Заставляет диалоговое окно использовать SYSTEM_FIXED_FONT вместо SYSTEM_FONT. Это моноширный шрифт с системным шрифтом 16-битных систем Windows более ранних, чем Windows 3.0.
DS_NOFAILCREATE 10 Стиль применялся в Windows 95/98. Позволял системе создать диалоговое окно даже при наличии ошибок при создании окна.
DS_LOCALEDIT 20 Стиль устаревший. Применялся на 16-битных системах.
DS_SETFONT 40 Если указан - это значит, что шаблон диалогового окна содержит параметр FONT, в котором разработчик указывает имя и размер шрифта для диалогового окна и его элементов управления.
Если не указан ни DS_SETFONT, ни DS_SHELLFONT, значит шаблон не содержит параметра FONT.
DS_SHELLFONT 48 Является комбинацией стилей DS_SHELLFONT и FIXEDSYS. Позволяет создать одно диалоговое окно, которое будет иметь современный вид в Windows 2000 и более новых, и "классический" вид на Windows 95/98/NT4.
DS_MODALFRAME 80 Позволяет создать модальное диалоговое окно.
DS_NOIDLEMSG 100 Запрещает отправку родительскому окну сообщения WM_ENTERIDLE.
DS_SETFOREGROUND 200 Позволяет системе использовать функцию SetForegroundWindow для вывода окна диалога на передний план. Служит для привлечения особого внимания пользователя к диалогу.
В Windows 98/2000 система сама решает какой процесс имеет право выводить окна на передний план.
DS_CONTROL 400 Создает диалоговое окно, которое является дочерним по отношению к другому диалоговому окну. Например, приложение-мастер является диалоговым окном, вкладками которого являются другие (дочерние) диалоговые окна. Это позволяет перемещаться между дочерними диалогами как между элементами управления, используя назначенные комбинации клавиш.
DS_CENTER 800 Центрирует диалоговое окно относительно клиентской области родительского окна или, если "родитель" не задан, относительно экрана.
DS_CENTERMOUSE 1000 Центрирует диалоговое окно относительно курсора.
DS_CONTEXTHELP 2000 Создает в заголовке окна диалога кнопку с изображением вопросительного знака. При нажатии её, курсор принимает вид вопросительного знака, пока не будет выбран какой-либо из элементов управления диалогового окна. После выбора элемента управления, он получает сообщение WM_HELP, которое должен передать процедуре диалогового окна на обработку. Для обработки Вы можете вызвать функцию WinHelp с командой HELP_WM_HELP и указанием файла справки для элемента управления.

      О последнем стиле можно и подробнее. Функция WinHelp позволяет отображать контекстную справку по элементу управления или действию, либо работать с отдельными опциями справочной системы. Правда работает она только со старым форматом справочных файлов с расширением hlp. Пример использования функции приведен ниже.

.data
    hfile	db "c:\masm32\help\ASMINTRO.HLP", 0
.code
    .if uMsg == WM_HELP
        invoke WinHelp, hDlg, addr hfile, HELP_FORCEFILE, 0

      Кроме указания начальной позиции и размеров диалогового окна, его шаблон может содержать описание элементов управления, располагаемых в окне. Для каждого элемента управления необходимо указать его псевдоидентификатор (неповторяющийся в пределах приложения номер), тип (класс, на основе которого он создается), размеры, позицию (относительно клиентской области диалогового окна) и стили. Операционная система использует эту информацию при вызове функции CreateWindowEx, используемую для создания элементов управления. Функция вызывается для каждого элемента управления отдельно и в порядке их указания в шаблоне. Каждый элемент управления должен содержать стиль WS_CHILD (40000000h), поскольку все они являются дочерними окнами по отношению к окну диалога. Чтобы элемент управления стал видимым после создания диалога, не забудьте выставить стиль WS_VISIBLE (10000000h). Другие стили, такие как WS_BORDER (800000h), WS_DISABLED (8000000h), WS_TABSTOP (10000h) или WS_GROUP (20000h) указываются по мере надобности. Шаблон может также содержать стили, специфичные для данного класса элементов управления. Перед использованием специальных стилей различных элементов управления, ознакомьтесь со следующими статьями из MSDN:

      Псевдоидентификатор необходим элементу управления, чтобы процедура диалогового окна могла понять от которого из них поступило сообщение WM_COMMAND (111h). Единственный элемент управления, которому не обязателен идентификатор, элемент управления STATIC, - просто он не посылает сообщений WM_COMMAND (111h).

      Обычно при создании диалогового окна на нем располагают кнопки OK и CANCEL. Этим кнопкам назначают соответственно идентификаторы IDOK (1) и IDCANCEL (2), которые при получении диалоговым окном сообщения WM_COMMAND (111h), можно извлечь из параметра wParam оконной процедуры. Кстати системное меню (у диалогового окна оно содержит только два-три активных пункта: переместить и закрыть, а при использовании стиля WS_THICKFRAME еще и пункт размер), при выборе пункта "Закрыть", посылает сообщение WM_COMMAND (111h) с wParam, установленным в IDCANCEL (2).

      Как уже упоминалось выше, для вычисления размеров и позиции диалогового окна, операционная система использует среднюю ширину символа. По-умолчанию, система отрисовывает весь текст на диалоговом окне, используя шрифт SYSTEM_FONT (13). Если желаете отказаться от шрифта по-умолчанию, используйте оператор FONT шаблона диалогового окна,

    FONT размер_шрифта, "название шрифта"

в котором укажите размер шрифта и его название. Имейте в виду, что Windows 2000 использует по-умолчанию шрифт отличный от шрифта, принятого по-умолчанию в Windows 9x и Windows NT 4.0. Чтобы шрифты диалогового окна, отрисовывались одинаково адекватно во всех этих системах, используйте шаблон DIALOGEX вместо DIALOG, в стили диалогового окна добавьте DS_SHELLFONT, а в операторе FONT укажите шрифт MS Shell Dlg. При таком раскладе система будет рисовать текст в диалоговом окне шрифтом Tahoma в Windows 2000 и шрифтом MS Sans Serif в Windows 9x и Windows NT 4.0.

      А вот теперь мы, похоже, добрались до самого интересного места данной статьи. Сейчас рассмотрим каким же всё-таки образом можно использовать шаблоны диалоговых окон, расположенные в памяти.

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

    #include "resource.h"
    
    #define IDD_DLG 1001
    #define IDSTATIC 1002
    #define IDBUTTON 1003

    IDD_DLG DIALOG 0,0,221,127
    CAPTION "Простое использование шаблона"
    FONT 8,"MS Sans Serif"
    STYLE WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_CENTER | DS_SETFONT
    BEGIN
        CONTROL "Элемент управления STATIC",IDSTATIC,"Static",WS_CHILD | WS_VISIBLE | WS_TABSTOP,50,40,130,22
        CONTROL "Кнопка",IDBUTTON,"Button",WS_CHILD | WS_VISIBLE | WS_TABSTOP,73,80,74,22
    END

диалогового окна, на котором расположены элементами управления Static с идентификатором 1002 и Button (кнопка) - идентификатор 1003. Компилируем данный шаблон с помощью компилятора ресурсов. После этого открываем получившийся RES-файл любым hex-редактором и со смещения 64 (40h) байтов от начала файла копируем весь 16-ричный код, который присваиваем переменной в коде программы, показанном ниже, разбивая строку на отдельные байты. А переменную передаем функции DialogBoxIndirectParam. Название переменной я специально выделил, чтобы было более понятно.

.data
    dlgTemplate db 040h,008h,0C8h,010h,000h,000h,000h,000h,002h,000h,000h,000h,000h,000h,0DDh,000h
        db 07Fh,000h,000h,000h,000h,000h,01Fh,004h,040h,004h,03Eh,004h,041h,004h,042h,004h
        db 03Eh,004h,035h,004h,020h,000h,038h,004h,041h,004h,03Fh,004h,03Eh,004h,03Bh,004h
        db 04Ch,004h,037h,004h,03Eh,004h,032h,004h,030h,004h,03Dh,004h,038h,004h,035h,004h
        db 020h,000h,048h,004h,030h,004h,031h,004h,03Bh,004h,03Eh,004h,03Dh,004h,030h,004h
        db 000h,000h,008h,000h,04Dh,000h,053h,000h,020h,000h,053h,000h,061h,000h,06Eh,000h
        db 073h,000h,020h,000h,053h,000h,065h,000h,072h,000h,069h,000h,066h,000h,000h,000h
        db 000h,000h,001h,050h,000h,000h,000h,000h,032h,000h,028h,000h,082h,000h,016h,000h
        db 0EAh,003h,0FFh,0FFh,082h,000h,02Dh,004h,03Bh,004h,035h,004h,03Ch,004h,035h,004h
        db 03Dh,004h,042h,004h,020h,000h,043h,004h,03Fh,004h,040h,004h,030h,004h,032h,004h
        db 03Bh,004h,035h,004h,03Dh,004h,038h,004h,04Fh,004h,020h,000h,053h,000h,054h,000h
        db 041h,000h,054h,000h,049h,000h,043h,000h,000h,000h,000h,000h,000h,000h,001h,050h
        db 000h,000h,000h,000h,049h,000h,050h,000h,04Ah,000h,016h,000h,0EBh,003h,0FFh,0FFh
        db 080h,000h,01Ah,004h,03Dh,004h,03Eh,004h,03Fh,004h,03Ah,004h,030h,004h,000h,000h
        db 000h,000h,000h,000h

.code
start:
    invoke DialogBoxIndirectParam, hInstance, addr dlgTemplate, NULL, addr DialogProc, 0
invoke ExitProcess, eax DialogProc proc hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .if uMsg == WM_INITDIALOG .elseif uMsg == WM_COMMAND .if wParam == 1003 ; реакция на нажатие кнопки invoke SendMessage, hDlg, WM_CLOSE, 0, 0 .endif .elseif uMsg == WM_CLOSE invoke EndDialog, hDlg, 0 ret .endif xor eax, eax ret DialogProc endp end start

      Исходный код с примером создания диалогового окна находится в файле sample_5_2.rar. Данный способ хорош тем, что нет надобности разбираться в формате шаблона и составляющих его структурах заголовка и элементов управления. Минусом же его является негибкость. Изменить данный шаблон так, чтобы он отображал диалоговое окно с другим набором стилей или элементов управления, довольно сложно. И уж тем более, для этого нужно разбираться в правилах построения шаблона в памяти. Вот тут то мы и приходим ко второму варианту создания шаблона диалога в памяти.

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

DLGTEMPLATE DLGITEMTEMPLATE

    DLGTEMPLATE struct
        style              DWORD ?
        dwExtendedStyle    DWORD ?
        cdit               WORD ?
        x                  WORD ?
        y                  WORD ?
        cx                 WORD ?
        cy                 WORD ?
    DLGTEMPLATE ends
			

    DLGITEMTEMPLATE struct
        style              DWORD ?
        dwExtendedStyle    DWORD ?
        x                  WORD ?
        y                  WORD ?
        cx                 WORD ?
        cy                 WORD ?
        id                 WORD ?
    DLGITEMTEMPLATE ends
			
DLGTEMPLATEEX DLGITEMTEMPLATEEX

    DLGTEMPLATEEX struct
        dlgVer             WORD ?
        signature          WORD ?
        helpID             DWORD ?
        exStyle            DWORD ?
        style              DWORD ?
        cDlgItems          WORD ?
        x                  WORD ?
        y                  WORD ?
        cx                 WORD ?
        cy                 WORD ?
        menu               WORD ?
        windowClass        WORD ?
        title              BYTE titleLen dup(?)
        ; следующие члены структуры указываются,
        ; если член структуры style 
        ; имеет стиль DS_SETFONT или DS_SHELLFONT
        pointsize          WORD ?
        weight             WORD ?
        italic             BYTE ?
        charset            BYTE ?
        typeface           WORD stringLen dup(?)
    DLGTEMPLATEEX ends
			

    DLGITEMTEMPLATEEX struct
        helpID             DWORD ?
        exStyle            DWORD ?
        style              DWORD ?
        x                  WORD ?
        y                  WORD ?
        cx                 WORD ?
        cy                 WORD ?
        id                 WORD ?
        windowClass        WORD ?
        title              BYTE titleLen dup(?)
        extraCount         WORD ?
    DLGITEMTEMPLATEEX ends
			

      Как и в предыдущем примере за основу возьмем шаблон диалогового окна с двумя элементами управления.

      Правила построения шаблона в памяти требуют, чтобы текст заголовка окна, название класса, меню и имя шрифта диалога, указывались в юникод-формате. Для перевода ASCII-строки в юникод-строку используем функцию MultiByteToWideChar.

      Теперь смотрим как нам следует заполнить структуру DLGTEMPLATE. Первые 4 байта - стиль диалогового окна (двойное слово - DWORD). Устанавливаем его в значение WS_VISIBLE or WS_CAPTION or WS_SYSMENU or DS_CENTER или 10CC0840h. Расширенный стиль не используется, но место, предусмотренное под него, заполнить нулями нужно всё-равно (тоже DWORD). Далее указываем сколько элементов управления находится в диалоговом окне (2 байта или WORD). После этого вставляем по 2 байта (WORD) значения положение по-горизонтали (x), по вертикали (y), и размеры окна. На этом структура DLGTEMPLATE заполнена и после неё можно указать дополнительные байтовые массивы, описывающие меню, класс окна, заголовок и шрифт. Итак, следующие 2 байта (WORD), показывают имеется ли меню. Если 0, значит меню нет, если FFFF - массив имеет дополнительный эелемент, в котором указывается идентификатор ресурса меню в исполняемом файле, иначе - это название юникод-название ресурса меню. Следующие 2 байта (WORD) - класс окна диалога. Если 0, значит система использует класс по-умолчанию, если FFFF - массив имеет дополнительный элемент с идентификатором класса окна, иначе - это юникод-название зарегистрированного класса окна. За классом идет заголовок окна в юникод-формате или 2 нулевых байта, если заголовок не нужен. В случае использования оператора FONT, далее идет описание шрифта диалогового окна: 2 байта размер шрифта и юникод-строка название шрифта.

      За структурой, описывающей окно и дополнительными строковыми массивами, идут описания элементов управления, выравненные по границе двойного слова (DWORD) DLGITEMTEMPLATE. Стиль элемента управления - 4 байта. Расширенный стиль - 4 байта. Далее по 2 байта координаты и размеры элемента управления. После его псевдоидентификатор 2 байта. На этом структура DLGITEMTEMPLATE заканчивается и начинаются дополнительные байтовые массивы. Далее байтовый массив, описывающий класс окна элемента управления. Если первые 2 байта равны FFFF, значит далее указывается идентификатор предопределённого в операционной системе элемента управления, иначе система воспринимает байтовый массив, как юникод-строку с названием зарегистрированного класса окна.

hex-значение класса Описание
0080 Button (класс кнопки)
0081 Edit (класс текстового поля ввода)
0082 Static (класс нередактируемого текстового поля)
0083 List box (класс списка)
0084 Scroll bar (класс полосы прокрутки)
0085 Combo box (класс выпадающего списка)

      Далее юникод-строка текста, расположенного на элементе управления, выравненная по границе двойного слова. И в конце 2 байта - размер дополнительных данных и сами дополнительные данные (могут быть любого размера), которые система затем пересылает вместе с сообщением WM_CREATE в параметре lParam в процедуру окна.


;********* Заполнение шаблона заголовка диалога ***********;
DLGTEMPLATE
    стиль окна = 10CC0840h (DWORD)
    расширенный стиль = 0h (DWORD)
    кол-во контролов = 0h (WORD)
    x координата = 0h (WORD)
    y координата = 0h (WORD)
    ширина окна = 0DDh (WORD)
    высота окна = 07Fh (WORD)

Дополнительные массивы диалога
    меню = 0h (WORD)
    класс = 0h (WORD)
    заголовок = "Самостоятельное диалоговое окно" (в юникод-формате)
    размер шрифта = 08h (WORD)
    имя шрифта = "MS Sans Serif" (в юникод-формате)

;******** Заполнение шаблона элемента управления ************;

DLGITEMTEMPLATE
    стиль кнопки = 50010000h (DWORD)
    расширенный стиль = 0h (DWORD)
    x координата = 049h (WORD)
    y координата = 050h (WORD)
    ширина элемента = 04Ah (WORD)
    высота элемента = 016h (WORD)
    id элемента = 03EAh (WORD (1002 в десятичном виде))

Дополнительные массивы элемента управления
    есть описание = 0FFFFh (WORD)
	класс элемента = 080h (WORD)
	текст = "Закрыть окно" (в юникод-формате)
	размер дополнит. данных = 0h (WORD)
	дополнит. данные = 0h (DWORD)

;***********************************************************;

...и так далее для каждого последующего элемента управления

      Исходный код с примером создания диалогового окна из шаблона, подготовленного в памяти, находится в файле sample_5_3.rar. Думаю, обилие комментариев поможет Вам разобраться в примере.

Процедура диалогового окна

      Процедура диалогового окна похожа на процедуру основного (главного) окна тем, что операционная система посылает в неё сообщения о различных событиях. Отличие её от процедуры основного окна в том, что в ней не используется вызов функции DefWindowProc.

    invoke DefWindowProc, hWnd, uMsg, wParam, lParam

      Вместо этого, процедура диалогового окна возвращает TRUE, если происходит обработка сообщения, или FALSE - в ином случае. Каждая диалоговая процедура имеет следующий вид:

DlgProc proc hDlg:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    ; выбор и обработка сообщения

    ; стандартный выход
    xor eax, eax
    ret
DialogProc endp

      Основными сообщениями, которые обрабатываются в процедуре диалогового окна, конечно же являются WM_INITDIALOG (110h), отправляемое системой при инициализации диалогового окна, и WM_COMMAND (111h), отправляемое эементами управления (кроме, как было сказано выше, элемента STATIC).

Взаимодействие диалогового окна с клавиатурным вводом

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

Кнопка клавиатуры Действие
ALT+мнемоника
или
мнемоника
Перемещает фокус ввода на первый элемент управления, имеющий стиль WS_TABSTOP и расположенный после элемента управления STATIC с указанной мнемоникой.
DOWN
или
RIGHT
Перемещает фокус ввода на следующий элемент управления в группе элементов.
ENTER Посылает сообщение WM_COMMAND с параметром wParam, установленным в значение IDOK (1) или в значение идентификатора кнопки по-умолчанию (DEFPUSHBUTTON).
ESC Посылает сообщение WM_COMMAND с параметром wParam, установленным в значение IDCANCEL (2).
LEFT
или
UP
Перемещает фокус ввода на предыдущий элемент управления в группе элементов.
TAB Перемещает фокус ввода на следующий элемент управления, имеющий стиль WS_TABSTOP.
SHIFT+TAB Перемещает фокус ввода на предыдущий элемент управления, имеющий стиль WS_TABSTOP.

      Операционная система автоматически заботится об обеспечении реакции на нажатия указанных клавиш в модальных диалоговых окнах. В немодальных диалогах необходимо, при получении ожидаемого сообщения, сразу же послать его функции IsDialogMessage, которая позволяет это сообщение обработать именно впроцедуре диалогового окна. Иначе сообщение попадет в процедуру основного окна, где и будет обработано или потеряно. Если функция IsDialogMessage возвратит значение, отличное от 0, значит сообщение было успешно обработано.

    invoke IsDialogMessage, hDlg, uMsg

      И ещё на заметку. Поскольку кнопки клавиатуры LEFT, RIGHT, UP и DOWN используются для перемещения между элементами управления диалогового окна, их нельзя использовать для прокрутки содержимого окна. Поэтому, если в вашем диалоговом окне имеются полосы прокрутки, пользоваться ими придется при помощи альтернативных комбинаций клавиш клавиатуры. Назначение и обработка событий от нахначенных клавиш целиком и полностью ложится на разработчика программы.

Пользовательское диалоговое окно

      Иногда может возникнуть желание сделать своё диалоговое окно, отличное от того, что предлагает по-умолчанию операционная система Windows. В этом случае можно воспользоваться парой вариантов, представленных ниже.

      Во-первых, можно создать диалоговое окно, наследовав класс основного (главного) окна приложения. Для начала подготовьте шаблон диалогового окна. В заголовке шаблона должен быть указан оператор CLASS.

    CLASS "имя_класса"

      В коде приложения вызовите либо функцию GetClassInfo, передав ей в качастве параметра пустую структуру WNDCLASS, либо GetClassInfoEx с указателем на структуру WNDCLASSEX.


WNDCLASS struct style DWORD ? lpfnWndProc DWORD ? cbClsExtra DWORD ? cbWndExtra DWORD ? hInstance DWORD ? hIcon DWORD ? hCursor DWORD ? hbrBackground DWORD ? lpszMenuName DWORD ? lpszClassName DWORD ? WNDCLASS ends
invoke GetClassInfo, hInstance, lpClassName, lpWndClass ; где lpClassName - имя класса окна, параметры которого запрашиваются, ; lpWndClass - указатель на структуру WNDCLASS
WNDCLASSEX struct cbSize DWORD ? style DWORD ? lpfnWndProc DWORD ? cbClsExtra DWORD ? cbWndExtra DWORD ? hInstance DWORD ? hIcon DWORD ? hCursor DWORD ? hbrBackground DWORD ? lpszMenuName DWORD ? lpszClassName DWORD ? hIconSm DWORD ? WNDCLASSEX ends
invoke GetClassInfoEx, hInstance, lpClassName, lpWndClass ; где lpClassName - имя класса окна, параметры которого запрашиваются, ; где lpWndClass - указатель на структуру WNDCLASSEX

      В результате соответствующая структура будет заполнена значениями, присущими запрашиваему классу окна. Члены этой структуры вы вправе изменить по-своему вкусу. Для члена структуры cbWndExtra укажите значение DLGWINDOWEXTRA, для lpfnWndProc - указатель на процедуру диалогового окна для приема и обработки сообщений, и для lpszClassName - имя класса диалогового окна (такое же как в шаблоне). После этого регистрируете наследованный класс с помощью функции RegisterClass или RegisterClassEx, и затем создаете диалоговое окно. Процедура диалогового окна примерно такая же, как процедура основного окна. Единственное отличие состоит в том, что в процедуре диалогового окна вместо функции DefWindowProc нужно вызывать функцию DefDlgProc.

      Второй способ лучше всего применять при создании приложений, главным окном в которых выступает диалоговое окно. Последовательность действий примерно следующая. В функции WinMain Вы заполняете структуру WNDCLASSEX, в которой члену cbWndExtra задаете значение DLGWINDOWEXTRA, а члену lpszClassName структуры присваиваете указатель на имя класса диалогового окна, такое как в шаблоне. После этого регистрируете класс диалогового окна с помощью функции RegisterClass или, соответственно, RegisterClassEx, и затем создаете диалоговое окно. В этом случае процедура диалогового окна точно такая же, как и процедура основного окна.

      Исходный код с примером создания пользовательского диалогового окна находится в файле sample_5_4.rar.

Пример из жизни

      Всем известны модальные окна сообщений (MessageBox), о которых уже было рассказано выше и довольно подробно. Основной недостаток таких диалоговых окон состоит в том, что пока пользователь не отреагирует и не нажмёт кнопку в окне этого диалогового окна, он не сможет продолжать работу с приложением. Соответственно, возникает вопрос: каким образом расширить функционал такого окна, добавив в него завершение работы по истечении заданного интервала времени. В MSDN я нашёл пример создания такого окна. Слегка его переработал и выкладываю всем Вам для ознакомления. Сразу предупреждаю, код местами не оптимизирован (в принципе и не особо стремился); основной задачей считаю указать направление разработки таких окон.

      Исходный код с примером создания окна сообщения с таймаутом находится в файле sample_5_5.rar.


Предыдущая страница   Следующая страница


© 2005 ironahot@idknet.com - при использовании статей просьба делать ссылку на автора