[WIN API]为什么共享同一个WriteFile(sync)和ReadFile(sync)的HANDLE导致ReadFile错误?

时间:2017-07-15 15:13:32

标签: c++ winapi readfile createfile writefile

我搜索了MSDN,但没有找到任何关于与WriteFile和ReadFile共享同一个HANDLE的信息。注意:我没有使用create_always标志,因此文件不可能被替换为空文件。 我尝试使用相同HANDLE的原因是基于性能问题。我的代码基本上下载了一些数据(写入文件),立即读取然后删除它。 在我看来,文件HANDLE只是一个内存地址,也是进行I / O工作的入口。 这就是错误发生的方式:

CreateFile(OK) - > WriteFile(OK) - > GetFileSize(OK) - > ReadFile(失败) - > CloseHandle的(OK)

如果WriteFile被调用synchronized,则此ReadFile操作应该没有问题,即使WriteFile之后的GetFileSize返回正确的值!!(新修改的文​​件大小),但事实是,ReadFile在修改之前读取值( lpNumberOfBytesRead始终是旧值)。我想到了一个想法,缓存!

然后我试着了解更多关于我不了解的Windows File Caching。我甚至试过Flag FILE_FLAG_NO_BUFFERINGFlushFileBuffers功能,但没有运气。当然我知道我可以在WriteFile和ReadFile之间再次执行CloseHandle和CreateFile,我只是想知道是否有一些可能的方法来实现这一点而不再调用CreateFile?

以上是关于我的问题的最小值,下面是我为此概念制作的演示代码:

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;
}

然后输出是:

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

nRead应为5而不是0。

3 个答案:

答案 0 :(得分:3)

Win32文件有一个文件指针,用于读写;在WriteFile之后,它位于文件末尾,因此如果您尝试从中读取它将失败。要阅读您刚才写的内容,您必须使用SetFilePointer函数将文件指针重新定位在文件的开头。

此外,不需要FlushFileBuffer - 操作系统确保文件句柄上的读取和写入看到相同的状态,而不管缓冲区的状态如何。

答案 1 :(得分:1)

首先在文件结束时写入文件光标点。没有什么可读的。您可以使用SetFilePointer

将其回退到开头
::DWORD const result(::SetFilePointer(hFile, 0, nullptr, FILE_BEGIN));
if(INVALID_SET_FILE_POINTER == result)
{
    ::DWORD const last_error(::GetLastError());
    if(NO_ERROR != last_error)
    {
        // TODO do error handling...
    }
}

答案 2 :(得分:1)

当你尝试阅读文件时 - 你尝试从哪个位置阅读它?

FILE_OBJECT维持&#34;当前&#34; position( CurrentByteOffset 成员),当您读取或写入文件时,它可用作默认位置(仅适用于同步文件的 - 在没有FILE_FLAG_OVERLAPPED !! 的情况下打开)。并且在每次读取或写入n个字节后更新此位置(向前移动n个字节)。

最佳解决方案始终在ReadFile(或WriteFile)中使用显式文件偏移量。最后一个参数OVERLAPPED lpOverlapped中的此偏移量 - 查找偏移[高] 成员 - 读取操作从OVERLAPPED结构中指定的偏移量开始

使用此更有效,只需比较使用特殊的api调用SetFilePointer来调整FILE_OBJECT中的 CurrentByteOffset 成员(这不适用于异步文件句柄(使用{{创建) 1}}标志)

尽管有很常见的混淆 - FILE_FLAG_OVERLAPPED仅用于异步io - 这只是OVERLAPPED(或ReadFile)的附加参数,可以随时使用 - 对于任何文件句柄< / p>