根据MSDN ReadFile()Win32函数可能会错误地报告读取操作完成。什么时候?

时间:2010-03-18 19:35:21

标签: winapi io synchronous readfile overlapped-io

MSDN在ReadFile()函数的描述中声明:

  

如果使用FILE_FLAG_OVERLAPPED打开hFile,则lpOverlapped参数必须指向有效且唯一的OVERLAPPED结构,否则该函数可能会错误地报告读取操作已完成。

我有一些违反上述建议的应用程序,我想知道问题的严重性。我的意思是该程序使用已使用FILE_FLAG_OVERLAPPED创建的命名管道,但它使用以下调用从中读取:

ReadFile(handle, &buf, n, &n_read, NULL);

这意味着它将NULL作为lpOverlapped参数传递。根据文档,在某些情况下,该调用不应该正常工作。我花了很多时间试图重现这个问题,但我无法做到!我总是在正确的时间将所有数据放在正确的位置。我虽然只测试了命名管道。

有人知道我什么时候可以预期ReadFile()会错误地返回并报告成功完成,即使数据还没有在缓冲区中?为了重现这个问题会发生什么?是否会出现文件,管道,套接字,控制台或其他设备?我是否必须使用特定版本的操作系统?或者特定版本的读取(比如将句柄注册到I / O完成端口)?或者是读写进程/线程的特定同步?

或者什么时候会失败?它对我有用:/

请帮忙!

关于,马丁

4 个答案:

答案 0 :(得分:3)

在系统内部,系统仅支持异步I / O.对于同步I / O,系统使用OVERLAPPED创建临时hEvent = NULL;结构,发出传递此临时的异步I / O请求,然后使用GetOverlappedResult( bWait = TRUE )等待完成。

回想一下,临时hEvent结构的OVERLAPPEDNULL并注意GetOverlappedResult的备注部分:

  

如果OVERLAPPED结构的hEvent成员为NULL,则系统使用hFile句柄的状态来指示操作何时完成。

文件HANDLE是一个可等待的对象,在I / O操作开始时变为无信号,并在I / O操作结束时发出信号。

现在考虑一种情况,即在发出同步I / O请求时异步文件HANDLE具有挂起的I / O请求。系统创建OVERLAPPED结构并等待hFile HANDLE完成。与此同时,异步I / O完成,从而发出HANDLE信号,导致同步I / O过早返回,而没有实际完成。

更糟糕的是,当响应同步I / O请求而启动的异步I / O完成时,它将更新不再存在的临时OVERLAPPED结构。结果是内存损坏。

完整的故事可以在The Old New Thing找到。

答案 1 :(得分:0)

似乎您处于违反记录的最佳做法而故意调用API的情况。在这种情况下,所有赌注都是关闭的。它可能有效,但可能没有。如果可以在此OS上运行,但不能在OS的下一次迭代或同一OS的下一个Service Pack上运行。当你移植到Win64时会发生什么?它还能继续吗?

调用GetLastError()(或在调试器中查看@ ERR,hr)是否提供除错误代码之外有用的任何值?

我建议您使用有效的OVERLAPPED结构调用它,使其工作并消除所有疑问(以及随机失败的可能性)。当您可以通过使用有效的OVERLAPPED结构轻松解决问题时,为什么软件中可能存在错误的代码(并且很难重现错误)?

答案 2 :(得分:0)

为什么要问问题而不是修改代码来按原样调用API?

我怀疑它似乎总是有效,因为即使这是一个异步I / O,它也会很快完成。根据您测试成功的方式,该功能可能会错误地报告操作已完成,但在测试结果之前它实际上已完成。

真正的测试是在读取数据之前对管道进行读取。

但实际上,你应该修改代码。如果您的体系结构无法处理异步I / O,则从创建命名管道中删除FILE_FLAG_OVERLAPPED

答案 3 :(得分:0)

当他们说

  

块引用   如果使用FILE_FLAG_OVERLAPPED打开hFile,则lpOverlapped参数必须指向有效且唯一的OVERLAPPED结构,否则该函数可能会错误地报告读取操作已完成。

他们的意思是代码中没有任何东西阻止它工作,但是他们的代码中还有一条路径会产生错误的结果。仅仅因为你不能用你的特定硬件重现问题并不意味着没有问题。

如果你真的想重现这个问题,请保持原样,继续你的生活。关于你忘记这个问题的所有时间,奇怪的行为将表明与调用ReadFile没有任何明显的关系。你会花几天时间把头发拉出来,这个问题似乎会随机出现。最终你会找到它并因为没有遵循指示而踢自己。在那里,做到了,没有乐趣!

重新创建问题的另一种方法是为您的客户安排重要的演示。那肯定会失败!

如果您不想使用OVERLAPPED结构和所有相关的返回值检查(Waits,Events等)来破坏您的代码,您可以编写一个包装函数,该函数包含一个可以读取的句柄和一个超时。只需用这个方便的包装器替换你对ReadFile的调用。