WinINet Notes

InternetQueryDataAvailable / InternetReadFile call pairing: Details on Asynchronous Mode Behavior


Internet Explorer use these two functions repeatedly to read from Internet stream: he checks for availability of data bytes and then reads the data using InternetReadFile API in one or more partial reads, depending on it's desired buffer size (typically it will read up to 2K per call depending on context).

However, there are a few details we've discovered using Deviare attached to the WININET functions, specially when IE is requesting data in Asynchronous mode. This mode is established using InternetOpen with the INTERNET_ASYNC flag specified. Subsequent operations with the InternetOpen-returned-handle will operate asynchronously.

First, InternetQueryDataAvailable won't block in this mode, contrary to the MSDN documentation own words. It will return TRUE and the available number of bytes ONLY if the HTTP request has been completed; otherwise (and this also is not present in MS docs) it will return code 997 (ERROR_IO_PENDING) stating that you can't actually know how much bytes are ready for a next InternetReadFile call. As a side note, surprisingly even when the function fails with I/O pending error, it may not actually return zero bytes as available. 

A first approach could be to wait sleeping until it succeeds. It will eventually succeed but unfortunately, this is not reliable as the returned number of bytes won't match the total available. The proper method is to use the callback to wait for the request to complete and use the provided status data to get the correct number of bytes.   In resume, InternetQueryDataAvailable won't return reliable data in asynchronous mode through the lpdwBytesAvail parameter once it enters in "pending I/O" state; the callback routine must be used.

So how to get the proper number of bytes with the callback? In the INTERNET_STATUS_REQUEST_COMPLETE, check the lpdvStatusInformation parameter, casting to the INTERNET_ASYNC_RESULT structure. In this structure, you will get the number of bytes in the dwError member, and a boolean flag in dwResult indicating if the call succeeded.