Приложение Windows - это оконное приложение со стандартными функциями обработки, например, оконные приложения в среде WIN32.
Графический интерфейс пользователя делает возможным использование графики на растровом экране дисплея. Графика дает лучшее восприятие информации и возможность WYSIWYG (What you see is what you get, что вы видите, то и получите) как для графики, так и для форматированных для печати документов текста.
Рассмотрим простейшую оконную программу (при ее запуске на выполнение легко понять, что она делает):
#include <windows.h>
#include <windowsx.h>
#define MENUITEM_1 1
#define BUTTON_1 2
char MyStr[40] = "" ;
int Control[5] ;
HRGN hrgn ;
HWND hwnd1, hwnd2 ;
HMENU MyMenu, MyMenu1 ;
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static char szAppName[] = "MyWindowApp" ;
HWND hwnd ;
MSG msg ;
WNDCLASSEX wndclass ;
wndclass.cbSize = sizeof (wndclass) ; // размер структуры в байтах
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
// стиль окна - перерисовка при изменении размеров
wndclass.lpfnWndProc = WndProc ;
// указатель на функцию обратного вызова для обработки сообщений
wndclass.cbClsExtra = 0 ; // число дополнительных байт в конце этой структуры
wndclass.cbWndExtra = 0 ; // число дополнительных байт за экземпляром окна
wndclass.hInstance = hInstance ;
// дескриптор экземпляра, в котором находится оконная процедура
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
// дескриптор иконки для этого класса окна
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
// дескриптор курсора для этого класса окна
wndclass.hbrBackground = (HBRUSH) GetStockObject (COLOR_BACKGROUND) ;
// дескриптор кисти фона окна
wndclass.lpszMenuName = NULL ; // имя меню
wndclass.lpszClassName = szAppName ; // имя класса
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;
// дескриптор иконки для заголовка окон приложения
RegisterClassEx (&wndclass) ; // регистрируем класс окна
hwnd = CreateWindow // создаем окно на основе класса окна
(szAppName, // имя класса окна
"My Window App", // заголовок окна
WS_OVERLAPPEDWINDOW, // стиль окна
CW_USEDEFAULT, // начальное положение по x
CW_USEDEFAULT, // начальное положение по y
CW_USEDEFAULT, // начальный размер по x
CW_USEDEFAULT, // начальный размер по y
NULL, // описатель родительского окна
NULL, // описатель меню окна
hInstance, // описатель экземпляра программы
NULL) ; // параметры создания
ShowWindow (hwnd, iCmdShow) ; // выводим окно на экран
UpdateWindow (hwnd) ; // заставляем окно перерисовать свое содержимое
while (GetMessage (&msg, NULL, 0, 0)) // получаем сообщение из очереди сообщений
{
TranslateMessage (&msg) ; // преобразуем сообщения от клавиатуры
DispatchMessage (&msg) ; // отправляем сообщение оконной процедуре
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
PAINTSTRUCT ps ;
RECT rect ;
switch (iMsg)
{
case WM_CREATE :
MyMenu = CreateMenu () ; // создаем меню
if (!MyMenu)
{
MessageBox (hwnd, "Cannot Create Menu", "ERROR", MB_OK) ;
DestroyWindow (hwnd) ;
}
MyMenu1 = CreatePopupMenu () ;
if (!MyMenu1)
{
MessageBox (hwnd, "Cannot Create Menu", "ERROR", MB_OK) ;
DestroyWindow (hwnd) ;
}
// добавляем элементы меню
InsertMenu(MyMenu, 0xFFFFFFFF, MF_BYPOSITION | MFT_STRING | MF_POPUP,
(UINT)MyMenu1, "My Menu") ;
InsertMenu(MyMenu1, 0xFFFFFFFF, MF_BYPOSITION | MFT_STRING,
MENUITEM_1, "Press Me !") ;
SetMenu (hwnd, MyMenu) ; // подключаем меню к окну
DrawMenuBar (hwnd) ; // отображаем меню
lstrcpy (MyStr, "Select Menu Item !!!") ;
Control[0] = 1 ;
return 0 ;
break ;
case WM_PAINT : // рисуем окно
hdc = BeginPaint (hwnd, &ps) ;
GetClientRect (hwnd, &rect) ;
SetBkColor (hdc, GetSysColor (COLOR_BACKGROUND));
DrawText (hdc, MyStr, -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;// вывод строки на экран
if (Control[3])
{
Rectangle (hdc, 10, 10, 100, 100) ;
}
EndPaint (hwnd, &ps) ;
return 0 ;
break ;
case WM_CHAR : // обработка нажатия клавиши на клавиатуре
if (Control[1])
{
lstrcpy (MyStr, "Press F2 !!!") ;
GetClientRect (hwnd, &rect) ;
InvalidateRect (hwnd, &rect, 1) ; // указываем область для перерисовки
// генерируется WM_PAINT
Control[1] = 0 ;
Control[2] = 1 ;
return 0 ;
}
break ;
case WM_KEYDOWN : // обработка нажатия клавиши F2
switch (wParam)
{
case (VK_F2) :
if (Control[2])
{
lstrcpy (MyStr, "Click mouse in rectangle !!!") ;
hrgn = CreateRectRgn (10, 10, 100, 100) ;
GetClientRect (hwnd, &rect) ;
InvalidateRect (hwnd, &rect, 1) ; // указываем область для перерисовки
// генерируется WM_PAINT
Control[2] = 0 ;
Control[3] = 1 ;
}
break ;
}
return 0 ;
break ;
case WM_LBUTTONDOWN : // обработка нажатия мышки
if (Control[3])
{
if (PtInRegion (hrgn, GET_X_LPARAM (lParam), GET_Y_LPARAM (lParam)))
{
DeleteObject (hrgn) ;
hwnd1 = CreateWindow // создаем окно на основе класса окна
("BUTTON", // имя класса окна
"Press me!", // заголовок окна
BS_PUSHBUTTON|WS_CHILD, // стиль окна
10, // начальное положение по x
10, // начальное положение по y
100, // начальный размер по x
20, // начальный размер по y
hwnd, // описатель родительского окна
(HMENU)BUTTON_1, // описатель меню окна
NULL, // описатель экземпляра программы
NULL) ; // параметры создания
ShowWindow (hwnd1, SW_SHOW) ;
lstrcpy (MyStr, "Press Button !!!") ;
Control[3] = 0 ;
Control[4] = 1 ;
GetClientRect (hwnd, &rect) ;
InvalidateRect (hwnd, &rect, 1) ; // указываем область для перерисовки
// генерируется WM_PAINT
}
}
return 0 ;
break ;
case WM_COMMAND :
switch (wParam)
{
case MENUITEM_1 :// обработка нажатия меню
if (Control[0])
{
DestroyMenu (MyMenu) ;
DestroyMenu (MyMenu1) ;
SetMenu (hwnd, NULL) ;
lstrcpy (MyStr, "Press any symbol on keyboard !!!") ;
GetClientRect (hwnd, &rect) ;
InvalidateRect (hwnd, &rect, 1) ; // указываем область для перерисовки
// генерируется WM_PAINT
Control[0] = 0 ;
Control[1] = 1 ;
}
break ;
case BUTTON_1 :// обработка нажатия кнопки
if (Control[4])
{
DestroyWindow (hwnd1) ;
lstrcpy (MyStr, "Now you can close the program !!!") ;
GetClientRect (hwnd, &rect) ;
InvalidateRect (hwnd, &rect, 1) ; // указываем область для перерисовки
// генерируется WM_PAINT
Control[4] = 0 ;
}
break ;
}
return 0 ;
break ;
case WM_DESTROY :
if (MyMenu)
{
DestroyMenu (MyMenu) ;
}
PostQuitMessage (0) ; //запрос операционной системе на завершение приложения
return 0 ;
}
//обработка по умолчанию всех сообщений, которые не были
//обработаны в приложении
return DefWindowProc (hwnd, iMsg, wParam, lParam) ;
}
Функция WinMain использует последовательность вызовов WINAPI и, по своему завершению, возвращает операционной системе Windows целое. В ней есть четыре параметра. Параметр hInstance называется описателем экземпляра (instance handle). Это уникальное число, идентифицирующее программу, когда она работает под Windows. Может так случиться, что пользователь запустит под Windows несколько копий одной и той же программы. Каждая копия называется "экземпляром" и у каждой свое значение hInstance. Параметр hPrevInstance – не используется. Параметр szCmdLine – указатель на оканчивающуюся нулем строку, в которой содержатся любые параметры, переданные в программу из командной строки. Параметр iCmdShow – число, показывающее, каким должно быть выведено на экран окно в начальный момент.
Регистрация класса окна
Окно всегда создается на основе класса окна. Класс окна идентифицирует оконную процедуру, которая выполняет процесс обработки сообщений, поступающих окну. На основе одного класса окна можно создать несколько окон. Например, все окна-кнопки в Windows создаются на основе одного и того же класса окна. Класс окна определяет оконную процедуру и некоторые другие характеристики окон, создаваемых на основе этого класса.
Перед созданием окна необходимо зарегистрировать класс окна путем вызова функции RegisterClassEx. У функции RegisterClassEx имеется один параметр: указатель на структуру типа WNDCLASSEX. Структура WNDCLASSEX определяется в заголовочных файла Windows.
Следующая инструкция устанавливает оконную WndProc как оконную процедуру данного окна wndclass.lpfnWndProc = WndProc;
Создание окна
Вызов функции CreateWindow требует, чтобы вся информация передавалась функции в качестве параметров. К тому времени, когда функция CreateWindow возвращает управление программе, окно уже создано внутри Windows. Однако на экране монитора оно еще не появилось. Необходимы еще два вызова. Первый из них:
ShowWindow (hwnd, iCmdShow);
Первым параметром является описатель только что созданного функцией CreateWindow окна. Вторым параметром является величина iCmdShow, передаваемая в качестве параметра функции WinMain. Он задает начальный вид окна на экране. Если iCmdShow имеет значение SW_SHOWNORMAL, на экран выводится обычное окно. Если iCmdShow имеет значение SW_SHOWMINNOACTIVE, то окно не выводится, а на панели задач появляются его имя и иконка. Функция ShowWindow выводит окно на экран. Вызов функции:
UpdateWindow (hwnd);
вызывает затем перерисовку рабочей области. Для этого в оконную процедуру (функция WndProc) посылается сообщение WM_PAINT.
Цикл обработки сообщений
Windows поддерживает "очередь сообщений" (message queue) для каждой программы, работающей в данный момент в системе. Когда происходит ввод информации, Windows преобразует ее в "сообщение", которое помещается в очередь сообщений программы. Программа извлекает сообщения из очереди сообщений, выполняя блок команд, известный как "цикл обработки сообщений" (message loop):
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return msg.wParam;
Переменная msg — это структура типа MSG, которая определяется в заголовочных файлах Windows.
Если поле message сообщения, извлеченного из очереди сообщений, равно любому значению, кроме WM_QUIT, то функция GetMessage возвращает ненулевое значение. Сообщение WM_QUIT заставляет программу прервать цикл обработки сообщений.
Инструкция:
TranslateMessage (&msg);
передает структуру msg обратно в Windows для преобразования какого-либо сообщения с клавиатуры. Инструкция:
DispatchMessage (&msg);
также передает структуру msg обратно в Windows. Windows отправляет сообщение для его обработки соответствующей оконной процедуре – таким образом, Windows вызывает оконную процедуру.
Обработка сообщений
Каждое получаемое окном сообщение идентифицируется номером, который содержится в параметре iMsg оконной процедуры. В заголовочных файлах Windows определены идентификаторы, начинающиеся с префикса WM ("window message") для каждого типа сообщений.
Обычно используются конструкции switch для определения того, какое сообщение получила оконная процедура и то, как его обрабатывать. Если оконная процедура обрабатывает сообщение, то ее возвращаемым значением должен быть 0. Все сообщения, не обрабатываемые оконной процедурой, должны передаваться функции Windows, которая называется DefWindowProc. Значение, возвращаемое функцией DefWindowProc, должно быть возвращаемым значением оконной процедуры.
Дополнительная информация о работе оконного приложения может быть получена из соответствующей литературы и из системы помощи MS SDK.
Передача сообщений
При обмене данными на основе отображаемых в память файлов необходимо заботиться о синхронизации, для чего следует использовать объекты синхронизации.
Если скорость передачи данных не является критичной, можно воспользоваться передачей данных на основе механизма сообщений, не требующего синхронизации. Данный механизм основан на передаче сообщения WM_COPYDATA из одного приложения в другое при помощи функции SendMessage.
Сообщение WM_COPYDATA использует оба параметра – wParam и lParam. Через параметр wParam необходимо передать идентификатор окна, посылающего сообщение. Параметр lParam используется для передачи указателя на предварительно заполненную структуру COPYDATASTRUCT, в которой находится ссылка на передаваемые данные.
Если приложение Windows обрабатывает сообщение WM_COPYDATA, то соответствующий обработчик должен вернуть значение TRUE, а если нет – FALSE.
Перед посылкой сообщения WM_COPYDATA приложение должно заполнить структуру COPYDATASTRUCT.
Формат структуры COPYDATASTRUCT:
typedef struct tagCOPYDATASTRUCT
{
DWORD dwData; // 32-разрядные данные
DWORD cbData; // размер передаваемого буфера с данными
PVOID lpData; // указатель на буфер с данными
} COPYDATASTRUCT;
В поле dwData можно записать произвольное 32-разрядное значение, которое будет передано вместе с сообщением. В поле lpData можно записать указатель на буфер с данными, размер которого заносится в поле cbData.
Когда функция окна, принимающего приложения, получит сообщение WM_COPYDATA, она должна скопировать в свой локальный буфер данные, которые были переданы вместе с сообщением.
Если необходимо передать из одного приложения в другое только 32-разрядное значение, в поле lpData можно записать константу NULL.
Источник: Панченко В.І та ін. Системне програмне забезпечення Windows [текст] навч. посібник П16 [з дисципліни "Системне програмне забезпечення"] / В.І. Панченко, А. М. Клименко, И. В. Максита, - Харків: НТУ "ХПІ", 2009 - 196 с.