Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Основы программирования для информатиков и инженеров. Часть 2 Прикладное программирование в Windows.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
603 Кб
Скачать
☆

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: опять потоки выполняются не параллельно, а строго последовательно.