- •Часть 2 (продолжение): Прикладное программирование в Windows
- •Глава 8. Виртуальное окно: работа с текстом
- •8.1. Оконные координаты, цвета, отображение строк
- •Кодировка rgb-значений
- •Размеры системных метрик
- •8.2. Виртуальное окно
- •Способы заполнения прямоугольной области
- •8.3. Изменение шрифтов
- •Макроимена распространенных шрифтов
- •8.4. Создание собственных шрифтов
- •Глава 9. Программирование графики
- •9.1. Логическая система координат
- •Рекомендуемый самый яркий состав цветов
- •9.2. Пиксели, линии, дуги, прямоугольники, эллипсы и секторы
- •9.3. Работа с перьями и кистями
- •Типы линий, создаваемых пером
- •9.4. Технология виртуального окна
- •9.5. Режимы отображения и области вывода
- •Текущий режим отображения (mappind mode)
- •Глава 10. Работа с панелями инструментов
- •10.1. Общие элементы управления
- •Общие элементы управления
- •10.2. Подключение и инициализация общих элементов управления
- •10.3. Работа с панелью инструментов
- •Начальные состояния кнопок панели инструментов
- •Стиль кнопки определяется (значения поля fsStyle)
- •Сообщения к панели инструментов
- •10.4. Создание растрового изображения для панели инструментов
- •10.5. Включение подсказок
- •Глава 11. Спины, ползунки и индикаторы процессов
- •11.1. Работа со спином
- •Стили «up-down control»
- •Сообщения, которые можно посылать спину
- •11.2. Создание спина с «приятельским» окном
- •11.3. Работа с ползунком
- •Значения стилей для ползунка
- •Перечень сообщений, которые можно посылать ползунку
- •Перечень нотификационных сообщения ползунка
- •11.4. Индикатор процесса
- •Управляющие сообщения индикатору
- •Глава 12. Многозадачность: процессы и потоки
- •12.1. Создание нового процесса (отдельной задачи)
- •Разрешенные значения поля dwFlags
- •12.2. Многопотоковые программы
- •12.3. Работа с несколькими потоками
- •12.4. Синхронизация процессов и потоков: семафоры
- •12.5. Обработка событий
- •Глава 13. Приемы программного управления вычислительным процессом
- •13.1. Использование функций Проводника Explorer для работы в файловой системе
- •Управляющая информация Проводника
- •Режимы обработки файлов
- •13.2. Создание собственных динамических библиотек (dll-файлов)
- •13.3. Работа с буфером обмена Clipboard
- •Функции подсистемы Clipboard
- •Свойства выделяемого блока памяти
- •Форматы и типы данных
- •Литература
12.5. Обработка событий
Как уже отмечалось, события используются для того, чтобы сообщить потокам или процессам о возникновении определенной ситуации. Для создания объекта события используется функция API CreateEvent():
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpAttr,
BOOL Manual, BOOL Initial,
LPSTR lpszName};
Здесь параметр lpAttr является указателем на структуру атрибутов доступа. Если такие атрибуты не используются, как в случае с Windows, значение этого параметра может быть NULL. Значение параметра Manual определяет, что должно происходить с объектом события после того, как событие произошло. Если это значение равно TRUE (не нуль), то событие может быть сброшено только при помощи вызова функции ResetEvent(). В противном случае событие сбрасывается автоматически после того, как к нему получает доступ какой-либо блокированный процесс. Значение Initial задает начальное состояние события. Если оно равно TRUE, событие сразу устанавливается (сразу же подается сигнал о наступлении соответствующей ситуации). Если же это значение равно FALSE, событие инициализируется как сброшенное.
Параметр lpszName является указателем на строку, содержащую имя события. События представляют собой глобальные системные объекты, доступные всем процессам в системе. Поэтому, если разные процессы открывают объект события, задавая одно и то же имя, используется один и тот же системный объект. Этот параметр может быть задан как NULL, и в этом случае объект события определяется как локальный для данного процесса.
Функция CreateEvent() возвращает дескриптор созданного объекта события либо NULL при возникновении ошибки.
После создания объекта события процесс, ожидающий это событие, просто вызывает функцию WaitForSingleobject(), задавая в качестве первого параметра для нее дескриптор этого события. Тем самым выполнение процесса или потока приостанавливается до наступления соответствующего события.
Для того чтобы сигнализировать о наступлении события, используется функция SetEvent():
BOOL SetEvent(HANDLE hEventObject) ;
Здесь параметр hEventObject является дескриптором ранее созданного события. После вызова функции SetEvent() процесс или поток, ожидающий этого события и вызвавший для этого функцию WaitForSingleObject(), продолжит свое выполнение.
Чтобы понять, как нужно работать с объектами событий, модифицируйте предыдущую программу следующим образом. Во-первых, определите глобальный дескриптор (HANDLE)hEvent. Затем при создании главного окна создайте событие, добавив в код обработки сообщения WM_CREATE строку
hEvent = CreateEvent(NULL, FALSE, FALSE,"Myevent");
И наконец, измените функции MyThread1() и MyThread2() следующим образом:
//
// Начало выполнения 1-го потока
//
DWORD MyThread1(LPVOID param)
{
int i;
DWORD curTid = Tid1;
// Получить метрики текста
GetTextMetrics(memdc, &tm);
// Ждать события
if(WaitForSingleObject(hEvent,10000)==WAIT_TIMEOUT)
{
MessageBox((HWND)param, "Поток 1 - таймаут",
"Ошибка семафора",
MB_OK);
return 0;
}
for(i=0; i<10; i++)
{
Sleep(500); // Задержка 0,5 сек
sprintf(str, "Поток %ld, сигнал %d", curTid, i);
TextOut(memdc, X, Y, str, strlen(str));
Y = Y + tm.tmHeight + tm.tmExternalLeading;
InvalidateRect((HWND)param, NULL, 1) ;
Beep(600, 250); // 600 Гц, 0,25 сек
}
return 0;
}
//
// Начало выполнения 2-го потока
//
DWORD MyThread2(LPVOID param)
{
int i;
DWORD curTid = Tid2;
// Получить метрики текста
GetTextMetrics(memdc, &tm);
for(i=0; i<10; i++)
{
Sleep(200); // Задержка 0,2 сек
sprintf(str, "Поток %ld, сигнал %d", curTid, i);
TextOut(memdc, X, Y, str, strlen(str));
Y = Y + tm.tmHeight + tm.tmExternalLeading;
InvalidateRect((HWND)param, NULL, 1) ;
Beep(300, 250); // 300 Гц, 0,25 сек
}
// Послать информацию о событии
SetEvent(hEvent);
return 0;
}
Теперь при выполнении программы поток MyThreadl() будет блокирован до окончания выполнения потока MyThread2(), который сигнализирует об этом.
Пример 12-5. Многопотоковая обработка событий
// Многопотоковая программа, иллюстрирующая
// обработку событий
// с использованием виртуального окна
#include <Windows.h>
#include <String.h>
#include <Stdio.h>
#include "Proc.h"
#define Procmax 5
LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);
DWORD MyThread1(LPVOID); // Функция 1-го потока
DWORD MyThread2(LPVOID); // Функция 2-го потока
char szWinName[] = "МоеОкно"; // Имя класса окна
char str[255]; // Буфер строки вывода
int X=0, Y=0; // Текущие координаты строки
int procnum=0; // Количество активных процессов
DWORD Tid1; // Идентификатор 1-го потока
DWORD Tid2; // Идентификатор 3-го потока
int maxX, maxY; // Размеры экрана
HDC memdc; // DC виртуального окна
HBITMAP hbit; // Растр - это виртуальное окно
HBRUSH hbrush; // Дескриптор кисти
PROCESS_INFORMATION pinfo[Procmax];
HANDLE hEvent; // Дескриптор события
TEXTMETRIC tm;
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, // Имя класса
"Порождение процессов и потоков",
WS_OVERLAPPEDWINDOW,// Стиль окна
CW_USEDEFAULT, // Х-координата
CW_USEDEFAULT, // Y-координата
CW_USEDEFAULT, // Ширина окна
CW_USEDEFAULT, // Высота окна
HWND_DESKTOP, // Нет родит. окна
NULL, // Нет меню
hThisInst, // Дескрип. приложения
NULL); // Без дополит. аргументов
// Загрузить акселераторы
hAccel=LoadAccelerators(hThisInst,"MYMENU");
ShowWindow(hwnd,nWinMode) ; // Показать окно и
UpdateWindow(hwnd); // перерисовать содержимое
// Запустить цикл обработки сообщений
while(GetMessage (&msg,NULL,0,0))
if(!TranslateAccelerator(hwnd,hAccel,&msg))
{
TranslateMessage(&msg); // Использ.Клавиатуры
DispatchMessage (&msg); // Возврат к Windows
}
return msg. wParam;
}
// Следующая функция вызывается операционной системой
// Windows и получает в качестве параметров сообщения
// из очереди сообщений данного приложения
LRESULT CALLBACK WindowFunc(HWND hwnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT paintstruct;
STARTUPINFO startin;
switch(message)
{
case WM_CREATE:
hEvent = CreateEvent(NULL, FALSE, FALSE,
"Myevent");
// Получаем размеры экрана
maxX=GetSystemMetrics(SM_CXSCREEN);
maxY=GetSystemMetrics(SM_CYSCREEN);
hdc=GetDC(hwnd); // Совмест. с окном растр
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);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_PROCESS:
if(procnum == Procmax)
{
MessageBox(hwnd,
"Нельзя создать больше",
"Ошибка", MB_OK);
break;
} // Не более чем Procmax
// Получить метрики текста */
GetTextMetrics(memdc, &tm);
sprintf(str, "Порождается процесс %d",
procnum);
TextOut(memdc, X, Y, str, strlen(str));
Y = Y+tm.tmHeight+tm.tmExternalLeading;
InvalidateRect(hwnd,NULL,1); //Сообщить
// Порождение нового процесса
startin.cb=sizeof(STARTUPINFO);
startin.lpReserved = NULL;
startin.lpDesktop = NULL;
startin.lpTitle = NULL;
startin.dwFlags =STARTF_USESHOWWINDOW;
startin.cbReserved2 = 0;
startin.lpReserved2 = NULL;
startin.wShowWindow = SW_SHOWMINIMIZED;
CreateProcess(NULL, "Test.exe", NULL,
NULL, FALSE, 0, NULL,
NULL, &startin,
&(pinfo[procnum]));
procnum++;
break;
case ID_KILLPROC:
if(procnum)
procnum--;
else
{
MessageBox(hwnd,
"Больше процессов нет",
"Ошибка", MB_OK);
break;
}
// Получить метрики текста
GetTextMetrics(memdc, &tm);
TerminateProcess
(pinfo[procnum].hProcess, 0);
sprintf(str, "Процесс %d завершен",
procnum);
TextOut(memdc, X, Y, str, strlen(str));
Y = Y+tm.tmHeight+tm.tmExternalLeading;
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
case ID_THREAD:
CreateThread
(NULL, 0,
(LPTHREAD_START_ROUTINE)MyThread1,
(LPVOID)NULL, 0, &Tid1);
CreateThread
(NULL, 0,
(LPTHREAD_START_ROUTINE)MyThread2,
(LPVOID)NULL, 0, &Tid2);
InvalidateRect(hwnd, NULL, 1);
break;
case ID_HELP:
MessageBox(hwnd,
"F2: Новый процесс\n"
"F3: Завершить процесс\n"
"F4: Новый поток",
"Помощь:", MB_OK);
InvalidateRect(hwnd,NULL,1); //Сообщить
break;
}
break;
case WM_PAINT: // Перерисовка окна
hdc=BeginPaint(hwnd,&paintstruct); // Пол. DC
// Теперь копируем растр из памяти на экран
BitBlt(hdc,0,0,maxX,maxY,memdc,0,0,SRCCOPY);
EndPaint(hwnd,&paintstruct); // Освободить DC
break;
case WM_DESTROY: // Завершение программы
DeleteDC(memdc); // Удалить виртуальное окно
PostQuitMessage(0);
break;
default:
// Все сообщения, не обрабатываемые в данной
// функции, направляются на обработку по
// умолчанию
return DefWindowProc(hwnd,message,
wParam,lParam);
}
return 0;
}
//
// Начало выполнения 1-го потока
//
DWORD MyThread1(LPVOID param)
{
int i;
DWORD curTid = Tid1;
// Получить метрики текста
GetTextMetrics(memdc, &tm);
// Ждать события
if(WaitForSingleObject(hEvent,10000)==WAIT_TIMEOUT)
{
MessageBox((HWND)param, "Поток 1 - таймаут",
"Ошибка семафора",
MB_OK);
return 0;
}
for(i=0; i<10; i++)
{
Sleep(500); // Задержка 0,5 сек
sprintf(str, "Поток %ld, сигнал %d", curTid, i);
TextOut(memdc, X, Y, str, strlen(str));
Y = Y + tm.tmHeight + tm.tmExternalLeading;
InvalidateRect((HWND)param, NULL, 1) ;
Beep(600, 250); // 600 Гц, 0,25 сек
}
return 0;
}
//
// Начало выполнения 2-го потока
//
DWORD MyThread2(LPVOID param)
{
int i;
DWORD curTid = Tid2;
// Получить метрики текста
GetTextMetrics(memdc, &tm);
for(i=0; i<10; i++)
{
Sleep(200); // Задержка 0,2 сек
sprintf(str, "Поток %ld, сигнал %d", curTid, i);
TextOut(memdc, X, Y, str, strlen(str));
Y = Y + tm.tmHeight + tm.tmExternalLeading;
InvalidateRect((HWND)param, NULL, 1) ;
Beep(300, 250); // 300 Гц, 0,25 сек
}
// Послать информацию о событии
SetEvent(hEvent);
return 0;
}
Выполнение многопотоковой программы иллюстрируется точно так, как показано на рис. 12.4: опять потоки выполняются не параллельно, а строго последовательно.
