Приложение Fmap/Server
Для иллюстрации методики обмена данными мжеду различными процессами с использованием файлов, отображаемых на память, мы подготовили исходные тексты двух консольных приложений: Fmap/Server и Fmap/Client. Эти приложения работают в паре (рис. 2.1).
Рис. 2.1. Взаимодействие консольных приложений Fmap/Server и Fmap/Client
Приложение Fmap/Server создает отображение и два объекта-события. Первый объект предназначен для работы с клавиатурой, второй - для обнаружения момента завершения приложения Fmap/Client. Объекты-события были описаны нами в предыдущем томе “Библиотеки системного программиста”, посвященном программированию для операционной системы Microsoft Windows NT.
Приложение Fmap/Client открывает созданное отображение и объекты-события, а затем в цикле вводит символы с клавиатуры, переключая один из объектов-событий в отмеченное состояние при вводе каждого символа. Коды введенных символов записываются в отображенную память.
По мере того как пользователь вводит символы в окне приложения Fmap/Client, приложение Fmap/Server отображает их в своем окне, получая коды введенных символов из отображенной памяти. Для синхронизации используется объект-событие, выделенное для работы с клавиатурой.
Если пользователь нажимает клавишу <Esc> в окне приложения Fmap/Client, это приложение отмечает оба события и завершает свою работу. Приложение Fmap/Server, обнаружив, что второй объект-событие оказался в отмеченном состоянии, также завершает свою работу. Таким образом, если завершить работу приложения Fmap/Client, то приложение Fmap/Server также будет завершено.
Исходный текст приложения Fmap/Server представлен в листинге 2.1.
Листинг 2.1. Файл fmap/server/server.c
#include <windows.h>
#include <stdio.h>
#include <conio.h>
// Идентификаторы объектов-событий, которые используются
// для синхронизации задач, принадлежащих разным процессам
HANDLE hEventChar;
HANDLE hEventTermination;
HANDLE hEvents[2];
// Имя объекта-события для синхронизации ввода и отображения
CHAR lpEventName[] =
"$MyVerySpecialEventName$";
// Имя объекта-события для завершения процесса
CHAR lpEventTerminationName[] =
"$MyVerySpecialEventTerminationName$";
// Имя отображния файла на память
CHAR lpFileShareName[] =
"$MyVerySpecialFileShareName$";
// Идентификатор отображения файла на память
HANDLE hFileMapping;
// Указатель на отображенную область памяти
LPVOID lpFileMap;
int main()
{
DWORD dwRetCode;
printf(" Mapped and shared file, server process\n"
"(C) A. Frolov, 1996, Email: frolov@glas.apc.org\n");
// Создаем объект-событие для синхронизации
// ввода и отображения, выполняемого в разных процессах
hEventChar = CreateEvent(NULL, FALSE, FALSE, lpEventName);
// Если произошла ошибка, получаем и отображаем ее код,
// а затем завершаем работу приложения
if(hEventChar == 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;
}
// Создаем объект-отображение
hFileMapping = CreateFileMapping((HANDLE)0xFFFFFFFF,
NULL, PAGE_READWRITE, 0, 100, lpFileShareName);
// Если создать не удалось, выводим код ошибки
if(hFileMapping == NULL)
{
fprintf(stdout,"CreateFileMapping: Error %ld\n",
GetLastError());
getch();
return 0;
}
// Выполняем отображение файла на память.
// В переменную lpFileMap будет записан указатель на
// отображаемую область памяти
lpFileMap = MapViewOfFile(hFileMapping,
FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
// Если выполнить отображение не удалось,
// выводим код ошибки
if(lpFileMap == 0)
{
fprintf(stdout,"MapViewOfFile: Error %ld\n",
GetLastError());
getch();
return 0;
}
// Готовим массив идентификаторов событий
// для функции WaitForMultipleObjects
hEvents[0] = hEventTermination;
hEvents[1] = hEventChar;
// Цикл отображения. Этот цикл завершает свою работу
// при завершении процесса ввода
while(TRUE)
{
// Выполняем ожидание одного из двух событий:
// - завершение клиентского процесса;
// - завершение ввода символа
dwRetCode = WaitForMultipleObjects(2,
hEvents, FALSE, INFINITE);
// Если ожидание любого из двух событий было отменено,
// если произошло первое событие (завершение клиентского
// процесса) или если произошла ошибка, прерываем цикл
if(dwRetCode == WAIT_ABANDONED_0
dwRetCode == WAIT_ABANDONED_0 + 1
dwRetCode == WAIT_OBJECT_0
dwRetCode == WAIT_FAILED)
break;
// Читаем символ из первого байта отображенной
// области памяти, записанный туда клиентским
// процессом, и отображаем его в консольном окне
putch(*((LPSTR)lpFileMap));
}
// Закрываем идентификаторы объектов-событий
CloseHandle(hEventChar);
CloseHandle(hEventTermination);
// Отменяем отображение файла
UnmapViewOfFile(lpFileMap);
// Освобождаем идентификатор созданного
// объекта-отображения
CloseHandle(hFileMapping);
return 0;
}
В глобальных переменных hEventChar и hEventTermination хранятся идентификаторы объектов-событий, предназначенных, соответственно, для работы с клавиатурой и для фиксации момента завершения работы приложения Fmap/Client. Эти же идентификаторы записываются в глобальный массив hEvents, который используется функцией WaitForMultipleObjects.
Глобальные имена объектов-событий хранятся в переменных lpEventName и lpEventTerminationName.
Имя отображения записывается в массив lpFileShareName, а идентификатор этого отображения - в глобальную переменную hFileMapping.
После выполнения отображения адрес отображенной области памяти, предназначенной для обмена данными с другим процессом, сохраняется в глобальной переменной lpFileMap.
Функция main приложения Fmap/Server создает два объекта-события, пользуясь для этого функцией CreateEvent. Описание этой функции вы найдете в предыдущем томе “Библиотеки системного программиста”.
Далее функция main создает объект-отображение и выполняет отображение, вызывая для этого, соответственно, функции CreateFileMapping и MapViewOfFile. Так как в качестве идентификатора файла функции CreateFileMapping передается значение (HANDLE)0xFFFFFFFF, отображение будет создано непосредственно в виртуальной памяти без использования файла, расположенного на диске.
После инициализации массива hEvents функция main запускает цикл, в котором выполняется ожидание событий и вывод символов, записанных приложением Fmap/Client в отображенную область виртуальной памяти.
Для ожидания двух событий используется функция WaitForMultipleObjects. Через третий параметр этой функции передается значение FALSE, поэтому ожидание прекращается в том случае, если любое из событий переходит в отмеченное состояние.
В том случае, когда в отмеченное состояние перешел объект-событие hEventTermination, функция WaitForMultipleObjects возвращает значение WAIT_OBJECT_0. Обнаружив это, функция main завершает свою работу, потому что событие hEventTermination отмечается при завершении работы клиентского приложения Fmap/Client.
Если же в отмеченное состояние переходит объект-событие hEventChar, функция WaitForMultipleObjects возвращает значение WAIT_OBJECT_0 + 1. В этом случае функция main читает первый байт из отображенной области памяти и выводит его в консольное окно при помощи хорошо знакомой вам из программирования для MS-DOS функции putch:
putch(*((LPSTR)lpFileMap));
Перед своим завершением функция main закрывает идентификаторы объектов-событий, отменяет отображение и освобождает идентификатор этого отображения.