5. Прикладной интерфейс для WINDOWS


Добавил:DMT
Дата создания:5 апреля 2008, 15:41
Дата обновления:5 апреля 2008, 15:54
Просмотров:11665 последний ---
Комментариев: 0

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. Схема диалога
 

up