Подія (об'єкт події, event object) в операційній системі Windows — об'єкт для синхронізації виконання процесів (потоків), який може знаходитися у двох станах (сигнальному та несигнальному)[1].

Застосунки (прикладні програми) можуть використовувати об'єкти подій у різних ситуаціях, щоб повідомити потік про виникнення потрібної події. В загальному випадку, об’єкти подій використовують для сигналізації очікуючим потокам про досягнення певних умов виконання програми.

Види об'єктів подій ред.

Бувають події ручного скидання (manual-reset) та автоматичного скидання (auto-reset). Подія ручного скидання, будучи сигнальною, залишається такою, доки не буде вимкнена програмно викликом функції API ResetEvent. Події автоматичного скидання перемикаються у несигнальний стан системою, коли один із очікуючих потоків закінчить роботу[1].

Робота з подіями ред.

Для створення події використовується функція API CreateEvent. Її опис мовою C++:[2]:

HANDLE WINAPI CreateEvent(
 __in_opt  LPSECURITY_ATTRIBUTES lpEventAttributes,
 __in      BOOL bManualReset,
 __in      BOOL bInitialState,
 __in_opt  LPCTSTR lpName
);

Опис мовою Delphi[3]:

function CreateEvent(
 lpEventAttributes: PSecurityAttributes;  // Атрибути секретності
 bManualReset,         // Задає вид події: ручна (true) чи автоматична (false)
 bInitialState: BOOL;  // Задає початк. стан. Якщо true - сигнальний 
 lpName: PChar         // Назва (або nil, якщо не потрібно)
): THandle;

Функція повертає дескриптор створеного об’єкта або нуль у випадку невдачі. Якщо об’єкт події із заданою назвою вже існує, то повертається його дескриптор. При цьому ігноруються параметри bManualReset та bInitialState, а функція GetLastError поверне значення ERROR_ALREADY_EXISTS. Ім’я події не повинно збігатися з іменами існуючих об’єктів типу Semaphore, Mutex, Job, Waitable Timer або File Mapping. Якщо відомо, що подія вже існує, її дескриптор можна отримати функцією OpenEvent. Опис на C++[4]:

HANDLE WINAPI OpenEvent(
 __in  DWORD dwDesiredAccess,
 __in  BOOL bInheritHandle,
 __in  LPCTSTR lpName
);

Опис мовою Delphi[3]:

function OpenEvent(
 dwDesiredAccess: DWORD;  // Задає права доступу до об'єкта
 bInheritHandle: BOOL;    // Вказує, чи може об'єкт успадковуватися 
                          // дочірніми процесами
 lpName: PChar            // Ім'я об'єкта
): THandle;

Функція повертає дескриптор об'єкта або нуль у випадку помилки. Параметр dwDesiredAccess може набувати одне з таких значень:

  • EVENT_ALL_ACCESS – програма отримує повний доступ до об'єкта;
  • EVENT_MODIFY_STATE – програма може змінювати стан об'єкта функціями SetEvent і ResetEvent;
  • SYNCHRONIZE – можна використовувати об'єкт події у функціях синхронізації.

Після отримання дескриптора події його можна використовувати. Для цього Windows API надає такі функції: SetEvent, ResetEvent та PulseEvent. Функція SetEvent встановлює об'єкт у сигнальний стан:

BOOL WINAPI SetEvent(__in  HANDLE hEvent);   // Опис мовою C++
function SetEvent(hEvent: THandle): BOOL;    // Опис мовою Delphi

Функція ResetEvent скидає об'єкт, встановлюючи його в несигнальний стан:

BOOL WINAPI ResetEvent(__in  HANDLE hEvent); // Опис мовою C++
function ResetEvent(hEvent: THandle): BOOL;  // Опис мовою Delphi

Функція PulseEvent встановлює об'єкт у сигнальний стан, дозволяє відпрацювати всім функціям синхронізації, які його очікують, а потім знову скидає об’єкт події.

BOOL WINAPI PulseEvent(__in  HANDLE hEvent); // Опис мовою C++
function PulseEvent(hEvent: THandle): BOOL;  // Опис мовою Delphi

Приклад використання об'єкта події ред.

Вказаний приклад демонструє, як за допомогою системного об’єкта події програма може отримувати повідомлення про зміни у певному підрозділі (текст програми мовою Delphi) реєстру Windows[3].

function ThreadFunc(Ptr: Pointer): LongInt;
var evnt : THandle;
   RK   : HKey;
   S    : string;
begin
 evnt := CreateEvent(nil, false, false, nil);
 RegOpenKeyEx(HKEY_CURRENT_USER, 'Software\IC\RegDemo', 0, KEY_READ, RK);
 RegNotifyChangeKeyValue(RK, true, REG_NOTIFY_CHANGE_LAST_SET, evnt, true);
 if WaitForSingleObject(evnt, INFINITE) = WAIT_OBJECT_0 then
 begin
   S := TimeToStr(Time) + ': У реєстр внесено зміни!';
   MessageBox(hWindow, PWideChar(S), 'Ооо!', MB_ICONINFORMATION);
 end;
 RegCloseKey(RK);
 CloseHandle(evnt);
end;

Спочатку викликом функції CreateEvent створюється об'єкт події автоматичного скидання, дескриптор якого зберігається у змінній evnt. Після цього відкривається потрібна гілка реєстру та за допомогою функції RegNotifyChangeKeyValue здійснюється підписка на отримання повідомлень про зміни у реєстрі. Таким чином RegNotifyChangeKeyValue пов'язує зміни у реєстрі з об’єктом події evnt. Після цього за допомогою функції WaitForSingleObject здійснюється нескінченне очікування на перехід об’єкта evnt у сигнальний стан.

Якщо у вказану гілку реєстру (Software\IC\RegDemo) внесено зміни, то наш потік отримає відповідний сигнал, і програма видасть відповідне повідомлення (за допомогою функції MessageBox).

Джерела ред.

  1. а б MSDN: Event Objects [Архівовано 15 жовтня 2012 у Wayback Machine.].
  2. MSDN: CreateEvent function [Архівовано 22 червня 2012 у Wayback Machine.].
  3. а б в Коноваленко І.В., Федорів П.С. Системне програмування у Windows з прикладами на Delphi, Т:ТНТУ.- 2012 [Архівовано 8 грудня 2012 у Wayback Machine.].
  4. MSDN: OpenEventfunction [Архівовано 18 квітня 2012 у Wayback Machine.].

Дивись також ред.