[WIN API]为什么共享相同的 WriteFile(sync) 和 ReadFile(sync) 句柄会导致 Rea



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

创建文件(正常( --> 写入文件(正常( --> 获取文件大小(正常( -->读取文件(失败( --> 关闭句柄(正常(

如果写入文件被调用为同步,则此读取文件操作应该没有问题,即使写入文件后的 GetFileSize 返回正确的值!!(新修改的文件大小(,但事实是,ReadFile 在修改之前读取值(lpNumberOfBytesRead 始终是旧值(。我脑海中突然浮现出一个念头,缓存!

然后我试图了解更多关于我不了解的Windows文件缓存。我什至尝试了标志FILE_FLAG_NO_BUFFERING和FlushFileBuffers功能,但没有运气。当然,我知道我可以在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。

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

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

第一次写入文件后,光标指向文件末尾。没有什么可读的。您可以使用 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...
}
}

当你尝试读取文件时 - 你尝试从什么位置读取它?

FILE_OBJECT保持"当前"位置(CurrentByteOffset成员(,当您读取或写入文件时,该位置可用作默认位置(仅适用于同步文件 - 打开时没有FILE_FLAG_OVERLAPPED!!并且此位置在每次读取或写入 n 个字节后更新(向前移动 n 个字节(。

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

使用这个更有效,简单地比较使用特殊的 API 调用SetFilePointer来调整CurrentByteOffset成员FILE_OBJECT(这不适用于异步文件句柄(使用FILE_FLAG_OVERLAPPED标志创建(

尽管存在非常常见的混淆 -OVERLAPPED仅用于异步IO - 这只是ReadFile(或WriteFile(的附加参数,并且可以随时使用 - 用于任何文件句柄

最新更新