Документы для FILE_FLAG_OVERLAPPED на WriteFile() говорят, что вы должны предоставить OVERLAP и рекомендуют NULL для lpNumberOfBytesWritten, потому что значение вводит в заблуждение. Однако документы для GetOverlappedResult() говорят, что звонить нужно только в том случае, если WriteFile() вернул FALSE с ERROR_IO_PENDING. Таким образом, остается случай, когда ReadFile() / WriteFile() завершается в самом вызове API. Как вы должны получить количество прочитанных/записанных байтов? Вы полагаете, что это запрошенный номер? Но WriteFile() говорит: «При записи в неблокирующий дескриптор канала байтового режима с недостаточным буферным пространством WriteFile возвращает TRUE с * lpNumberOfBytesWritten < nNumberOfBytesToWrite».
ТИА!!





If hFile was opened with
FILE_FLAG_OVERLAPPEDThelpNumberOfBytesWrittenparameter should be set to NULL.
это неправда (ошибка или ложь). lpNumberOfBytesWrittenмогу должен быть установлен в НУЛЕВОЙ, но не должен. если запрос ввода-вывода завершен синхронно с успехом, в *lpNumberOfBytesWritten будет допустимое количество байтов.
также обратите внимание, что lpNumberOfBytesне должен указывает на местоположение, которое будет действительным до завершения операции (например, lpOverlapped) - например, он может указывать на локальную переменную в функции, и вы можете выйти из функции до завершения ввода-вывода - и это будет нормально слишком. система просто скопирует InternalHigh из OVERLAPPED в *lpNumberOfBytes. так в псевдокоде:
if (lpNumberOfBytes) *lpNumberOfBytes = (ULONG)lpOverlapped->InternalHigh;
очевидно правильное значение в *lpNumberOfBytes будет только в том случае, если ввод-вывод уже завершен успешно. так что и использовать его можно только в этом случае. система не запоминает значение lpNumberOfBytes, потому что оно должно быть действительным только во время вызова [Write|Read]File, но не во время активного ввода-вывода (что может быть дольше в случае асинхронного ввода-вывода)
GetOverlappedResult мы можем вызвать, если запрос ввода-вывода завершен синхронно с успехом (в случае ReadFile или WriteFile, если они возвращают TRUE) или если ожидание возвращено. этот API не может быть вызван только в случае сбоя запроса ввода-вывода (ReadFile или WriteFile вернуть FALSE и GetLastError() != ERROR_IO_PENDING)
поэтому лучше всего всегда передавать не 0 лпнумберофбайтес в API и использовать его, если API завершится только с успехом. в противном случае используйте GetOverlappedResult или, если вы говорите, что используете BindIoCompletionCallback - вы напрямую получили dwNumberOfBytesTransfered в обратном вызове.
поэтому концептуально можно использовать следующий код:
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
HANDLE hFile = CreateFile(*, FILE_GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
UCHAR buf[0x200];
OVERLAPPED ov = {};
ULONG NumberOfBytesRead;
ULONG dwError = BOOL_TO_ERROR(ReadFile(hFile, buf, sizeof(buf), &NumberOfBytesRead, &ov));
switch (dwError)
{
case ERROR_IO_PENDING:
dwError = BOOL_TO_ERROR(GetOverlappedResult(hFile, &ov, &NumberOfBytesRead, TRUE));
if (dwError != NOERROR) goto __default;
[[fallthrough]];
case NOERROR:
DbgPrint("NumberOfBytesRead=%x\n", NumberOfBytesRead);
// use GetOverlappedResult(hFile, &ov, &NumberOfBytesRead, TRUE) here also possible
break;
__default:
default:
DbgPrint("dwError = %u\n", dwError);
}
CloseHandle(hFile);
}
@eryksun - да, конечно Offset и OffsetHigh должны быть явно установлены, если файл поддерживает позицию файла (файловая система). скажем, смещение труб не имеет значения. но я не акцентировал на этом внимание. вопрос был о другом
Является ли NumberOfBytesRead действительным, если GetOverlappedResult возвращает FALSE. Скажем, частичное чтение до возникновения ошибки или неопределенного значения?
@user3161924 user3161924 — если GetOverlappedResult возвращает FALSE — NumberOfBytesRead недействителен. Количество байтов (чтение/запись) допустимо только после успешного завершения операции. если ошибка ввода-вывода - Количество байтов недействителен, а фактическое значение равно 0
«не должен указывает на местоположение ...», я думаю, легко ввести в заблуждение. Лучше, ИМО, было бы "не обязательно указывать на местоположение...". Не должно создаваться впечатление, что он не может/не должен "указывать на местоположение..."
@nyholku - да, согласен. я просто плохо знаю английский
вы также можете вызвать
GetOverlappedResult, когда запрос ввода-вывода (чтение/запись) возвращает значение true. msdn неправильный, как обычно. в любом случае, если запрос не завершится ошибкой - количество байт будет вOVERLAPPED.InternalHigh. Параметр lpNumberOfBytesWritten должен иметь значение NULL. - это тоже абсолютно неправильно. вы можете передать здесь не 0 и использовать его значение, если ввод-вывод возвращает true