Программирование для Windows NT

Исходные тексты приложения 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);


Содержание раздела