user3161924
user3161924

Reputation: 2315

ReadFile and WriteFile with Overlapped IO result if not ERROR_IO_PENDING?

The docs for FILE_FLAG_OVERLAPPED on WriteFile() say you must provide OVERLAP and recommend NULL for lpNumberOfBytesWritten because value is misleading. However docs for GetOverlappedResult() say to only call if WriteFile() returned FALSE with ERROR_IO_PENDING. So that leaves the case where ReadFile() / WriteFile() complete in the API call itself. How are you supposed to get the number of bytes read/written? Do you presume it's the number requested? But WriteFile() says "When writing to a non-blocking, byte-mode pipe handle with insufficient buffer space, WriteFile returns TRUE with * lpNumberOfBytesWritten < nNumberOfBytesToWrite".

TIA!!

Upvotes: 1

Views: 7958

Answers (1)

RbMm
RbMm

Reputation: 33744

If hFile was opened with FILE_FLAG_OVERLAPPED The lpNumberOfBytesWritten parameter should be set to NULL.

this is not true (mistake or lie). the lpNumberOfBytesWritten can be set to NULL, but not should. if I/O request complete synchronous with success in *lpNumberOfBytesWritten will be valid number of bytes.

note also that lpNumberOfBytes must not point to location which will be valid until operation is complete (like lpOverlapped) - it can point to local variable in function for example and you can exit from function before I/O is complete - and this is will be ok too. system simply copy InternalHigh from OVERLAPPED to *lpNumberOfBytes. so in pseudo code :

if (lpNumberOfBytes) *lpNumberOfBytes = (ULONG)lpOverlapped->InternalHigh;

obvivous correct value in *lpNumberOfBytes will be only if I/O already completed with success. so and can use it only in this case. system doesn't remember value of lpNumberOfBytes - because this it must be valid only during [Write|Read]File call but not during I/O active (which can be longer in case asynchronous I/O)

GetOverlappedResult we can call if I/O request complete synchronous with success (in case ReadFile or WriteFile, if thay return TRUE) of if pending returned. this api can not be called only in case I/O request just fail (ReadFile or WriteFile return FALSE and GetLastError() != ERROR_IO_PENDING)

so the best always pass not 0 lpNumberOfBytes to api and use it, if api complete just with success. otherwise use GetOverlappedResult or if say you use BindIoCompletionCallback - you direct got dwNumberOfBytesTransfered in callback.

so in conceptually can use next code:

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);
}

Upvotes: 3

Related Questions