[WIN API]为什么共享 WriteFilesync) 和 ReadFilesync) 的同一个 HANDLE 会导致 ReadFile 错误?
[WIN API]Why sharing a same HANDLE of WriteFile(sync) and ReadFile(sync) cause ReadFile error?
我搜索了 MSDN,但没有找到任何关于与 WriteFile 和 ReadFile 共享同一个 HANDLE 的信息。 NOTE:I 没有使用 create_always
标志,所以文件不可能被空文件替换。
我尝试使用相同 HANDLE 的原因是基于性能问题。我的代码基本上下载一些数据(写入文件),立即读取然后删除它。
在我看来,文件HANDLE只是一个内存地址,也是执行I/O作业的入口。
错误是这样发生的:
CreateFile(成功)--> WriteFile(成功)--> GetFileSize(成功)--> ReadFile(失败)--> CloseHandle(成功)
如果WriteFile被调用synchronized,这个ReadFile动作应该没有问题,即使是WriteFile之后的GetFileSize returns正确的值!!(新修改的文件大小),但事实是,ReadFile读取修改前的值(lpNumberOfBytesRead 始终是旧值)。我突然想到一个想法,缓存!
然后我尝试了解更多关于 Windows File Caching which I have no knowledge with. I even tried Flag FILE_FLAG_NO_BUFFERING
, and 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。
第一次写入文件后光标指向文件末尾。没有什么可读的。您可以使用 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...
}
}
Win32 文件只有一个文件指针,既可读又可写;在 WriteFile
之后它位于文件末尾,因此如果您尝试从中读取它将会失败。要阅读您刚刚编写的内容,您必须使用 SetFilePointer
函数将文件指针重新定位到文件的开头。
此外,不需要 FlushFileBuffer
- 操作系统确保文件句柄上的读取和写入看到相同的状态,而不管缓冲区的状态如何。
当您尝试读取文件时 - 您尝试从什么位置读取文件?
FILE_OBJECT
维护 "current" 位置(CurrentByteOffset 成员)可以用作默认位置( 仅用于同步文件 - 打开没有 FILE_FLAG_OVERLAPPED
!!) 读取或写入文件时。并且在每次读取或写入 n 个字节后更新此位置(向前移动 n 个字节)。
最佳解决方案始终在 ReadFile(或 WriteFile)中使用显式文件偏移量。最后一个参数中的此偏移量 OVERLAPPED lpOverlapped
- 查找 Offset[High] 成员 - 读取操作从 OVERLAPPED
结构中指定的偏移量开始
使用这个更有效,只需比较使用特殊的 api 调用 SetFilePointer
调整 FILE_OBJECT
中的 CurrentByteOffset 成员(这不起作用对于异步文件句柄(使用 FILE_FLAG_OVERLAPPED
标志创建)
尽管很常见的混淆 - OVERLAPPED
不仅仅用于异步 io - 这只是 ReadFile
(或 WriteFile
)的附加参数并且可以始终使用 - 用于任何文件手柄
我搜索了 MSDN,但没有找到任何关于与 WriteFile 和 ReadFile 共享同一个 HANDLE 的信息。 NOTE:I 没有使用 create_always
标志,所以文件不可能被空文件替换。
我尝试使用相同 HANDLE 的原因是基于性能问题。我的代码基本上下载一些数据(写入文件),立即读取然后删除它。
在我看来,文件HANDLE只是一个内存地址,也是执行I/O作业的入口。
错误是这样发生的:
CreateFile(成功)--> WriteFile(成功)--> GetFileSize(成功)--> ReadFile(失败)--> CloseHandle(成功)
如果WriteFile被调用synchronized,这个ReadFile动作应该没有问题,即使是WriteFile之后的GetFileSize returns正确的值!!(新修改的文件大小),但事实是,ReadFile读取修改前的值(lpNumberOfBytesRead 始终是旧值)。我突然想到一个想法,缓存!
然后我尝试了解更多关于 Windows File Caching which I have no knowledge with. I even tried Flag FILE_FLAG_NO_BUFFERING
, and 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。
第一次写入文件后光标指向文件末尾。没有什么可读的。您可以使用 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...
}
}
Win32 文件只有一个文件指针,既可读又可写;在 WriteFile
之后它位于文件末尾,因此如果您尝试从中读取它将会失败。要阅读您刚刚编写的内容,您必须使用 SetFilePointer
函数将文件指针重新定位到文件的开头。
此外,不需要 FlushFileBuffer
- 操作系统确保文件句柄上的读取和写入看到相同的状态,而不管缓冲区的状态如何。
当您尝试读取文件时 - 您尝试从什么位置读取文件?
FILE_OBJECT
维护 "current" 位置(CurrentByteOffset 成员)可以用作默认位置( 仅用于同步文件 - 打开没有 FILE_FLAG_OVERLAPPED
!!) 读取或写入文件时。并且在每次读取或写入 n 个字节后更新此位置(向前移动 n 个字节)。
最佳解决方案始终在 ReadFile(或 WriteFile)中使用显式文件偏移量。最后一个参数中的此偏移量 OVERLAPPED lpOverlapped
- 查找 Offset[High] 成员 - 读取操作从 OVERLAPPED
结构中指定的偏移量开始
使用这个更有效,只需比较使用特殊的 api 调用 SetFilePointer
调整 FILE_OBJECT
中的 CurrentByteOffset 成员(这不起作用对于异步文件句柄(使用 FILE_FLAG_OVERLAPPED
标志创建)
尽管很常见的混淆 - OVERLAPPED
不仅仅用于异步 io - 这只是 ReadFile
(或 WriteFile
)的附加参数并且可以始终使用 - 用于任何文件手柄