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

       

Функции ReadFile и WriteFile


С помощью функций ReadFile и WriteFile приложение может выполнять, соответственно, чтение из файла и запись в файл. По своему назначению эти функции аналогичны функциям _lread, _lwrite, _hread и _hwrite из программного интерфейса Microsoft Windows версии 3.1.

Приведем прототипы функций ReadFile и WriteFile:

BOOL ReadFile(

  HANDLE  hFile,                // идентификатор файла

  LPVOID  lpBuffer,             // адрес буфера для данных

  DWORD   nNumberOfBytesToRead, // количество байт, которые

                                // необходимо прочесть в буфер

  LPDWORD lpNumberOfBytesRead,  // адрес слова, в которое

                // будет записано количество прочитанных байт

  LPOVERLAPPED lpOverlapped); // адрес структуры типа

                              // OVERLAPPED

BOOL WriteFile(

  HANDLE  hFile,                 // идентификатор файла



  LPVOID  lpBuffer,       // адрес записываемого блока данных

  DWORD   nNumberOfBytesToWrite, // количество байт, которые

                                 // необходимо записать

  LPDWORD lpNumberOfBytesWrite,  // адрес слова, в котором

               // будет сохранено количество записанных байт

  LPOVERLAPPED lpOverlapped); // адрес структуры типа

                              // OVERLAPPED

Через параметр hFile этим функциям необходимо передать идентификатор файла, полученный от функции CreateFile.

Параметр lpBuffer должен содержать адрес буфера, в котором будут сохранены прочитанные данные (для функции ReadFile), или из которого будет выполняться запись данных (для функции WriteFile).

Параметр nNumberOfBytesToRead используется для функции ReadFile и задает количество байт данных, которые должны быть прочитаны в буфер lpBuffer. Аналогично, параметр nNumberOfBytesToWrite задает функции WriteFile размер блока данных, имеющего адрес lpBuffer, который должен быть записан в файл.

Так как в процессе чтения возможно возникновение ошибки или достижение конца файла, количество прочитанных или записанный байт может отличаться от значений, заданных, соответственно, параметрами nNumberOfBytesToRead и nNumberOfBytesToWrite. Функции ReadFile и WriteFile записывают количество действительно прочитанных или записанных байт в двойное слово с адресом, соответственно, lpNumberOfBytesRead и lpNumberOfBytesWrite.


Параметр lpOverlapped используется в функциях ReadFile и WriteFile  для организации аснхронного режима чтения и записи. Если запись выполняется синхронно, в качестве этого параметра следует указать значение NULL. Способы выполнения асинхронного чтения и записи мы рассмотрим позже. Заметим только, что для использования асинхронного режима файл должен быть открыт функцией CreateFile с использованием флага FILE_FLAG_OVERLAPPED. Если указан этот флаг, параметр lpOverlapped не может иметь значение NULL. Он обязательно должен содержать адрес подготовленной структуры типа OVERLAPPED.

Если функции ReadFile и WriteFile были выполнены успешно, они возвращают значение TRUE. При возникновении ошибки возвращается значение FALSE. В последнем случае вы можете получить код ошибки, вызвав функцию GetLastError.

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



Заметим, что для однозадачных приложений используется библиотека с именем libc.lib, а для мультизадачных - с именем libcmt.lib.

После того как вы указали мультизадачную библиотеку, вы можете использовать функции _beginthread и _beginthreadex для запуска задач. Приведем прототип функции _beginthread, описанный в файле process.h:

unsigned long _beginthread(

  void(*StartAddress)(void*), // адрес функции задачи

  unsigned uStackSize,    // начальный размер стека в байтах

  void     *ArgList);     // параметры для задачи

Заметим, что функция задачи, запускаемой при помощи функции _beginthread, не возвращает никакого значения. Ее адрес передается функции _beginthread через параметр StartAddress. Через параметр ArgList вы можете передать функции задачи один параметр.

Начальный размер стека, выделяемого задаче, указывается через параметр uStackSize. Так же как и в случае с функцией CreateThread, для размера стека можно указать нулевое значение. При этом для задачи создается стек такого же размера, что и для главной задачи процесса.

В случае успеха функция _beginthread возвращает идентификатор запущенной задачи. Если же произошла ошибка, возвращается значение -1.

Приведем пример использования функции _beginthread:

_beginthread(ThreadRoutine, 0, (void*)(Param));

Здесь запускается задача, функция которой имеет имя ThreadRoutine. Ей передается в качестве параметра значение Param.

Функция ThreadRoutine должна выглядеть следующим образом:

void ThreadRoutine(void *Param)

{

  . . .

  _endthread();

}

Заметим, что для завершения задачи здесь используется функция _endthread, не имеющая параметров. С помощью этой функции вы можете завершить задачу в любом месте функции задачи. Однако в приведенном выше фрагменте функцию _endthread можно было бы и не использовать, так как операция возврата из функции задачи также приведет к неявному вызову функции _endthread и, как следствие, к завершению задачи.


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