c++winapireadfile

Overlapped Synchronous ReadFile call to read 1000 bytes of a 88 byte file does not trigger ERROR_HANDLE_EOF


The contents of a text file named existingfile.txt is as follows

The First Order Derivative of X2 is
2x. The derivative of sin is cos.
Math is the best

The code below is from a file named main.cpp which is in the same directory as existingfile.txt The example shows the process of

#include <windows.h>
#include <cstdio>

int main(int, char**){
//Creating Synchronous File Handle
    HANDLE hFile = CreateFileW(
        L"existingfile.txt",
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_ALWAYS,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL
    );
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("ERROR IN OPENING FILE %lu\n", GetLastError());
        return -1;
    }
    //Obtaining File Size
    {
        ULARGE_INTEGER FileSize;
        FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart);
        DWORD LastError = GetLastError();
        if (FileSize.LowPart == INVALID_FILE_SIZE && LastError != NO_ERROR){
            printf("FAILED TO OBTAIN FILE SIZE, ERROR %lu\n", LastError);
            return -1;
        }
        printf("FILE SIZE: %llu\n", FileSize.QuadPart);
    }
    //Reading To End Of the File
    {
        OVERLAPPED Overlapped = {};
        Overlapped.Offset = 15;
        Overlapped.OffsetHigh = 0;
        constexpr DWORD NumBytesToRead = 1000;
        char BytesRead[NumBytesToRead + 1] = {0};
        BOOL Successful = ReadFile(hFile, BytesRead, NumBytesToRead, NULL, &Overlapped);
        DWORD LastError = GetLastError();
        if (!Successful){
            if (LastError != ERROR_HANDLE_EOF){
                printf("ERROR IN OVERLAPPED SYNCHRONOUS READ TO EOF, ERROR %lu\n", LastError);
                return -1;
            }
            puts("EOF ENCOUNTERED IN OVERLAPPED SYNCHRONOUS READ TO EOF");
        }
        BytesRead[NumBytesToRead] = 0;
        printf("2: Information Read: '%s'\n", BytesRead); 
    }
    CloseHandle(hFile);
    return 0;
}

The output i get is

FILE SIZE: 88
2: Information Read: ' Derivative of X2 is
2x. The derivative of sin is cos.
Math is the best'

The output I expected was

FILE SIZE: 88
EOF ENCOUNTERED IN OVERLAPPED SYNCHRONOUS READ TO EOF
2: Information Read: ' Derivative of X2 is
2x. The derivative of sin is cos.
Math is the best'

because it is stated in the documentation of ReadFile

Considerations for working with synchronous file handles:

...

If lpOverlapped is not NULL, then when a synchronous read operation reaches the end of a file, ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF.

I would most grateful if someone could explain/find the cause of why the expected behaviour as mentioned in the documentation is not happening.


Solution

  • ReadFile() can read fewer bytes than requested. You need to use the lpNumberOfBytesRead parameter to know how many bytes were actually read. But your code is ignoring that parameter.

    The call does not fail in your example since there are (73) bytes available to read. If at least 1 byte is read, then EOF is not reported yet. It would not make sense to report an error on a successful read, otherwise the app might decide not to process the bytes it has received.

    So, you won't get ERROR_HANDLE_EOF until you try to read past the EOF (ie when Overlapped.Offset >= 88), which your code is not doing.

    Try this instead:

    #include <windows.h>
    #include <cstdio>
    
    int main(){
        //Creating Synchronous File Handle
        HANDLE hFile = CreateFileW(
            L"existingfile.txt",
            GENERIC_READ,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL,
            OPEN_ALWAYS,
            FILE_FLAG_SEQUENTIAL_SCAN,
            NULL
        );
        if (hFile == INVALID_HANDLE_VALUE) {
            printf("ERROR IN OPENING FILE: %lu\n", GetLastError());
            return -1;
        }
        //Obtaining File Size
        {
            ULARGE_INTEGER FileSize;
            FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart);
            DWORD LastError = GetLastError();
            if (FileSize.LowPart == INVALID_FILE_SIZE && LastError != NO_ERROR){
                printf("ERROR OBTAINING FILE SIZE: %lu\n", LastError);
                return -1;
            }
            printf("FILE SIZE: %llu\n", FileSize.QuadPart);
        }
        //Reading To End Of the File
        {
            OVERLAPPED Overlapped = {};
            Overlapped.Offset = 15;
            Overlapped.OffsetHigh = 0;
            constexpr DWORD NumBytesToRead = 1000;
            char BytesRead[NumBytesToRead + 1] = {0};
            DWORD NumBytesActuallyRead;
            BOOL Successful = ReadFile(hFile, BytesRead, NumBytesToRead, &NumBytesActuallyRead, &Overlapped);
            if (!Successful){
                DWORD LastError = GetLastError();
                if (LastError != ERROR_HANDLE_EOF){
                    printf("ERROR IN OVERLAPPED SYNCHRONOUS READ: %lu\n", LastError);
                } else {
                    puts("EOF ENCOUNTERED IN OVERLAPPED SYNCHRONOUS READ");
                }
                return -1;
            }
            printf("BYTES READ: %lu\n", NumBytesActuallyRead);
            printf("2: Information Read: '%.*s'\n", (int)NumBytesActuallyRead, BytesRead); 
        }
        CloseHandle(hFile);
        return 0;
    }