Исходные тексты приложения EVENT
Рассмотрим сначала исходные тексты приложения EVENT, представленные в листинге 4.1.
Листинг 4.1. Файл event/event1.c
#include <windows.h>
#include <stdio.h>
#include <conio.h>
// Идентификаторы объектов-событий, которые используются
// для синхронизации задач, принадлежащих разным процессам
HANDLE hEvent;
HANDLE hEventTermination;
// Имя объекта-события для синхронизации ввода и отображения
CHAR lpEventName[] =
"$MyVerySpecialEventName$";
// Имя объекта-события для завершения процесса
CHAR lpEventTerminationName[] =
"$MyVerySpecialEventTerminationName$";
int main()
{
DWORD dwRetCode;
printf("Event demo application, master process\n"
"(C) A. Frolov, 1996, Email: frolov@glas.apc.org\n");
// Создаем объект-событие для синхронизации
// ввода и отображения, выполняемого в разных процессах
hEvent = CreateEvent(NULL, FALSE, FALSE, lpEventName);
// Если произошла ошибка, получаем и отображаем ее код,
// а затем завершаем работу приложения
if(hEvent == NULL)
{
fprintf(stdout,"CreateEvent: Error %ld\n",
GetLastError());
getch();
return 0;
}
// Если объект-событие с указанным именем существует,
// считаем, что приложение EVENT уже было запущено
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
printf("\nApplication EVENT already started\n"
"Press any key to exit...");
getch();
return 0;
}
// Создаем объект-событие для определения момента
// завершения работы процесса ввода
hEventTermination = CreateEvent(NULL,
FALSE, FALSE, lpEventTerminationName);
if(hEventTermination == NULL)
{
fprintf(stdout,"CreateEvent (Termination): Error %ld\n",
GetLastError());
getch();
return 0;
}
// Цикл отображения. Этот цикл завершает свою работу
// при завершении процесса ввода
while(TRUE)
{
// Проверяем состояние объекта-события, отвечающего
// за контроль завершения процесса ввода. Так как
// указано нулевое время ожидания, такая проверка
// не уменьшает заметно скорость работы приложения
dwRetCode = WaitForSingleObject(hEventTermination, 0);
// Если объект-событие перешел в отмеченное состояние,
// если процесс ввода завершил свою работу, или
// если при ожидании произошла ошибка,
// останавливаем цикл отображения
if(dwRetCode == WAIT_OBJECT_0
dwRetCode == WAIT_ABANDONED
dwRetCode == WAIT_FAILED)
break;
// Выполняем ожидание ввода символа в процессе,
// который работает с клавиатурой
dwRetCode = WaitForSingleObject(hEvent, INFINITE);
// При возникновении ошибки прерываем цикл
if(dwRetCode == WAIT_FAILED
dwRetCode == WAIT_ABANDONED)
break;
// В ответ на каждый символ, введенный процессом, который
// работает с клавиатурой, отображаем символ '*'
putch('*');
}
// Закрываем идентификаторы объектов-событий
CloseHandle(hEvent);
CloseHandle(hEventTermination);
return 0;
}
В глобальных переменных lpEventName и lpEventTerminationName мы храним имена двух объектов-событий, которые будут использоваться для синхронизации. Эти же имена будут указаны функции OpenEvent в приложении EVENTGEN, которое мы рассмотрим чуть позже.
Объекты-события создаются нашим приложением при помощи функции CreateEvent, вызываемой следующим способом:
hEvent = CreateEvent(NULL, FALSE, FALSE, lpEventName);
hEventTermination = CreateEvent(NULL, FALSE, FALSE,
lpEventTerminationName);
Здесь в качестве атрибутов защиты мы указываем значение NULL. Через второй и третий параметр функции CreateEvent имеют значение FALSE, поэтому создается автоматический объект-событие, которое изначально находится в неотмеченном состоянии. Имя этого события, доступное всем запущенным приложениям, передается функции CreateEvent через последний параметр.
Обратите внимание на следующий фрагмент кода, который расположен сразу после вызова функции, создающей объект-событие с именем lpEventName:
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
printf("\nApplication EVENT already started\n"
"Press any key to exit...");
getch();
return 0;
}
Функция CreateEvent может завершиться с ошибкой и в этом случае она возвращает значение NULL. Однако пользователь может попытаться запустить приложение EVENT два раза. В первый раз при этом будет создан объект-событие с именем lpEventName. Когда же функция CreateEvent будет вызвана для этого имени еще раз при попытке повторного запуска приложения, она не вернет признак ошибки, несмотря на то что объект-событие с таким именем уже существует в системе. Вместо этого функция вернет идентификатор для уже существующего объекта-события.
Как распознать такую ситуацию?
Очень просто - достаточно сразу после вызова функции CreateEvent проверить код завершения при помощи функции GetLastError. Как мы уже говорили, в случае попытки создания объекта-события с уже существующим в системе именем эта функция вернет значение ERROR_ALREADY_EXISTS. Как только это произойдет, наше приложение выдает сообщение о том, что его копия уже запущена, после чего завершает свою работу.
Таким образом мы нашли еще один способ (далеко не последний), с помощью которого в операционной системе Microsoft Windows NT можно предотвратить повторный запуск приложений. До сих пор мы решали эту задачу в графических приложениях с помощью функции FindWindow.
Продолжим изучение исходный текстов приложения EVENT.
После того как приложение создаст два объекта-события, оно входит в цикл отобаржения символа ‘*’.
В этом цикле прежде всего проверяется состояние объекта-события с идентификатором hEventTermination:
dwRetCode = WaitForSingleObject(hEventTermination, 0);
if(dwRetCode == WAIT_OBJECT_0
dwRetCode == WAIT_ABANDONED
dwRetCode == WAIT_FAILED)
break;
Приложение EVENTGEN переводит этот объект в отмеченное состояние перед своим завершением.
Обращаем ваше внимание на то что функции WaitForSingleObject указано нулевое время ожидания. В этом случае функция проверяет состояние объекта и сразу же возвращает управление.
Далее выполняется ожидание объекта-события с идентификатором hEvent:
dwRetCode = WaitForSingleObject(hEvent, INFINITE);
if(dwRetCode == WAIT_FAILED
dwRetCode == WAIT_ABANDONED)
break;
Если это ожидание завершилось с ошибкой, выполняется выход из цикла. Если же оно было выполнено без ошибок, приложение EVENT отображает символ ‘*’, пользуясь для этого функцией putch, известной вам из практики программирования для MS-DOS.
Перед завершением работы приложения мы выполняем освобождение идентификаторов созданных объектов-событий, пользуясь для этого функцией CloseHandle:
CloseHandle(hEvent);
CloseHandle(hEventTermination);