5. Прикладной интерфейс для WINDOWS
Данная глава посвящена программированию на языке С++ в операционной
системе Windows . Операционная система Windows ( NT /95/98/2000) ориентирована
на многооконный интерфейс. После загрузки в память программа пользователя
ожидает сообщения, посылаемого ей операционной системой Windows . Для приема
сообщения программа запускает цикл обработки сообщений. В этом цикле сообщения
выбираются из очереди сообщений для данной программы и направляются обратно к
операционной системе Windows , которая вызывает оконную функцию, передавая ей
эти сообщения в качестве параметров. Программа получает доступ к ресурсам
Windows , используя множество функций API (
Application Program Interface – Программный Интерфейс Приложений). Поддержку
независимого от графического оборудования обмена обеспечивает GDI
( Graphics Device Interface – Интерфейс Графических Устройств),
который является подмножеством API . 5.1. Приложения
Windows Поскольку Windows поддерживает 32 – разрядную
адресацию, некоторые типы данных изменили свою размерность: типы int
и unsigned стали 32 – битными, при объявлении
указателей не требуется их определение как near или far
(если такие определения встречаются, то они игнорируются).
Компоненты окна . Каждое окно имеет рамку,
определяющую его границы. В верхнем левом углу находится иконка
( пиктограмма) системного меню. Щелчок
мыши на этой пиктограмме открывает системное меню. Справа от иконки находится
заголовок окна . В правом верхнем углу – кнопки
минимизации, полноэкранной развертки и закрытия окна. Действие программы
отображается в рабочей области. Многие окна имеют горизонтальные и вертикальные
линейки прокрутки. Главная подпрограмма и оконные функции.
Приложения, использующие API , начинают свое выполнение с вызова
WinMain() вместо вызова main() . Подпрограмма
WinMain() является главной и должна быть описана как WINAPI .
Когда операционная система Windows посылает сообщения приложению, она вызывает
оконные функции и сообщения передает через параметры этих функций. Все оконные
функции должны объявляться как LRESULT CALLBACK .
5.2. Каркас приложения Главная подпрограмма
WinMain() должна выполнять следующие действия: •
определить класс окна (заполнить поля фрейма); · зарегистрировать окно в
системе Windows ; • создать окно данного класса; • вывести
окно на экран; • запустить цикл обработки сообщений. Оконная
функция WindowFunc() должна обрабатывать все сообщения,
относящиеся к данному окну. В приведенном ниже примере обрабатывается лишь
сообщение WM_DESTROY о закрытии окна. Фрейм окна определен
структурой WNDCLASS wcl . Окно создается с помощью функции
CreateWindow() . Добавлены типы данных: HANDLE – число,
идентифицирующее некоторый ресурс (32 бита), HWND – 32 – разрядное целое
число, используемое как дескриптор окна, WORD – 16 – разрядное короткое
целое без знака, DWORD – длинное целое без знака, UINT – 32 – разрядное
беззнаковое целое, BOOL – принимает значения 0 или 1, так называемый булевый
тип, BYTE – 8 – разрядное целое без знака, LPSTR – указатель на строку,
LPCSTR – постояный указатель на строку. Ниже приведём текст программы,
реализующей каркас приложения: #include <windows.h> #include
<string.h> // 1. Набрать данный текст, вызвав bcw // 2. Создать
проект: File -> New -> Project // (ввести имя, например karkas.ide
вместо proj0001.ide) // 3. Удалить *.def // 4. Нажать [Ctrl]+ F9
#include<stdio.h> LRESULT CALLBACK
WindowFunc(HWND,UINT,WPARAM,LPARAM); char szwinname[] = "May
Window"; int WINAPI WinMain(HINSTANCE hthisinst, HINSTANCE
hprevinst, LPSTR lpszargs, int nwinmode) { HWND hwnd ; //
дескриптор (заголовок) окна, т.е. номер, // единственным образом
идентифицирующий окно MSG msg; // сообщение состоит из HWND окна, //
получающего сообщение, UINT (номер) сообщения // LPARAM , WPARAM , DWORD
time , POINT pt -позиция курсора WNDCLASS wcl ; wcl .
hInstance = hthisinst ; // дескриптор данного приложения wcl . lpszClassName
= szwinname ; // имя класса окна wcl . lpfnWndProc = WindowFunc ; // оконная
функция wcl.style = 0; // стиль по умолчанию wcl.hIcon =
LoadIcon (NULL, IDI_APPLICATION);// как exe - MSDOS // IDI_ASTERISK -
перевернутый воскл знак (информация) // IDI_EXCLAMATION - внимание (воскл
знак в желтом тр-ке) // ID_HAND - знак стоп (два скрещенных отрезка в
красном круге) // IDI_QUESTION - вопрос в белом круге wcl . hCursor =
LoadCursor ( NULL , IDC _ ARROW ); // курсор выводится как стрелка // CROSS
(+), IBEAM (верт черта), ( WAIT ) пес часы wcl.lpszMenuName = NULL;
wcl.cbClsExtra = 0; wcl.cbWndExtra = 0; //
цвет окна белый wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
if(!RegisterClass(&wcl)) // регистрация окна return 0;
hwnd = CreateWindow(szwinname, "KARKAS", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
HWND_DESKTOP, NULL,hthisinst, NULL); ShowWindow(hwnd,nwinmode);
// показать окно UpdateWindow(hwnd); // перерисовать содержимое
while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg);//
транслировать клавиши // если убрать, то не будет реагировать на клавиши
DispatchMessage(&msg);// вернуть управление Windows } return
msg.wParam; } LRESULT CALLBACK WindowFunc(HWND hwnd, UINT
message, WPARAM wParam, LPARAM lParam) { switch(message) {
case WM_DESTROY: PostQuitMessage(0); break; default: return
DefWindowProc(hwnd,message,wParam,lParam); } return 0; }
Обсудим приведённый выше текст программы. Выполнение программы
начинается с WinMain() , имеющей четыре параметра: •
hthisinst – дескриптор текущего экземпляра приложения (в
системе Windows могут работать несколько экземпляров одной и той же программы);
• hprevinst – всегда равен NULL , в Windows 3.1 мог
принимать ненулевые значения, если ранее были запущены другие экземпляры данного
приложения; • lpszargs – указатель на строку,
содержащую аргументы программы при ее запуске; • nwinmode
– определяет способ визуализации окна при запуске программы. Затем
определяется и регистрируется класс окна, содержащий поля (классом называют
фрейм): UINT style ; // тип окна WNDPROC lpfnWndProc ; //
адрес оконной функции int cbClsExtra ; // дополнительные данные для класса
int cbWndExtra ; // дополнительные данные для окна HINSTANCE hInstance ;
// дескриптор экземпляра приложения HICON hIcon ; // дескриптор иконки
HCURSOR hCursor ; // дескриптор курсора мыши HBRUSH hbrBackground ; //
цвет заполнения окна LPCSTR lpszMenuName ; // имя главного меню LPCSTR
lpszClassName ; // имя класса окна В данном случае меню не
требуется, и имя главного меню будет равно NULL . Создание окна
осуществляется с помощью функции HWND CreateWindow ( LPCSTR
lpClassName , // имя класса окна LPCSTR lpWinName , // заголовок окна
DWORD dwStyle , // стиль окна int x , int y , // координаты верхнего
левого угла int Width, int Height, // размеры окна HWND hParent, //
дескриптор родительского окна HMENU hMenu, // дескриптор главного меню
HINSTANCE hThisInst, // дескриптор приложения LPVOID lpszAdditional); //
указатель на доп . информацию В нашем случае для параметров
x , y , Width ,
Height используется значение, заданное макросом
CW_USEDEFAULT , позволяющее системе самостоятельно выбирать
координаты и размеры окна. Если окно не имеет родительского окна, то
hParent должен быть равен HWND_DESKTOP или
NULL . Поскольку главного меню нет, то hMenu = NULL .
Аналогично указатель на дополнительную информацию равен NULL . Для стиля
(или типа) создаваемого окна используется макрос WS_OVERLAPPEDWINDOW
, определяющий стандартное окно, имеющее системное меню, заголовок,
рамку для изменения размеров, а также кнопки минимизации, развертки и закрытия.
Используется наиболее общий стиль. Можно создавать окна, имеющие другие стили.
Для этого применяют комбинации стилевых макросов. Наиболее широко используются
стили: WS _ OVERLAPPED - стандартное окно с рамкой, WS _
MAXIMIZEBOX - наличие кнопки развертки, WS _ MINIMIZEBOX - наличие кнопки
минимизации, WS _ SYSMENU - наличие системного меню, WS _ HSCROLL -
наличие горизонтальной панели прокрутки, WS _ VSCROLL - наличие вертикальной
панели прокрутки. Для отображения окна на экран используется
функция BOOL ShowWindow(HWND hwnd, int
nHow); параметры которой задают дескриптор окна ( hwnd
) и способ отображения nHow . Можно использовать
следующие способы отображения: SW _ HIDE - скрыть окно, SW _
MAXIMIZE - развернуть окно до полноэкранного, SW _ MINIMIZE - свернуть окно
до иконки, SW _ RESTORE - отобразить окно в нормальном представлении.
Вызов функции UpdateWindow() означает
немедленную перерисовку окна, и Windows в этом случае направляет программе
сообщение, указывающее на необходимость перерисовки. Последней частью
программы WinMain() является цикл обработки сообщений. Целью
этого цикла является получение и обработка сообщений, передаваемых операционной
системой. Эти сообщения ставятся в очередь сообщений приложения, откуда они
потом выбираются функцией BOOL GetMessage(LPMSG msg, HWND hwnd, UINT
min, UINT max); Выбираемые из очереди сообщения сохраняются по
адресу msg , где находится структура: struct MSG {
HWND hwnd ; // окно, для которого назначено сообщение UINT message ; //
само сообщение WPARAM wParam ; // параметры LPARAM lParam ; // сообщения
DWORD time ; // время посылки сообщения POINT pt ; // положение курсора
мыши } Структура, определяющая координаты курсора мыши, описывается
следующим образом struct POINT { LONG x,y; // координаты }
Параметр hwnd функции GetMessage()
является дескриптором окна, для которого эта функция получает
сообщения. Если требуется получать сообщения, адресованные всем окнам
приложения, то hwnd должен быть равен NULL . Остальные два
параметра определяют диапазон получаемых сообщений. Если они равны 0 , то
программа получает все сообщения. Подпрограмма GetMessage()
возвращает 0 , когда пользователь завершает работу. Функция
TranslateMessage() транслирует виртуальные коды клавиш,
генерируемые системой Windows , в клавиатурные сообщения. Когда сообщение
получено и преобразовано, функция DispatchMessage() возвращает
его обратно к Windows . Затем это сообщение будет передано системой Windows в
оконную функцию приложения. В конце работы программа возвращает значение
msg.wParam . В нашем примере оконная функция имеет имя
WindowsFunc() . Ее параметрами являются первые четыре поля
структуры MSG . В нашем примере оконная функция обрабатывает
единственное сообщение WM_DESTROY – завершить работу программы.
Она обрабатывает его с помощью функции PostQuitMessage() ,
приводящей к передаче сообщения WM_QUIT приложению. Получив это
сообщение, GetMessage() возвращает 0 и завершает тем самым цикл
обработки сообщений. Заметим, что при создании проекта приложения с помощью
выбора опций File ® New
® Project и включения имени, например
karkas.ide , требуется создать пустой файл karkas.def
, или удалить его из списка файлов, используемых при компиляции.
5.3. Обработка сообщений в системе Windows
Сообщения обрабатываются оконной функцией. Типы сообщений
представляются 32-разрядными словами. Для идентификации этих чисел применяются
макроимена. Наиболее часто используются следующие макроимена сообщений:
WM _ CHAR - нажата клавиша; WM _ PAINT - запрос на
перерисовку окна; WM _ LBUTTONDOWN - нажата левая кнопка мыши; WM _
RBUTTONDOWN - нажата правая кнопка мыши; WM _ COMMAND - выбрана команда
меню; WM _ TIMER - истечение заданного интервала времени.
Каждое сообщение имеет два 32-разрядных параметра типов WPARAM и LPARAM .
Обработку сообщений выполняет оконная функция, имеющая четыре параметра:
дескриптор окна, сообщение и два указанных выше параметра. Для доступа к
младшему и страшему словам 32-битного двойного слова применяются функции
LOWORD и HIWORD . Например, операции x =
LOWORD ( lParam ); y = HIWORD ( lParam ); записывают в ( x
, y ) координаты курсора мыши в случаях
обработки сообщения о нажатии левой или правой кнопки мыши.
Обработка нажатия клавиши . Для сообщения
WM_CHAR параметр wParam содержит ASCII – код
нажатой клавиши, LOWORD(lParam) – количество повторов,
генерируемых при удержании клавиши в нажатом состоянии, HIWORD(lParam)
содержит следующую информацию, определенную значениями битов: 15: 1
– клавиша отпущена, 0 – нажата; 14: устанавливается, если клавиша уже была
нажата перед посылкой сообщения; 13: дополнительно нажата клавиша ALT ;
12-9: используется системой; 8: нажата дополнительная или функциональная
часть клавиатуры; 7-0: scan – код клавиши. Для того чтобы добавить в
написанный выше каркас приложения обработку события WM_CHAR ,
определим массив символов для ввода и подключим библиотеки обработки строки и
вывода на экран: #include <string.h> #include <stdio.h>
char str [80] = “”; // буфер для строки ввода Добавив
обработку события WM_CHAR , получаем следующую оконную функцию:
LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) { HDC hdc; switch(message)
{ case WM_CHAR: hdc = GetDC ( hwnd ); // получить контекст
устройства TextOut ( hdc , 1, 1, “ “, 2); // стереть старый символ
sprintf ( str , “% c ”, ( char ) wParam ); // записать в буфер новый символ
TextOut ( hdc , 1, 1, str , strlen ( str )); // вывести на экран
ReleaseDC ( hwnd , hdc ); // освободить контекст устройства break;
case WM_DESTROY: PostQuitMessage(0); // сообщение о завершении
break; default: // обработка остальных сообщений return
DefWindowProc(hwnd, message, wParam, lParam); } return 0; }
Контекст устройства.
Функция GetDC() устанавливает связь программы с
экраном. Значение, возвращаемое этой функцией, называется контекстом
устройства . Ниже дадим более точное определение.
Контекст устройства – это структура данных,
связывающая приложение с драйвером устройства и полностью определяющая состояние
драйвера и способ вывода информации. Программа может вывести информацию на
экран, только получив контекст устройства. По окончании процесса вывода она
должна этот контекст освободить, вызвав ReleaseDC() . Эти
функции имеют следующие прототипы: HDC
GetDC(HWND hwnd); int ReleaseDC(HWND hwnd, HDC hdc);
Тип HDC означает дескриптор контекста
устройства. Функция ReleaseDC() возвращает ненулевое значение,
если она завершилась успешно. Текст на экран выводит функция, имеющая
следующий прототип: BOOL TextOut(HDC
hdc, int x, int y, LPSTR lpstr, int nlength); На экран выводится
nlength символов строки, указатель на которую равен
lpstr . Координаты задаются относительно начала рабочей
области, а не экрана. Обработка запроса на перерисовку окна
. Если нажать кнопку минимизации окна, а затем вновь распахнуть
окно, то символы, записанные в окне с помощью TextOut() , будут
потеряны. Система Windows , как правило, не запоминает содержимое окна, но
каждый раз, когда надо перерисовать окно, она посылает оконной функции сообщение
WM_PAINT . В некоторых случаях при перемещении и при
изменении размеров окна система сохраняет и перерисовывает содержимое окна, но
это не касается случая, когда окно минимизируется или перекрывается другим
окном, а потом делается попытка восстановить его в исходное состояние. Для того
чтобы обрабатывать сообщение WM_PAINT , добавим соответствующий
этому сообщению случай в оператор switch функции окна:
case WM_PAINT: hdc = BeginPaint(hwnd, &paintstruct); //
получить DC TextOut(hdc, 1, 1, str, strlen(str)); EndPaint(hwnd,
&paintstruct); // освободить DC break; В данном случае
применяются другие функции получения и освобождения контекста устройства. Они
имеют дополнительный параметр, равный адресу структуры. struct
PAINTSTRUCT { HDC hdc ; // дескриптор DC BOOL fErase ; // true ,
если перерисовывается заполнение окна RECT rcPaint ; // координаты области
перерисовки BOOL fRestore , fIncUpdate ; // резерв BYTE rgbReserved
[32]; // резерв } Здесь тип RECT описывает
структуру struct RECT { LONG left, top, right, bottom; }
Для обработки сообщения WM_PAINT добавим
определение PAINTSTRUCT
paintstruct ; В результате получаем приведённую ниже оконную
функцию: LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) { HDC hdc; // контекст устройства
PAINTSTRUCT paintstruct; switch(message) { case WM_CHAR: hdc
= GetDC ( hwnd ); // получить контекст устройства TextOut ( hdc , 1, 1, “ “,
2); // стереть старый символ sprintf ( str , “% c ”, ( char ) wParam ); //
записать в буфер новый символ TextOut ( hdc , 1, 1, str , strlen ( str ));
// вывести на экран ReleaseDC ( hwnd , hdc ); // освободить контекст
устройства break; case WM_PAINT: hdc = BeginPaint(hwnd,
&paintstruct); // получить DC TextOut(hdc, 1, 1, str, strlen(str));
EndPaint(hwnd, &paintstruct); // освободить DC break; case
WM_DESTROY: PostQuitMessage(0); // сообщение о завершении break;
default: // обработка остальных сообщений return DefWindowProc(hwnd,
message, wParam, lParam); } return 0; } В реальных
программах обработка сообщения WM_PAINT может быть намного
сложнее. Она выполняется одним из трех способов: • программа выводит
при перерисовке информацию, полученную в результате некоторых вычислений;
• последовательность событий запоминается и при необходимости
воспроизводится вновь для перерисовки окна; • программа поддерживает
виртуальный экран, который при перерисовке копируется в окно.
Обработка сообщений мыши . Рассмотрим наиболее
часто употребляемые сообщения мыши WM_LBUTTONDOWN и
WM_RBUTTONDOWN , которые поступают при нажатии соответственно
левой и правой кнопок мыши. Для обработки этих сообщений в оконной функции
добавим следующие строки: case WM _ RBUTTONDOWN : // нажата
правая кнопка мыши hdc = GetDC ( hwnd ); // получить DC strcpy ( str ,
“Нажата правая кнопка”); TextOut(hdc, LOWORD(lParam), HIWORD(lParam), str,
strlen(str)); ReleaseDC(hwnd, hdc); // освободить DC break; case
WM_LBUTTONDOWN: // нажата левая кнопка мыши hdc = GetDC(hwnd); // получить
DC strcpy(str, “ Нажата левая кнопка ”); TextOut(hdc, LOWORD(lParam),
HIWORD(lParam), str, strlen(str)); ReleaseDC(hwnd, hdc); // освободить DC
break; При нажатии кнопки мыши координаты курсора x
и y передаются в lParam . Поскольку
текст в данном случае выводится, начиная с позиции (
LOWORD ( lParam
), HIWORD (
lParam )) , то при нажатии кнопки
мыши сообщение об этом будет выводиться в текущей позиции курсора. Параметр
wParam будет содержать информацию, определенную комбинацией
флагов: MK _ CONTROL - в момент нажатия кнопки мыши была нажата
клавиша [ Ctrl ]; MK _ SHIFT - то же для клавиши SHIFT ; MK _ LBUTTON -
была нажата левая кнопка мыши; MK _ RBUTTON - была нажата правая кнопка
мыши; MK _ MBUTTON - была нажата средняя кнопка мыши.
Генерация сообщения WM_PAINT . Программа имеет
возможность вызвать генерацию и посылку операционной системой сообщения о
запросе на перерисовку окна. Операционная система передаст это сообщение в
удобное для себя время. Это позволяет эффективно использовать время центрального
процессора. Чтобы заставить Windows послать сообщение WM_PAINT
, программа должна вызвать функцию
BOOL InvalidateRect(HWND hwnd, CONST RECT *lpRect, BOOL
fErase); где hwnd – дескриптор окна, которому
должно быть адресовано сообщение WM_PAINT , lpRect
– указатель на структуру RECT , задающую прямоугольную
область в окне, которая должна быть перерисована. Если lpRect =
NULL , то окно перерисовывается полностью. Если fErase не равен
нулю, то вначале выполняется перерисовка заполнения, т.е. содержимое
перерисовываемой области вначале стирается. Для демонстрации способа
генерации запроса о перерисовке модифицируем каркасную программу следующим
образом: · добавим внешние переменные char str [80] = “”; int X = 1,
Y = 1; · главная программа не изменяется; · перепишем оконную функцию
таким образом, что вывод на экран производится с помощью обработки сообщения
WM_PAINT . Выводится строка из буфера. При обработке других
сообщений этот буфер заполняется и вызывается генерация сообщения
WM_PAINT . Ниже приведём текст модифицированной оконной
функции. LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT
paintstruct; switch(message) { case WM _ CHAR : X = Y = 1; //
выводим в левом верхнем углу sprintf(str, “%c”, (char)wParam); // заполнить
буфер InvalidateRect(hwnd, NULL, 1); // генерация WM_PAINT break;
case WM_PAINT: hdc = BeginPaint(hwnd, &paintstruct); // получить DC
TextOut(hdc, X, Y, str, strlen(str)); // вывод EndPaint(hwnd,
&paintstruct); // освободить DC break; case WM_RBUTTONDOWN: //
нажата правая кнопка мыши hdc = GetDC(hwnd); // получить DC strcpy(str,
“ Нажата правая кнопка ”); TextOut(hdc, LOWORD(lParam), HIWORD(lParam), str,
strlen(str)); ReleaseDC(hwnd, hdc); // освободить DC break; case
WM_LBUTTONDOWN: // нажата левая кнопка мыши hdc = GetDC(hwnd); // получить
DC strcpy(str, “ Нажата левая кнопка ”); TextOut(hdc, LOWORD(lParam),
HIWORD(lParam), str, strlen(str)); ReleaseDC(hwnd, hdc); // освободить DC
break; case WM_DESTROY: PostQuitMessage(0); // сообщение о
завершении break; default: // обработка остальных сообщений return
DefWindowProc(hwnd, message, wParam, lParam); } return 0; }
Генерация сообщений таймера . Можно
устанавливать таймер, который с заданной периодичностью будет посылать программе
сообщение WM_TIMER . Для установки таймера применяется функция
UINT SetTimer ( HWND hwnd , UINT nID , UINT wLength ,
TIMEPROC lpTFunc ); где hwnd – дескриптор окна, nID
-идентификатор устанавливаемого таймера (можно использовать несколько
таймеров), wLength – временной интервал в миллисекундах,
lpTFunc – функция обработки сообщений таймера. Если
TFunc = NULL , то для обработки сообщений таймера будет
вызываться оконная функция. Для отключения таймера вызывается функция
BOOL KillTimer ( HWND hwnd ,
UINT nID ); где nID – идентификатор таймера, а hwnd
– дескриптор окна, использующего таймер. Например, если в
программе, демонстрирующей генерацию WM_PAINT , поставить после
вызова функции ShowWindow(hwnd, nWinMode); вызов подпрограммы
установки таймера: SetTimer ( hwnd , 1, 1000, NULL ); а
после цикла обработки сообщений:
KillTimer ( hwnd , 1); то вывод окна, в
котором показывается день недели, месяц, число, время и год, осуществляется с
помощью оконной функции: LRESULT CALLBACK WindowFunc(HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc;
PAINTSTRUCT paintstruct; struct tm *newtime; time_t t;
switch(message) { case WM_PAINT: hdc = BeginPaint(hwnd,
&paintstruct); // получить DC TextOut(hdc, X, Y, str, strlen(str)); //
вывод буфера EndPaint(hwnd, &paintstruct); // освободить DC break;
case WM_TIMER: t = time(NULL); newtime = localtime (& t );
//получить текущее время strcpy(str, asctime(newtime)); // запись в буфер
str[strlen(str)-1] = ‘\0'; InvalidateRect(hwnd, NULL, 1); break;
case WM_DESTROY: PostQuitMessage(0); // сообщение о завершении
break; default: // обработка остальных сообщений return
DefWindowProc(hwnd, message, wParam, lParam); } return 0; }
Окна сообщений . Возможен вывод
информации с ожиданием реакции пользователя. Это средство является самым простым
элементом интерфейса и называется окном сообщения .
Для создания этого элемента используется функция: int
MessageBox ( HWND hwnd , LPCSTR lpText , LPCSTR lpCaption , UINT
wMBType ); где hwnd – дескриптор родительского окна,
lpText – выводящаяся на экран строка внутри окна сообщения,
lpCaption – заголовок окна сообщения. Параметр wMBType
является комбинацией значений, определяющих свойства окна сообщения,
включающих типы кнопок и дополнительную иконку рядом с текстом сообщения.
Возвращаемые значения зависят от реакции пользователя: если нажата кнопка OK ,
то возвращается значение IDOK , Abort – IDABORT
, Retry – IDRETRY , Ignore – IDIGNORE
, Cancel – IDCANCEL , No – IDNO , Yes
– IDYES . Ниже приведены значения, используемые для
параметра wMBType :
Значение
|
Отображаются на экран
|
MB_ABORTRETRYIGNORE
|
кнопки Abort, Retry и Ignore
|
MB _ ICONEXCLAMATION
|
иконка “восклицательный знак”
|
MB _ ICONHAND
|
иконка “знак Стоп”
|
MB _ ICONINFORMATION
|
иконка “информация”
|
MB _ ICONQUESTION
|
иконка “вопросительный знак”
|
MB _ ICONSTOP
|
иконка “знак Стоп”
|
MB_OK
|
кнопка Ok
|
MB_OKCANCEL
|
кнопки Ok и Cancel
|
MB_RETRYCANCEL
|
кнопки Retry и Cancel
|
MB_YESNO
|
кнопки Yes и No
|
MB_YESNOCANCEL
|
кнопки Yes, No и Cancel
| Если в рассмотренной нами каркасной
программе, в оконной функции добавить переменную int
response ; а для обработки сообщения о нажатии правой кнопки
добавить операторы case WM _ RBUTTONDOWN : //по нажатию правой
кнопки мыши response = MessageBox ( hwnd , “Нажмите кнопку”, “Окно
сообщения”, MB _ YESNO ); switch ( response ) //посмотрим, какую кнопку
нажал пользователь { case IDYES : //если YES MessageBox ( hwnd , “”,
“ Yes ”, MB _ OK ); // скажем ему об этом break; case IDNO: // если NO
MessageBox(hwnd, “”, “No”, MB_OK); // скажем ему об этом break ; }
break ; то при нажатии правой кнопки мыши в окне появится окно
сообщения с заголовком “Окно сообщения”, надписью “Нажмите кнопку” и двумя
кнопками – “ Yes ” и “ No ”. 5.4. Меню в системе
Windows В системе Windows меню являются наиболее часто
применяемыми элементами интерфейса. Включение меню в программу требует следующих
шагов: · определения формы меню в файле ресурсов; · загрузки меню при
создании главного окна; · обработки событий меню.
Файл ресурсов . Ресурсы
– это объекты, которые используются в программе, но не
определяются в ней. Например, меню, иконки, диалоги, графические растровые
изображения. Ресурсы определяются отдельно от текстов программы и связываются с
приложением при сборке. Они содержатся в текстовом файле с расширением
RC и обычно имеют имя nprog.rc , где
nprog – имя программы. Файл ресурсов обычно компилируется в
файл nprog.res с помощью компилятора rc.exe .
(Компиляция происходит автоматически, вместе с компиляцией программы). Меню
создается в файле ресурсов с помощью определений, имеющих следующий формат:
MenuName MENU [параметры] { Элементы меню } Здесь
MenuName – имя меню. Можно использовать следующие параметры:
DISCARDABLE - неиспользуемое меню может быть удалено из памяти;
FIXED - меню фиксировано в памяти; LOADONCALL - меню загружается только
перед непосредственным использованием; MOVEABLE - меню может перемещаться;
PRELOAD - меню загружается в момент старта программы. Обычно параметр не
указывается, и используются значения параметров по умолчанию. Имеется два
типа элементов меню: MENUITEM и POPUP ;
MENUITEM определяет конечный элемент меню, а POPUP
– элемент, который может содержать подменю. Они определяются в файле
ресурсов с помощью операторов: MENUITEM “Имя”, MenuID [,
параметры] POPUP “Имя”[, параметры] Имя ставится в
кавычках и является текстом соответствующей опции, как, например, File в
текстовых редакторах. Для конечного элемента меню число MenuID
– это идентификатор, по которому в оконной функции обрабатывается
данный элемент меню. Параметры могут задаваться по умолчанию, они имеют
следующие значения: CHECKED - рядом с меню отображается “птичка”;
не применимо к элементам верхнего уровня; GRAYED - элемент меню недоступен и
отображается серым цветом; HELP - связан с элементом “Помощь”, только для
MENUITEM . MENUBARBREAK - для горизонтальных меню верхнего уровня приводит к
размещению элемента в новой строке, а для вертикальных – к размещению элемента в
новой колонке (с разделением вертикальной линией); MENUBREAK - то же самое,
но без разделения вертикальной линией.
Пример. Рассмотрим следующий файл
ресурсов: MYMENU MENU { POPUP “&Раз” {
MENUITEM “&Альфа”, 100 MENUITEM “&Бета”, 101 } POPUP
“&Два” { MENUITEM “&Гамма”, 102 POPUP “&Дельта” {
MENUITEM “&Эпсилон”, 104 MENUITEM “&Зета”, 105 }
MENUITEM “&Эта”, 106 MENUITEM “&Тета”, 107 } MENUITEM
“&Помощь”, 108 } Поскольку идентификаторы 100, 101, …,
108 будут использоваться в оконной функции, то целесообразно присвоить им
символические имена в файле menu.h , с помощью директив
#define IDM_ALPHA 100 #define IDM_BETA 101 … и в
оконной функции обрабатывать опции меню по этим символическим именам. В этом
случае в файле menu.h и в программе необходимо поставить
директиву включения # include “ menu . h ”
Обработка команд меню . Для загрузки меню полю
класса окна lpszMenuName следует присвоить указатель на строку,
содержащую имя меню из файла ресурсов. В нашем примере это имя равно
MYMENU , значит, необходима операция:
wcl . lpszMenuName = “ MYMENU ”; Выбор
пользователем команд меню приводит к передаче сообщения WM_COMMAND
. Если выбран элемент, определенный как MENUITEM , то
его числовой идентификатор будет содержаться в LOWORD(wParam) .
Отсюда следует, что в нашем примере в оконной функции нужно добавить обработку
сообщения WM_COMMAND . В случае вложенных меню используется
вложенный оператор switch . Например: switch (
message ) { case WM _ COMMAND : //проанализируем выбор пользователя
switch(LOWORD(wParam)) { case 100: MessageBox(hwnd, “ Альфа ”, “”,
MB_OK); break; case 101: MessageBox(hwnd, “ Бета ”, “”, MB_OK);
break; case 102: MessageBox(hwnd, “ Гамма ”, “”, MB_OK); break;
case 104: MessageBox(hwnd, “ Эпсилон ”, “”, MB_OK); break; case 105:
MessageBox(hwnd, “ Зета ”, “”, MB_OK); break; case 106: MessageBox(hwnd,
“ Эта ”, “”, MB_OK); break; case 107: MessageBox(hwnd, “ Тета ”, “”,
MB_OK); break ; case 108: MessageBox ( hwnd ,“Программа демонстрации
меню”, “Помощь”, MB _ OK ); break ; } break ; // конец обработки
сообщения WM _ COMMAND } Для компиляции и сборки программы,
демонстрирующей меню, следует выполнить действия: • дополнить
karkas.c оператором wcl.lpszMenuName = “MYMENU”;
• и обработкой опции WM_COMMAND оконной
функцией, назвать программу nprog.c ; • набрать текст
файла ресурсов nprog.rc ; • создать проект, выбрав
опции компилятора File ® New ® Project • и
вывести для файла проекта имя nprog.ide , а затем удалить из
списка участвующих в проекте файлов имя nprog.def (нажав правую
кнопку мыши); • запустить компиляцию, сборку и выполнение с помощью
комбинации клавиш [ Ctrl ]+ F 9. Горячие клавиши
. Это клавиатурные комбинации, нажатие которых приводит к выбору
соответствующей команды меню. Эти клавиши называются также
акселераторами . При помощи акселератора можно
выбирать соответствующий элемент меню, не обращаясь к самому меню. Для
определения акселераторов составляется таблица и записывается в файл ресурсов.
Таблица составляется следующим образом: Имя_Таблицы ACCELERATORS
{ Клавиша1, MenuID 1[, тип][, параметр] Клавиша2, MenuID 2[, тип][,
параметр] … } где Клавиша1 ,
Клавиша2 , … определяют клавиши или комбинации клавиш
акселераторов, а значения MenuID – идентификаторы элементов
меню. Аргумент типа указывает, является ли клавиша стандартной или виртуальной.
Виртуальная клавиша – это системно-независимый
код, определенный для основного набора служебных клавиш. Виртуальные клавиши
включают функциональные клавиши F1 – F12
, стрелки и другие клавиши, не имеющие коды ASCII . Макроимена
этих клавиш начинаются с букв VK , например VK_F1
, и определены в файле windows.h .
Параметр может быть одним из макросов
NOINVERT , ALT , SHIFT ,
CONTROL . Наличие NOINVERT означает, что
соответствующий элемент меню при использовании акселератора не будет подсвечен,
ALT указывает, что должна быть дополнительно нажата клавиша ALT
, аналогичный смысл имеют параметры SHIFT и CONTROL
. Клавиши могут быть символами в кавычках
либо кодом ASCII символа, либо виртуальными. В последнем случае тип -
VIRTKEY . Например, “ a ”, 100; нажатие a ; “^ A ”,
100; нажатие Ctrl + A ; “a”, 100, ALT; нажатие Alt+A; VK_F2, 100, SHIFT;
нажатие Shift+F2. Если добавить в проект предшествующего примера таблицу
акселераторов, то файл nprog.rc будет выглядеть следующим
образом: #include <windows.h> MYMENU MENU {
POPUP “& Раз ” { MENUITEM “& Альфа \tF2”, 100 MENUITEM
“& Бета \tF3”, 101 } POPUP “& Два ” { MENUITEM “&
Гамма \tShift+ Г ”, 102 POPUP “& Дельта ” { MENUITEM “&
Эпсилон \tCtrl+E”, 104 MENUITEM “& Зета \tCtrl+Z”, 105 }
MENUITEM “& Эта \tCtrl+F4”, 106 MENUITEM “& Тета \tF5”, 107
} MENUITEM “& Помощь ”, 108 } MYMENU ACCELERATORS
{ VK_F2, 100, VIRTKEY VK_F3, 101, VIRTKEY “ Г ”, 102 “^E”,
104 “^Z”, 105 VK_F4, 106, VIRTKEY, CONTROL VK_F5, 107, VIRTKEY
VK _ F 1, 108, VIRTKEY } В главной программе акселераторы могут быть
загружены, например, с помощью объявления HACCEL hAccel;
и вызова фунции hAccel = LoadAccelerators(hThisInst,
“MYMENU”); а затем они должны обрабатываться с помощью фунции
TranslateAccelerator() в цикле обработки сообщений, который с
этой функцией будет выглядеть так: while(GetMessage(&msg,
NULL, 0, 0)) { if(!TranslateAccelerator(hwnd, hAccel. &msg) {
TranslateMessage(&msg); DispatchMesage(&msg); } }
5.5. Диалоги в системе Windows
Диалог является наиболее важным после меню элементом интерфейса. Диалог –
это специальный тип окна, поддерживающий гибкие средства для взаимодействия
пользователя с программой. Элементы управления .
Диалог взаимодействует с пользователем через элементы управления, которые тоже
являются особыми типами окна, для которых окно диалога служит родительским.
Типы элементов управления: • кнопка ( button ) –
изображение кнопки на экране, которую пользователь активизирует щелчком мыши или
нажатием клавиши Enter ; • контрольный переключатель ( check
box ) содержит один или более элементов, часть из которых может быть
отключена; отмеченные элементы считаются выбранными; • селекторная
кнопка ( radio button ) аналогична контрольному переключателю,
но может быть выбран лишь один из элементов; • список (
listbox ) – список строк, из которых пользователь может выбрать
одну (или более); • окно ввода ( edit box ) – ввод
строки символов с предварительным редактированием; • комбинированный
список ( combo box ) – комбинация списка и окна ввода;
• линейка прокрутки ( scrollbar ) используется для
прокрутки документов в окне; • статический элемент ( static
) предназначен для вывода информации, которая не может быть изменена
пользователем. Модальные и немодальные диалоги .
Диалог называется модальным , если программа
дожидается завершения этого диалога, и только затем ее выполнение может быть
продолжено. Модальный диалог не позволяет также переключить ввод на другие окна,
порожденные приложением. Диалог называется немодальным
, если продолжение программы не требует его завершения. При
немодальном диалоге разрешается переключаться на другие окна приложения.
Обработка сообщений в диалоге . Сообщения диалога
обрабатываются функцией диалога или
оконной функцией диалога , имеющей прототип
BOOL CALLBACK Dfunc(HWND hwnd, UINT
message, WPARAM wParam, LPARAM lParam); отличающейся от оконной
функции тем, что она возвращает значения истина или
ложь . Каждому элементу управления в диалоге
присваивается собственный идентификатор. При воздействии пользователя на элемент
управления в диалоговую функцию посылается сообщение, содержащее идентификатор
этого элемента и тип действия пользователя. Диалоговая функция анализирует эту
информацию и выполняет соответствующие действия. Активизация
диалога . Модальный диалог делается активным с помощью функции
int DialogBox(HINSTANCE hThisInst, LPCSTR
lpName, HWND hwnd, DLGPROC lpDFunc);
где hThisInst – дескриптор текущего приложения ,
lpName – указатель на имя диалога , определенное в файле
ресурсов , hwnd – дескриптор окна , порождающего диалог ,
lpDFunc – указатель на диалоговую функцию .
Определение ресурсов диалога . Для наглядности
содержимое диалога мы будем описывать с помощью текстового редактора, хотя на
практике такой подход используется редко, и большинство программистов пользуются
редактором диалогов. Ресурсы диалога задаются с помощью оператора:
Имя_Диалога DIALOG [ DISCARDABLE ] X , Y , Width , Height Параметры
{ Элементы диалога } Координаты левого верхнего угла
равны ( X , Y ), а значения Width
и Height задают ширину и высоту диалогового окна. Если
диалог может быть удален из памяти, то его необходимо определить как
DISCARDABLE . Для диалога задаются один или несколько
параметров. Параметр CAPTION определяет заголовок диалога,
параметр STYLE – стиль. Элементы диалога
определяют элементы управления, содержащиеся в диалоге. Стиль
окна диалога является логической комбинацией стилей: DS _
MODALFRAME - модальный диалог; WS _ BORDER - окно с рамкой; WS _ CAPTION
- окно с заголовком; WS _ CHILD - дочернее окно; WS _ MAXIMIZEBOX - окно
с кнопкой полноэкранного представления; WS _ MINIMIZEBOX - окно с кнопкой
минимизации; WS _ SYSMENU - окно с системным меню; WS _ TABSTOP -
возможность циклического выбора элемента нажатием клавиши табуляции; WS _
VISIBLE - отображение окна при активизации; WS _ POPUP - всплывающее окно.
Пример . Создадим диалог, содержащий
три кнопки: Red , Green и Reset . При нажатии кнопок Red и Green на экране
появляется сообщение, подтверждающее нажатие кнопки. При нажатии Reset диалог
будет закрыт. Диалог содержит также список и окно редактирования (окно ввода).
Кнопки определяются с помощью оператора: PUSHBUTTON “Строка”,
ID , X , Y , Width , Height [, Стиль] Активная по умолчанию кнопка
определяется с помощью оператора DEFPUSHBUTTON , имеющего те же
самые параметры. Список задается оператором: LISTBOX ID
, X , Y , Width , Height [, Стиль] Числовой идентификатор ID
предназначен для обработки элемента в функции диалога, координаты
X , Y и размеры Width ,
Height задают расположение окна, стиль является комбинацией
стилей, приведенных выше. Окно ввода определяется с помощью:
EDITTEXT ID, X, Y, Width, Height[, Стиль ] Приведённая
ниже программа состоит из файлов mydialog.h ,
mydialog.rc , mydialog.cpp , содержащих
соответственно определения числовых идентификаторов, ресурсов и текста
программы. В тексте программы идентификатор списка не обрабатывается. Если
ввести данные в окно редактирования, то они будут отображаться в главном окне.
Для завершения модального диалога применяется функция EndDialog()
, обрабатывающая сообщение IDCANCEL . Файл
mydialog.h : #define IDM_DIALOG1 100 #define
IDM_DIALOG2 101 #define IDM_HELP 102 #define IDD_RED 103 #define
IDD_GREEN 104 #define ID_EB1 400 #define ID_LB1 301 Файл
mydialog.rc #include "mydialog.h" #include
<windows.h> MYMENU MENU { MENUITEM "Dialog &1",IDM_DIALOG1
MENUITEM "Dialog &2",IDM_DIALOG2 MENUITEM "Help",IDM_HELP }
MYMENU ACCELERATORS { VK_F2,IDM_DIALOG1,VIRTKEY
VK_F3,IDM_DIALOG2,VIRTKEY VK_F1,IDM_HELP,VIRTKEY } MYDB DIALOG
18,18,142,92 CAPTION "First Dialog" // заголовок окна STYLE
DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE {
DEFPUSHBUTTON " Конец ввода ", IDOK, 68,22, 60,14, // кнопка по умолчанию
WS_CHILD | WS_VISIBLE | WS_TABSTOP PUSHBUTTON "Red",IDD_RED,32,40,36,14,
//кнопка Red WS_CHILD|WS_VISIBLE|WS_TABSTOP PUSHBUTTON
"Green",IDD_GREEN,74,40,36,14, //кнопка Green WS_CHILD|WS_VISIBLE|WS_TABSTOP
PUSHBUTTON "Reset",IDCANCEL,52,65,37,14, //кнопка Reset WS_CHILD |
WS_VISIBLE | WS_TABSTOP EDITTEXT ID_EB1, 68, 8, 72, 12, ES_LEFT | //edifbox
ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
LISTBOX ID_LB1, 2,10,47,28, LBS_NOTIFY | //list box WS_CHILD |
WS_VISIBLE | WS_BORDER | WS_VSCROLL | WS_TABSTOP } Файл
mydialog.cpp Строка вводится в диалоговом окне и
отображается в главном окне. #include <windows.h> #include
<string.h> #include <stdio.h> #include "mydialog.h"
//объявим прототипы функций и глобальные переменные LRESULT CALLBACK
WindowFunc(HWND,UINT,WPARAM,LPARAM); BOOL CALLBACK DialogFunc
(HWND,UINT,WPARAM,LPARAM); char szWinName[] = " Мое Окно "; HINSTANCE
hInst; char str[80]; int WINAPI WinMain(HINSTANCE hThisInst,
HINSTANCE hPrevinst, LPSTR lpszArgs, int nWinMode) { HWND
hwnd; MSG msg; WNDCLASS wcl; HACCEL hAccel;
wcl.hInstance = hThisInst; wcl.lpszClassName = szWinName;
wcl.lpfnWndProc= WindowFunc; wcl.style = 0; wcl.hIcon =
LoadIcon (NULL, IDI_APPLICATION); //иконка приложения wcl.hCursor =
LoadCursor(NULL,IDC_ARROW); //тип курсора wcl.lpszMenuName = "MYMENU";//меню
wcl.cbClsExtra = 0; wcl.cbWndExtra = 0;
wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); //фон
if(!RegisterClass(&wcl)) return 0; hwnd =
CreateWindow(szWinName, "Dialogs", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
HWND_DESKTOP, NULL, hThisInst, NULL); hInst =
hThisInst; hAccel = LoadAccelerators(hThisInst,"MYMENU");//загрузим
акселераторы ShowWindow(hwnd,nWinMode); //отобразим окно на
экране UpdateWindow(hwnd); // обрабатываем акселлераторы
while(GetMessage(&msg,NULL,0,0)) {
if(!TranslateAccelerator(hwnd,hAccel,&msg)) {
TranslateMessage(&msg); DispatchMessage(&msg); } }
return msg.wParam; } LRESULT CALLBACK WindowFunc(HWND
hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc;
switch(message) { case WM_COMMAND: // анализируем выбор switch
(LOWORD (wParam)) { case IDM_DIALOG1: // выбран пункт Dialog 1
DialogBox(hInst,"MYDB",hwnd, (DLGPROC)DialogFunc); // вызовем окно
диалога break; case IDM_DIALOG2: // выбран пункт Dialog2 hdc =
GetDC(hwnd);// получить DC TextOut(hdc,1,1,str,strlen(str)); // вывод
ReleaseDC(hwnd,hdc);// освободить DC break; case IDM_HELP: // выбран
пункт Help MessageBox(hwnd,"Help","Help",MB_OK); // выведем окно сообщений
break; } break; case WM_DESTROY: PostQuitMessage (0);
//сообщение о завершении break ; default : //обработка других сообщений
return DefWindowProc ( hwnd , message , wParam , lParam ); } return
0; } BOOL CALLBACK DialogFunc(HWND hdwnd, UINT message, WPARAM
wParam, LPARAM lParam) { long i; //char str[80]; HDC hdc;
switch(message) { case WM_COMMAND: switch (LOWORD(wParam)) {
case IDCANCEL: EndDialog ( hdwnd ,0); //выходим из диалога return 1;
case IDD_RED: //нажата кнопка Red MessageBox(hdwnd,"Choice
Red","Red",MB_OK); return 1; case IDD_GREEN: // нажата кнопка Green
MessageBox(hdwnd,"Choice Green","Green",MB_OK); return 1; case IDOK:
GetDlgItemText(hdwnd, ID_EB1, str,80); MessageBox(hdwnd,str,"In Entry ",
MB_OK); } return 1; } return 0; }
Ниже представлены результаты работы программы.
 5.6. Пример программы, использующей список и окно ввода
Рассмотрим циклический список, элементы которого
находятся в окне LISTBOX . Запись элементов в список
производится из окна EDITTEXT . Вывод оконной функцией
элементов циклического списка на экран осуществляется с помощью обработки
сообщения WM_PAINT . Остальные опции в оконной функции
заканчиваются генерацией сообщения о перерисовке окна. Определения числовых
идентификаторов содержатся в mydialog.h : #define
IDM_DIALOG1 100 #define IDM_DIALOG2 101 #define IDM_HELP 102 #define
IDD_RED 103 #define IDD_GREEN 104 #define ID_EB1 400 #define ID_LB1
301 #define IDM_INSERT 200 #define IDM_DELETE 201 #define IDM_SHOW
202 Файл ресурсов mydialog.rc : #include
<windows.h> #include "mydialog.h" MYMENU MENU //меню {
POPUP "List" { MENUITEM "I&nsert",IDM_INSERT MENUITEM
"&Delete",IDM_DELETE MENUITEM "&Show",IDM_SHOW } MENUITEM
"Help",IDM_HELP } MYMENU ACCELERATORS // акселераторы {
VK_F2,IDM_INSERT,VIRTKEY VK_F1,IDM_HELP,VIRTKEY } MYDB DIALOG
18,18,142,92 CAPTION "Insert & Show" //заголовок окна STYLE
DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU { PUSHBUTTON " Конец
ввода ",IDCANCEL,52,65,60,14, //кнопка Конец ввода WS_CHILD | WS_VISIBLE |
WS_TABSTOP EDITTEXT ID_EB1, 68, 8, 72, 12, ES_LEFT | //editbox
ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP
LISTBOX 301, 2,10,47,80, LBS_NOTIFY | //list box WS_CHILD | WS_VISIBLE |
WS_BORDER | WS_VSCROLL | WS_TABSTOP } Текст программы
mydialog.cpp #include <windows.h> #include
<string.h> #include <stdio.h> #include "mydialog.h"
//объявим прототипы функций и глобальные переменные LRESULT CALLBACK
WindowFunc(HWND,UINT,WPARAM,LPARAM); BOOL CALLBACK DialogFunc
(HWND,UINT,WPARAM,LPARAM); char szWinName[] = "Lists";
HINSTANCE hInst; int X=0, Y=0; int maxX,maxY; HDC memdc;
HBITMAP hbit; HBRUSH hbrush; char str[80]; int i=1;
struct slink {char *e; slink *next;}; slink * abc
1= NULL ; //операции со списком, вставка нового элемента, удаление элемента
slink *insert( slink *lst, char *a) { slink *prev=lst, *p= new
slink; p->e = new char[strlen(a)+1]; strcpy(p->e,a);
p->e[strlen(a)+1]='\0'; p->next = NULL; if(lst==NULL) return p;
while(lst->next!=NULL) { lst =lst->next; }
lst->next = p; return prev; } // удаление элемента slink
* del ( slink *lst) { slink *p=lst; if(lst) { lst =
lst->next; delete p; } return lst; } int WINAPI
WinMain(HINSTANCE hThisInst, HINSTANCE hPrevinst, LPSTR lpszArgs,
int nWinMode) { HWND hwnd; MSG msg; WNDCLASS wcl; HACCEL
hAccel; wcl.hInstance = hThisInst; wcl.lpszClassName =
szWinName; wcl.lpfnWndProc= WindowFunc; wcl.style = 0;
wcl.hIcon = LoadIcon (NULL, IDI_APPLICATION); //иконка wcl.hCursor =
LoadCursor(NULL,IDC_ARROW); //курсор wcl.lpszMenuName = "MYMENU"; //меню
wcl.cbClsExtra = 0; wcl.cbWndExtra = 0; //фон
wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
if(!RegisterClass(&wcl)) return 0; //создадим окно hwnd =
CreateWindow(szWinName, "Dialogs", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
HWND_DESKTOP, NULL, hThisInst, NULL); hInst =
hThisInst; hAccel = LoadAccelerators(hThisInst,"MYMENU");//подключим
акселераторы ShowWindow(hwnd,nWinMode); //отобразить окно
UpdateWindow(hwnd); // обработка акселераторов
while(GetMessage(&msg,NULL,0,0)) {
if(!TranslateAccelerator(hwnd,hAccel,&msg)) {
TranslateMessage(&msg); DispatchMessage(&msg); } }
return msg.wParam; } LRESULT CALLBACK WindowFunc(HWND
hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc;
PAINTSTRUCT paintstruct; static TEXTMETRIC tm; SIZE size; slink*
abc3 = abc1; switch(message) { case WM_CREATE: // получаем
размеры окна maxX= GetSystemMetrics(SM_CXSCREEN); maxY=
GetSystemMetrics(SM_CYSCREEN); // строим растр , совместимый с окном hdc
= GetDC(hwnd); // получить DC memdc= CreateCompatibleDC(hdc); hbit=
CreateCompatibleBitmap(hdc,maxX,maxY); SelectObject(memdc,hbit);
hbrush=(HBRUSH)GetStockObject(WHITE_BRUSH); SelectObject(memdc,hbrush);
PatBlt(memdc,0,0,maxX,maxY,PATCOPY); ReleaseDC(hwnd,hdc); //освободить
DC break; case WM_COMMAND: switch (LOWORD (wParam)) { case
IDM_INSERT: DialogBox(hInst,"MYDB",hwnd, (DLGPROC)DialogFunc); break;
case IDM_DELETE: GetTextMetrics(memdc,&tm); X=Y=0;
PatBlt(memdc,0,0,maxX,maxY,PATCOPY);// очистка экрана abc1= del (abc1);
abc3=abc1; while(abc3!=NULL) { sprintf(str, "%s", abc3->e);
TextOut(memdc, X, Y, str, strlen(str)); Y = Y+
tm.tmHeight+tm.tmExternalLeading; abc3=abc3->next; }
InvalidateRect(hwnd,NULL,1); break; case IDM_SHOW:
GetTextMetrics(memdc,&tm); X=Y=0;
PatBlt(memdc,0,0,maxX,maxY,PATCOPY); abc3=abc1; while(abc3!=NULL)
{ sprintf(str, "%s", abc3->e); TextOut(memdc, X, Y, str,
strlen(str)); Y = Y+ tm.tmHeight+tm.tmExternalLeading;
abc3=abc3->next; } InvalidateRect(hwnd,NULL,1); break;
case IDM_HELP: MessageBox(hwnd,"Help","Help",MB_OK); break; }
break; case WM_PAINT: hdc=BeginPaint(hwnd,&paintstruct); //get
dc BitBlt(hdc,0,0,maxX,maxY,memdc,0,0,SRCCOPY);
EndPaint(hwnd,&paintstruct); //release DC break; case
WM_DESTROY: DeleteDC(memdc); PostQuitMessage (0); //сообщение о
завершении break ; default : //обработка остальных сообщений return
DefWindowProc ( hwnd , message , wParam , lParam ); } return 0; }
BOOL CALLBACK DialogFunc(HWND hdwnd, UINT message, WPARAM wParam,
LPARAM lParam) { long i; HDC hdc; slink* abc2;
switch(message) { case WM_COMMAND: switch (LOWORD(wParam)) {
case IDCANCEL: EndDialog(hdwnd,0); return 1; case ID_LB1:
if(HIWORD(wParam)==LBN_DBLCLK) { i =
SendDlgItemMessage(hdwnd,ID_LB1,LB_GETCURSEL,0,0L); sprintf(str,"Index of
choused %d",i); MessageBox(hdwnd,str,"Choice made",MB_OK); } return
1; case IDOK: GetDlgItemText(hdwnd, ID_EB1, str,80); if
(strlen(str)) { abc1 = insert(abc1,str); SendDlgItemMessage(hdwnd,
ID_LB1, LB_ADDSTRING,0,(LPARAM)str); } } return 1; case
WM_INITDIALOG: abc2 = abc1; while (abc2!=NULL) {
SendDlgItemMessage(hdwnd, ID_LB1, LB_ADDSTRING,0,(LPARAM)(abc2->e));
abc2 = abc2->next; } ; return 1; } return 0; }
Результат работы этой программы приведён на рис. 5.1.

Рис. 5.1. Схема диалога
|