Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

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

© Александр Фролов, Григорий Фролов
Том 26, часть 1, М.: Диалог-МИФИ, 1996, 272 стр.

[Назад] [Содеожание] [Дальше]

Асинхронные операции с файлами

Когда вы работали с файлами в операционных системах MS-DOS или Microsoft Windows версии 3.1, то вы могли выполнять только синхронные операции с файлами. Это означает, что программа MS-DOS или 16-разрядное приложение Microsoft Windows, вызывая функции для работы с файлами, приостанавливали свою работу до тех пор, пока нужная операция (запись или чтение) не будет выполнена. Это и понятно - если приложение вызывает функцию _lread XE "_lread" или _lwrite XE "_lwrite" , она не вернет управление до тех пор, пока не будет завершена, соответственно, операция чтения или записи.

Приложение, запущенное в среде мультизадачной операционной системы Microsoft Windows NT, может совместить операции ввода или вывода с выполнением другой полезной работы. Например, процессор электронных таблиц может пересчитывать одну таблицу во время загрузки другой с диска.

Пользуясь сведениями, полученными из нашей книги, вы и сами без труда сможете организовать такую обработку, например, с помощью отдельных задач, выполняющих пересчет таблиц и загрузку таблиц с диска. Задача, работающая с диском, может открыть файл таблицы (пользуясь для этого функцией CreateFile XE "CreateFile" ) и затем выполнить его синхронное чтение функцией ReadFile XE "ReadFile" или даже функцией _lread XE "_lread" , если она вам больше нравится. Создавая отдельную задачу для чтения файла, вы и в самом деле сможете совместить в своем приложении выполнение файловых операций с обработкой данных.

Однако операционная система Microsoft Windows NT позволяет решить задачу совмещения файловых операций с другой работой намного проще. Для этого вы должны выполнять файловые операции асинхронно при помощи уже известных вам функций ReadFile XE "ReadFile" и WriteFile XE "WriteFile" .

Как это сделать?

Прежде всего, открывая или создавая файл функцией CreateFile XE "CreateFile" вы должны указать флаг FILE_FLAG_OVERLAPPED XE "FILE_FLAG_OVERLAPPED" . Далее, перед вызовом функций ReadFile XE "ReadFile" или WriteFile XE "WriteFile" вы должны подготовить структуру типа OVERLAPPED и передать ее адрес этим функциям через параметр lpOverlapped.

Структура OVERLAPPED определена следующим образом:


typedef struct _OVERLAPPED 
{ 
  DWORD  Internal;      // зарезервировано
  DWORD  InternalHigh;  // зарезервировано
  DWORD  Offset;        // младшее слово позиции в файле
  DWORD  OffsetHigh;    // старшее слово позиции в файле
  HANDLE hEvent;   // идентификатор события, который будет 
                   // установлен в отмеченное состояние 
                   // после завершения операции
} OVERLAPPED;    

В этой структуре вы должны заполнить поля Offset, OffsetHigh и hEvent. Поля Internal и InternalHigh зарезервированы для использования операционной системой.

В поля Offset и OffsetHigh необходимо записать смещение в файле, относительно которого будет выполняться асинхронная операция записи или чтения. Если для представления смещения достаточно 32 разрядов, в поле OffsetHigh нужно записать значение NULL.

В поле hEvent нужно записать идентификатор созданного предварительно объекта-события, который будет использоваться для синхронизации задач. Если записать в это поле значение NULL, для синхронизации будет использован идентификатор файла. Остановимся на этом подробнее.

Когда функции ReadFile или WriteFile вызывается для выполнения синхронной операции, она возвращает управление только после того, как операция, соответственно, чтения или записи будет завершена. Если же эти функции вызываются в асинхронном режиме, они только инициируют процесс чтения или записи, сразу же возвращая управление, не дожидаясь завершения операции. Для выполнения операции в этом случае операционной системой неявно создается отдельная задача.

Такми образом, задача, вызвавшая функцию ReadFile или WriteFile в асинхронном режиме, продолжит свою работу до завершения файловой операции. Если же этой задаче (или другой задаче) требуется дождаться завершения файловой операции, необходимо использовать средства синхронизации, описанные нами в предыдущей главе.

Самый простой способ синхронизации заключается в том, что задача, вызывающая функции ReadFile или WriteFile в асинхронном режиме, записывает в поле hEvent структуры OVERLAPPED значение NULL. После вызова указанных выше функций, когда задаче нужно дождаться завершения выполнения асинхронной операции, она вызывает функцию WaitForSingleObject XE "WaitForSingleObject" , передвая ей в качестве первого параметра идентификатор файла:


WaitForSingleObject(hFile, INFINITE);

Другой способ предполагает создание отдельного объекта-события, идентификатор которого записывается в поле hEvent структуры OVERLAPPED при инициализации последней. В этом случае в качестве первого параметра функции WaitForSingleObject XE "WaitForSingleObject" следует передать идентификатор этого объекта-события. Как только файловая операция будет завершена, объект-событие перейдет в отмеченное сосотояние.

Третий способ синхронизации заключается в использовании функции GetOverlappedResult XE "GetOverlappedResult" . Эта функция обычно используется для проверки результата выполнения асинхронной файловой операции:


BOOL GetOverlappedResult(
  HANDLE  hFile,                      // идентификатор файла 
  LPOVERLAPPED lpOverlapped,   // адрес структуры OVERLAPPED 
  LPDWORD lpNumberOfBytesTransferred, // адрес счетчика байт 
  BOOL    bWait);                     // флаг ожидания 

Через параметры hFile и lpOverlapped передются, соответственно, идентификатор файла, для которого выполнялась асинхронная операция, и адрес адрес структуры OVERLAPPED, подготовленной перед выполнением операции.

В переменную, адрес которой передается функции через параметр lpNumberOfBytesTransferred, записывается количество действительно прочитанных или записанных байт данных.

Параметр bWait может принимать значения TRUE или FALSE. В первом случае функция GetOverlappedResult будет дожидаться завершения выполнения операции (вот вам еще одно средство синхронизации). Если же значение параметра bWait равно FALSE, то если при вызове функции операция еще не завершилась, функция GetOverlappedResult вернет значение FALSE (признак ошибки).

При нормальном завершении (без ошибок) функция GetOverlappedResult возвращает значение TRUE.

В программном интерфейсе операционной системы Microsoft Windows NT есть еще две функции, специально предназначенные для выполнения асинхронных операций с файлами. Это функции ReadFileEx XE "ReadFileEx" и WriteFileEx XE "WriteFileEx" :


BOOL ReadFileEx(
  HANDLE hFile,                // идентификатор файла 
  LPVOID lpBuffer,             // адрес буфера 
  DWORD  nNumberOfBytesToRead, // количество байт для чтения 
  LPOVERLAPPED lpOverlapped,   // адрес структуры OVERLAPPED 
  LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);//адрес 
             // функции, вызываемой после завершения операции 

BOOL WriteFileEx(
  HANDLE hFile,                 // идентификатор файла 
  LPVOID lpBuffer,              // адрес буфера 
  DWORD  nNumberOfBytesToWrite, // количество байт для записи
  LPOVERLAPPED lpOverlapped,    // адрес структуры OVERLAPPED 
  LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);//адрес 
             // функции, вызываемой после завершения операции 

Этим функциям через параметр hFile необходимо передать идентификатор файла, созданного или открытого функцией CreateFile XE "CreateFile" c bcgjkmpjdfybtv флагf FILE_FLAG_OVERLAPPED XE "FILE_FLAG_OVERLAPPED" .

Через параметр lpBuffer передается адрес буфера, который будет использоваться функциями ReadFile Ex и WriteFile Ex, соответственно, для чтения и записи. При этом параметр nNumberOfBytesToRead определяет количество байт, которые будут прочитаны функцией ReadFileEx, а параметр nNumberOfBytesToWrite - количество байт, которые будут записаны функцией WriteFileEx.

Назначение параметра lpOverlapped аналогично назначению этого же параметра в функциях ReadFile и WriteFile.

Через параметр lpCompletionRoutine вы должны передать обеим функциям адрес функции завершения, которая будет вызвана после выполнения операции. Прототип такой функции приведен ниже (имя функции может быть любым):


VOID WINAPI CompletionRoutine(
  DWORD dwErrorCode,               // код завершения 
  DWORD dwNumberOfBytesTransfered, // количество прочитанных 
                               // или записанных байт данных 
  LPOVERLAPPED lpOverlapped);  // адрес структуры OVERLAPPED 

Более подробное описание функций ReadFile Ex и WriteFile Ex вы найдете в документации, которая поставляется вместе с SDK.

[Назад] [Содеожание] [Дальше]