Testing for the End of a File (Windows 的异步 IO)
The ReadFile function checks for the end-of-file condition (EOF) differently for synchronous and asynchronous read operations. When a synchronous read operation gets to the end of a file, ReadFile returns TRUE and sets the variable pointed to by the lpNumberOfBytesRead parameter to zero. An asynchronous read operation can encounter the end of a file during the initiating call to ReadFile or during subsequent asynchronous operations if the file pointer is programmatically advanced beyond the end of the file.
The following C++ example shows how to test for the end of a file during a synchronous read operation.
// Attempt a synchronous read operation.
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL);
// Check for eof.
if (bResult && nBytesRead == 0)
{
// at the end of the file
}
The test for end-of-file during an asynchronous read operation is slightly more involved than for a similar synchronous read operation. The end-of-file indicator for asynchronous read operations is when GetOverlappedResult returns FALSE and GetLastError returns ERROR_HANDLE_EOF.
The following C++ example shows how to test for the end of file during an asynchronous read operation.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#define BUF_SIZE (61)
LPCTSTR ErrorMessage( DWORD error )
// Routine Description:
// Retrieve the system error message for the last-error code
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
return((LPCTSTR)lpMsgBuf);
}
void GoDoSomethingElse(void)
// Routine Description:
// Placeholder to demo when async I/O might want to do
// other processing.
{
printf("Inside GoDoSomethingElse()\n");
}
DWORD AsyncTestForEnd( HANDLE hEvent, HANDLE hFile )
// Routine Description:
// Demonstrate async ReadFile operations that can catch
// End-of-file conditions. Unless the operation completes
// synchronously or the file size happens to be an exact
// multiple of BUF_SIZE, this routine will eventually force
// an EOF condition on any file.
// Parameters:
// hEvent - pre-made manual-reset event.
//
// hFile - pre-opened file handle, overlapped.
//
// inBuffer - the buffer to read in the data to.
//
// nBytesToRead - how much to read (usually the buffer size).
// Return Value:
// Number of bytes read.
{
char inBuffer[BUF_SIZE];
DWORD nBytesToRead = BUF_SIZE;
DWORD dwBytesRead = 0;
DWORD dwFileSize = GetFileSize(hFile, NULL);
OVERLAPPED stOverlapped = {0};
DWORD dwError = 0;
LPCTSTR errMsg = NULL;
BOOL bResult = FALSE;
BOOL bContinue = TRUE;
// Set up overlapped structure event. Other members are already
// initialized to zero.
stOverlapped.hEvent = hEvent;
// This is an intentionally brute-force loop to force the EOF trigger.
// A properly designed loop for this simple file read would use the
// GetFileSize API to regulate execution. However, the purpose here
// is to demonstrate how to trigger the EOF error and handle it.
while(bContinue)
{
// Default to ending the loop.
bContinue = FALSE;
// Attempt an asynchronous read operation.
bResult = ReadFile(hFile,
inBuffer,
nBytesToRead,
&dwBytesRead,
&stOverlapped);
dwError = GetLastError();
// Check for a problem or pending operation.
if (!bResult)
{
switch (dwError)
{
case ERROR_HANDLE_EOF:
{
printf("\nReadFile returned FALSE and EOF condition, async EOF not triggered.\n");
break;
}
case ERROR_IO_PENDING:
{
BOOL bPending=TRUE;
// Loop until the I/O is complete, that is: the overlapped
// event is signaled.
while( bPending )
{
bPending = FALSE;
// Pending asynchronous I/O, do something else
// and re-check overlapped structure.
printf("\nReadFile operation is pending\n");
// Do something else then come back to check.
GoDoSomethingElse();
// Check the result of the asynchronous read
// without waiting (forth parameter FALSE).
bResult = GetOverlappedResult(hFile,
&stOverlapped,
&dwBytesRead,
FALSE) ;
if (!bResult)
{
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// Handle an end of file
printf("GetOverlappedResult found EOF\n");
break;
}
case ERROR_IO_INCOMPLETE:
{
// Operation is still pending, allow while loop
// to loop again after printing a little progress.
printf("GetOverlappedResult I/O Incomplete\n");
bPending = TRUE;
bContinue = TRUE;
break;
}
default:
{
// Decode any other errors codes.
errMsg = ErrorMessage(dwError);
_tprintf(TEXT("GetOverlappedResult failed (%d): %s\n"),
dwError, errMsg);
LocalFree((LPVOID)errMsg);
}
}
}
else
{
printf("ReadFile operation completed\n");
// Manual-reset event should be reset since it is now signaled.
ResetEvent(stOverlapped.hEvent);
}
}
break;
}
default:
{
// Decode any other errors codes.
errMsg = ErrorMessage(dwError);
printf("ReadFile GLE unhandled (%d): %s\n", dwError, errMsg);
LocalFree((LPVOID)errMsg);
break;
}
}
}
else
{
// EOF demo did not trigger for the given file.
// Note that system caching may cause this condition on most files
// after the first read. CreateFile can be called using the
// FILE_FLAG_NOBUFFERING parameter but it would require reads are
// always aligned to the volume's sector boundary. This is beyond
// the scope of this example. See comments in the main() function.
printf("ReadFile completed synchronously\n");
}
// The following operation assumes the file is not extremely large, otherwise
// logic would need to be included to adequately account for very large
// files and manipulate the OffsetHigh member of the OVERLAPPED structure.
stOverlapped.Offset += dwBytesRead;
if ( stOverlapped.Offset < dwFileSize )
bContinue = TRUE;
}
return stOverlapped.Offset;
}
void __cdecl _tmain(int argc, TCHAR *argv[])
// To force an EOF condition, execute this application specifying a
// zero-length file. This is because the offset (file pointer) must be
// at or beyond the end-of-file marker when ReadFile is called. For
// more information, see the comments for the AsyncTestForEnd routine.
{
HANDLE hEvent;
HANDLE hFile;
DWORD dwReturnValue;
printf("\n");
if( argc != 2 )
{
printf("ERROR:\tIncorrect number of arguments\n\n");
printf("%s <file_name>\n", argv[0]);
return;
}
hFile = CreateFile(argv[1], // file to open
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // default security
OPEN_EXISTING, // existing file only
FILE_FLAG_OVERLAPPED, // overlapped operation
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
LPCTSTR errMsg = ErrorMessage(dwError);
printf("Could not open file (%d): %s\n", dwError, errMsg);
LocalFree((LPVOID)errMsg);
return;
}
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hEvent == NULL)
{
DWORD dwError = GetLastError();
LPCTSTR errMsg = ErrorMessage(dwError);
printf("Could not CreateEvent: %d %s\n", dwError, errMsg);
LocalFree((LPVOID)errMsg);
return;
}
dwReturnValue = AsyncTestForEnd(hEvent, hFile);
printf( "\nRead complete. Bytes read: %d\n", dwReturnValue);
CloseHandle(hFile);
CloseHandle(hEvent);
}
The output from this sample code is as follows.
ReadFile operation is pending
Inside GoDoSomethingElse()
GetOverlappedResult I/O Incomplete
ReadFile operation is pending
Inside GoDoSomethingElse()
ReadFile operation completed
Complete. Bytes read: 541