Correct way of accessing results for async ReadFile() that completed synchronously

ReadFile() documentation [https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx] says that even when called on a handle that was opened with FILE_FLAG_OVERLAPPED, the read can happen synchronously.

So, for reads that have been requested as async, the following possibilities arise :

  1. ReadFile() returns false and
    a) GetLastError() returns ERROR_HANDLE_EOF - Read completed synchronously
    b) GetLastError() returns ERROR_IO_PENDING - Asynch IO has been submitted - GetOverlappedResult() should be called later and that can also indicate an EOF condition
    c) GetLastError() returns some other error code - Read failed
  2. ReadFile() returns true - Read completed synchronously. The requested bytes have been read - no EOF, no other error could have happened

Now, the ReadFile() documentation says :

lpNumberOfBytesRead [out, optional]
A pointer to the variable that receives the number of bytes read when using a synchronous hFile parameter. ReadFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results.
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
For more information, see the Remarks section.

So, it prohibits from using lpNumberOfBytesRead when requesting asynchronous read. The documentation also has a link to this [https://support.microsoft.com/en-us/help/156932/asynchronous-disk-i-o-appears-as-synchronous-on-windows] that says :


If, on the other hand, an operation is completed immediately, then &NumberOfBytesRead passed into ReadFile is valid for the number of bytes read. In this case, ignore the OVERLAPPED structure passed into ReadFile; do not use it with GetOverlappedResult or WaitForSingleObject.

So, as is usual with MSFT documentation, this is self-contradicting.

Question : For 1(a) and 2, what is the correct way of accessing the number of bytes read.

> So, as is usual with MSFT documentation, this is self-contradicting

Question : For 1(a) and 2, what is the correct way of accessing the number of bytes read.

I would say that(2) is correct…

The above contradiction lies with the fact that a driver may theoretically complete an asynch request straight away and return the actual completion status right on the spot. IIRC. according to Doron and/or other MSFT guys, the internal “convention” at MSFT is to return STATUS_PENDING regardless of what IoCallDriver() does. Once IoCallDriver() caller (I think in this particular case it is going to be someone in IoReadFile()'s path) is always able to go the above mentioned way and tp present synch IO completion as an asynch one, option (2) seems to be the only possible one in case of asynch read, no matter how underlying driver/filesystem actually handles the request…

Anton Bassov

> I would say that(2) is correct.

I mean option (1), of course…

Anton Bassov

xxxxx@gmail.com wrote:

ReadFile() documentation [https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx] says that even when called on a handle that was opened with FILE_FLAG_OVERLAPPED, the read can happen synchronously.

Now, the ReadFile() documentation says :

lpNumberOfBytesRead [out, optional]
A pointer to the variable that receives the number of bytes read when using a synchronous hFile parameter. ReadFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results.
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
For more information, see the Remarks section.

So, it prohibits from using lpNumberOfBytesRead when requesting asynchronous read.

No, it doesn’t.  “Avoid” is not a prohibition.  It is a recommendation. 
It probably should be a warning that the value it returns is not
meaningful when you get ERROR_IO_PENDING.

When I’ve done overlapped I/O, I have always written the code to handle
three cases:

  • ReadFile returns non-zero, treat it just like a synchronous read, with
    lpNumberOfBytesRead
  • ReadFile returns zero, GetLastError returns ERROR_IO_PENDING, go wait
    for GetOverlappedResult
  • ReadFile returns zero, GetLastError returns something else, this was
    an immediate error


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

> IIRC. according to Doron and/or other MSFT guys, the internal “convention” at MSFT is to return STATUS_PENDING regardless of what IoCallDriver() does.

It’s only what a KMDF driver does, by the framework design. Some usermode code (the infamous MSCOMM32.OCX) can’t handle that, by the way. When they expect a synchronous completion on an overlapped handle, they fail.

>> IIRC. according to Doron and/or other MSFT guys, the internal “convention”

> at MSFT is to return STATUS_PENDING regardless of what IoCallDriver() does.

It’s only what a KMDF driver does, by the framework design.

IIRC, the first time I heard it somewhere in the year 2006, i.e at the time when practically all drivers were written in WDM ( in fact, I am not sure that KMDF even existed back then). To be honest, I am not particularly surprised by the fact KMDF does things in accordance with MSFT’s internal convention anyway. However, it does not necessarily imply that this is the only KM component that takes this approach, does it…

Some usermode code (the infamous MSCOMM32.OCX) can’t handle that, by the way.
When they expect a synchronous completion on an overlapped handle, they fail.

I am, again, not really surprised by this,taking into consideration that with the above mentioned approach any IO operation on overlapped handle gets treated as an asynch IO - even if gets completed immediately by underlying driver. In fact, this is exactly the scenario that MSDN excerpt (1) provided by the OP warns about…

Anton Bassov

Thanks everyone for the insights

As Tim stated, I just need to handle 3 cases. What I put in the opening question as 1(a) and 1(c) are not distinct cases that need to be handled separately.

Also did a small experiment and found that when the async request completes synchronously, then both lpNumberOfBytesRead as well as GetOverlappedResult() give the same details. Tested this on Windows Server 2008 R2 and 2012 R2. Will not be taking it for granted though

xxxxx@gmail.com wrote:

As Tim stated, I just need to handle 3 cases. What I put in the opening question as 1(a) and 1(c) are not distinct cases that need to be handled separately.

Also did a small experiment and found that when the async request completes synchronously, then both lpNumberOfBytesRead as well as GetOverlappedResult() give the same details. Tested this on Windows Server 2008 R2 and 2012 R2. Will not be taking it for granted though

Just to be clear, in that case you can rely on lpNumberOfBytesRead, but
not GetOverlappedResult.  The documentation for GetOverlappedResult
states that its results are only meaningful following an I/O operation
that returned ERROR_IO_PENDING.  As you say, experience suggests that it
does work just fine after a synchronous return, but that’s not the
“contract”.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.