Testing for the End of a File Program Example
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++ code snippet 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/C++ example shows how to test for the end of file during an asynchronous read operation.
Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.
Then, add the source file and give it a suitable name.
Next, add the following source code.
#include <windows.h>
#include <stdio.h>
#define BUF_SIZE 100
// Routine Description: Retrieve the system error message for the last-error code
LPCTSTR ErrorMessage(DWORD error)
{
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);
}
// Routine Description: Placeholder to demo when async I/O
// might want to do other processing.
void GoDoSomethingElse(void)
{
wprintf(LInside GoDoSomethingElse()...\n);
}
// 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.
DWORD AsyncTestForEnd(HANDLE hEvent, HANDLE hFile)
{
WCHAR 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.
wprintf(LAsynchronous read attempt...\n);
bResult = ReadFile(hFile,inBuffer,nBytesToRead,&dwBytesRead,&stOverlapped);
dwError = GetLastError();
// Check for a problem or pending operation.
if (!bResult)
{
// If the function fails, or is completing asynchronously
// the return value is zero (FALSE)
wprintf(LReadFile() returns %u. Failed or asynchronous completion...\n, bResult);
switch (dwError)
{
case ERROR_HANDLE_EOF:
{
// Another check
wprintf(L\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.
wprintf(L\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
wprintf(LGetOverlappedResult() found EOF\n);
break;
}
case ERROR_IO_INCOMPLETE:
{
// Operation is still pending, allow while loop
// to loop again after printing a little progress.
wprintf(LGetOverlappedResult() I/O Incomplete\n);
bPending = TRUE;
bContinue = TRUE;
break;
}
default:
{
// Decode any other errors codes.
errMsg = ErrorMessage(dwError);
wprintf(LGetOverlappedResult() failed, error %d: %s\n, dwError, errMsg);
LocalFree((LPVOID)errMsg);
}
}
}
else
{
wprintf(LGetOverlappedResult() is pretty fine!\n);
wprintf(LReadFile() operation completed\n);
// Manual-reset event should be reset since it is now signaled.
if(ResetEvent(stOverlapped.hEvent) != 0)
wprintf(LResetEvent() is OK!\n);
else
wprintf(LResetEvent() failed! Error %u\n, GetLastError());
}
}
break;
}
default:
{
// Decode any other errors codes.
errMsg = ErrorMessage(dwError);
wprintf(LReadFile() GetLastError() unhandled, error %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.
wprintf(LReadFile() returns %u, completed synchronously\n, bResult);
}
// 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;
}
// 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.
int wmain(int argc, WCHAR *argv[])
{
HANDLE hEvent;
HANDLE hFile;
DWORD dwReturnValue;
DWORD dwError;
LPCTSTR errMsg;
if( argc != 2 )
{
wprintf(LNo arguments supplied!\n);
wprintf(LUsage: %s <file_name_to_be_read>\n, argv[0]);
wprintf(LExample: %s C:\\mytestfile.txt\n, argv[0]);
wprintf(L The file must exist\n);
return 1;
}
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)
{
dwError = GetLastError();
errMsg = ErrorMessage(dwError);
wprintf(LCould not open file, error %d: %s\n, dwError, errMsg);
LocalFree((LPVOID)errMsg);
return 1;
}
else
wprintf(LCreateFile() is pretty fine!\n);
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (hEvent == NULL)
{
dwError = GetLastError();
errMsg = ErrorMessage(dwError);
wprintf(LCould not create event: %d %s\n, dwError, errMsg);
LocalFree((LPVOID)errMsg);
return 1;
}
else
wprintf(LCreateEvent() is working!\n);
dwReturnValue = AsyncTestForEnd(hEvent, hFile);
wprintf(LRead complete. Bytes read: %d\n, dwReturnValue);
if(CloseHandle(hFile) != 0)
wprintf(LhFile handle was closed!\n);
else
wprintf(LFailed to close hFile handle!\n);
if(CloseHandle(hEvent) != 0)
wprintf(LhEvent handle was closed!\n);
else
wprintf(LFailed to close hEvent handle!\n);
return 0;
}
To test this program, firstly, create an empty text file on C drive. In this case the file name used is mytesttextfile.txt.

Build and run the project with the empty file name as the argument. The following Figure shows the sample output.

Then, put some text in the text file and save it. Re-run the project. The following screenshot shows the sample output.
