Search code examples
c++winapireadfilecreatefilewritefile

[WIN API]Why sharing a same HANDLE of WriteFile(sync) and ReadFile(sync) cause ReadFile error?


I've search the MSDN but did not find any information about sharing a same HANDLE with both WriteFile and ReadFile. NOTE:I did not use create_always flag, so there's no chance for the file being replaced with null file. The reason I tried to use the same HANDLE was based on performance concerns. My code basically downloads some data(writes to a file) ,reads it immediately then delete it. In my opinion, A file HANDLE is just an address of memory which is also an entrance to do a I/O job. This is how the error occurs:

CreateFile(OK) --> WriteFile(OK) --> GetFileSize(OK) --> ReadFile(Failed) --> CloseHandle(OK)

If the WriteFile was called synchronized, there should be no problem on this ReadFile action, even the GetFileSize after WriteFile returns the correct value!!(new modified file size), but the fact is, ReadFile reads the value before modified (lpNumberOfBytesRead is always old value). A thought just came to my mind,caching!

Then I tried to learn more about Windows File Caching which I have no knowledge with. I even tried Flag FILE_FLAG_NO_BUFFERING, and FlushFileBuffers function but no luck. Of course I know I can do CloseHandle and CreateFile again between WriteFile and ReadFile, I just wonder if there's some possible way to achieve this without calling CreateFile again?

Above is the minimum about my question, down is the demo code I made for this concept:

int main()
{

    HANDLE hFile = CreateFile(L"C://temp//TEST.txt", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL| FILE_FLAG_WRITE_THROUGH, NULL);

    //step one write 12345 to file
    std::string test = "12345";
    char * pszOutBuffer;
    pszOutBuffer = (char*)malloc(strlen(test.c_str()) + 1); //create buffer for 12345 plus a null ternimator
    ZeroMemory(pszOutBuffer, strlen(test.c_str()) + 1); //replace null ternimator with 0
    memcpy(pszOutBuffer, test.c_str(), strlen(test.c_str())); //copy 12345 to buffer



    DWORD wmWritten;
    WriteFile(hFile, pszOutBuffer, strlen(test.c_str()), &wmWritten, NULL); //write 12345 to file

    //according to msdn this refresh the buffer
    FlushFileBuffers(hFile);

    std::cout << "bytes writen to file(num):"<< wmWritten << std::endl; //got output 5 here as expected, 5 bytes has bebn wrtten to file.

    //step two getfilesize and read file

    //get file size of C://temp//TEST.txt
    DWORD dwFileSize = 0;
    dwFileSize = GetFileSize(hFile, NULL);
    if (dwFileSize == INVALID_FILE_SIZE)
    {
        return -1; //unable to get filesize
    }
    std::cout << "GetFileSize result is:" << dwFileSize << std::endl; //got output 5 here as expected

    char * bufFstream;

    bufFstream = (char*)malloc(sizeof(char)*(dwFileSize + 1)); //create buffer with filesize & a null terminator
    memset(bufFstream, 0, sizeof(char)*(dwFileSize + 1));
    std::cout << "created a buffer for ReadFile with size:" << dwFileSize + 1 << std::endl; //got output 6 as expected here
    if (bufFstream == NULL) {
        return -1;//ERROR_MEMORY;
    }
    DWORD nRead = 0;
    bool bBufResult = ReadFile(hFile, bufFstream, dwFileSize, &nRead, NULL); //dwFileSize is 5 here

    if (!bBufResult) {
        free(bufFstream);
        return -1; //copy file into buffer failed
    }


    std::cout << "nRead is:" << nRead << std::endl; //!!!got nRead 0 here!!!? why?


    CloseHandle(hFile);
    free(pszOutBuffer);
    free(bufFstream);
    return 0;
}

then the output is:

bytes writen to file(num):5
GetFileSize result is:5
created a buffer for ReadFile with size:6
nRead is:0

nRead should be 5 not 0.


Solution

  • Win32 files have a single file pointer, both for read and write; after the WriteFile it is at the end of the file, so if you try to read from it it will fail. To read what you just wrote you have to reposition the file pointer at the start of the file, using the SetFilePointer function.

    Also, the FlushFileBuffer isn't needed - the operating system ensures that reads and writes on the file handle see the same state, regardless of the status of the buffers.