Подія (об'єкт події, 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).
Джерела
- ↑ а б MSDN: Event Objects [Архівовано 15 жовтня 2012 у Wayback Machine.].
- ↑ MSDN: CreateEvent function [Архівовано 22 червня 2012 у Wayback Machine.].
- ↑ а б в Коноваленко І.В., Федорів П.С. Системне програмування у Windows з прикладами на Delphi, Т:ТНТУ.- 2012 [Архівовано 8 грудня 2012 у Wayback Machine.].
- ↑ MSDN: OpenEventfunction [Архівовано 18 квітня 2012 у Wayback Machine.].
Дивись також