Функция Oem2Char (асинхронные операции с файлами)
Вариант функции Oem2Char, предназначенный для использования асинхронных операций с файлами, выглядит сложнее, однако он позволяет приложению выполнять дополнительную работу в то время когда происходит чтение или запись блока данных.
В асинхронном режиме для чтения и записи необходимо подготовить структуры типа OVERLAPPED. Мы делаем это следующим образом:
OVERLAPPED ovRead;
OVERLAPPED ovWrite;
ovRead.Offset = 0;
ovRead.OffsetHigh = 0;
ovRead.hEvent = NULL;
ovWrite.Offset = 0;
ovWrite.OffsetHigh = 0;
ovWrite.hEvent = NULL;
Структура ovRead используется для выполнения операций чтения. В поля OffsetHigh и Offset этой структуры мы записываем нулевые значения, поэтому чтение будет выполняться с самого начала файла.
Что же касается поля hEvent, то в него мы записываем значение NULL. При этом мы не будем создавать для синхронизации отдельный объект-событие, а воспользуемся идентификатором файла.
Структура ovWrite, которая предназначена для выполнения операций записи, используется аналогичным образом.
После подготовки структур ovRead и ovWrite функция Oem2Char начинает цикл перекодировки.
Прежде всего, в этом цикле запускается операция асинхронного чтения, для чего вызывается функция ReadFile:
bResult = ReadFile(hSrcFile, cBuf, sizeof(cBuf),
&dwBytesRead, &ovRead);
В качестве последнего параметра мы передаем этой функции адрес заранее подготовленной структуры ovRead.
Заметим, что в данном случае функция ReadFile вернет управление еще до завершения операции чтения, поэтому в переменную dwBytesRead не будет записано количествао прочитанных байт (так как пока неизвестно, сколько их удастся прочитать).
Далее функция Oem2Char проверяет код возврата фукнции ReadFile. При этом дополнительно вызывается функция GetLastError.
Если при запуске процедуры чтения был достигнут конец файла, эта функция вернет значение ERROR_HANDLE_EOF. В этом случае функция Oem2Char просто возвращает управление.
Если же функция GetLastError вернула значение ERROR_IO_PENDING, то это означает, что в настоящий момент происходит выполнение операции чтения. Приложение может вызвать функцию, например, IdleWork, для выполнения какой-либо фоновой работы.
Перед тем как продолжить свою работу, функция дожидается завершение операции чтения, вызывая для этого функцию WaitForSingleObject, описанную в предыдущем томе “Библиотеки системного программиста”:
WaitForSingleObject(hSrcFile, INFINITE);
При этом главная задача приложения перейдет в состояние ожидания до тех пор, пока не закончится операция чтения. Напомним, что в состоянии ожидания задача не отнимает циклы процессорного времени и не снижает производительность системы.
Далее функция проверяет результат выполнения асинхронной операции чтения, вызывая функцию GetOverlappedResult:
bResult = GetOverlappedResult(hSrcFile, &ovRead,
&dwBytesRead, FALSE);
Помимо всего прочего, эта функция записывает в локальную переменную dwBytesRead количество байт, прочитанных из исходного файла.
После дополнительных проверок ошибок функция Oem2Char выполняет преобразование содержимого буфера, заполненного прочитанными данными.
Вслед за этим в структуре ovRead изменяется содержимое поля Offset:
ovRead.Offset += dwBytesRead;
Значение, которое находится в этом поле, увеличивается на количество прочитанных байт. В результате при очередном вызове функции ReadFile будет запущено чтение для следующего блока данных. Так как мы не изменяем поле OffsetHigh, наше приложение способно работать с файлами, имеющими размер не более 4 Гбайт (что, однако, вполне достаточно).
Асинхронная операция записи прочитанных данных запускается при помощи функции WriteFile:
bResult = WriteFile(hDstFile, cBuf, dwBytesRead,
&dwBytesWritten, &ovWrite);
В качестве последнего параметра этой функции передается адрес заранее подготовленной структуры ovWrite.
После анализа кода возврата функции WriteFile вызывается функция GetOverlappedResult, с помощью которой определяется результат завершения операции записи:
GetOverlappedResult(hDstFile, &ovWrite,
&dwBytesWritten, TRUE);
Так как через последний параметр этой функции передается значение TRUE, функция GetOverlappedResult выполняет ожидание завершения операции записи. Кроме того, в локальную переменную dwBytesWritten эта функция заносит количество байт данных, записанных в выходной файл.
Так как асинхронные операци чтения и записи не изменяют текущую позицию в файле, после выполнения записи мы изменяем соответствующим образом содержимое поля Offset в структуре ovWrite:
ovWrite.Offset += dwBytesWritten;
Далее цикл перекодировки продолжает свою работу.