Функции 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 и, как следствие, к завершению задачи.