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

发布于 2024-08-25 21:30:22 字数 816 浏览 15 评论 0原文

MSDN 在 ReadFile( ) 函数:

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

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

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

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

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

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

请帮忙!

谨致问候,马丁

The MSDN states in its description of ReadFile() function:

If hFile is opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must point to a valid and unique OVERLAPPED structure, otherwise the function can incorrectly report that the read operation is complete.

I have some applications that are violating the above recommendation and I would like to know the severity of the problem. I mean the program uses named pipe that has been created with FILE_FLAG_OVERLAPPED, but it reads from it using the following call:

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

That means it passes NULL as the lpOverlapped parameter. That call should not work correctly in some circumstances according to documentation. I have spent a lot of time trying to reproduce the problem, but I was unable to! I always got all data in right place at right time. I was testing only Named Pipes though.

Would anybody know when can I expect that ReadFile() will incorrectly return and report successful completion even the data are not yet in the buffer? What would have to happen in order to reproduce the problem? Does it happen with files, pipes, sockets, consoles, or other devices? Do I have to use particular version of OS? Or particular version of reading (like register the handle to I/O completion port)? Or particular synchronization of reading and writing processes/threads?

Or when would that fail? It works for me :/

Please help!

With regards, Martin

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

小草泠泠 2024-09-01 21:30:22

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

回想一下临时 OVERLAPPED 结构的 hEventNULL 并注意 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 结构。结果是内存损坏。

完整的故事可以在 旧的新事物

Internally the system only supports asynchronous I/O. For synchronous I/O the system creates a temporary OVERLAPPED structure with hEvent = NULL;, issues an asynchronous I/O request passing in this temporary, and then waits for completion using GetOverlappedResult( bWait = TRUE ).

Recall that the hEvent of the temporary OVERLAPPED structure is NULL and pay attention to the Remarks section of GetOverlappedResult:

If the hEvent member of the OVERLAPPED structure is NULL, the system uses the state of the hFile handle to signal when the operation has been completed.

A file HANDLE is a waitable object that becomes unsignaled when an I/O operation begins, and signaled when an I/O operation ends.

Now consider a scenario where an asynchronous file HANDLE has a pending I/O request at the time you issue a synchronous I/O request. The system creates an OVERLAPPED structure and waits on the hFile HANDLE for completion. In the meantime the asynchronous I/O completes, thereby signaling the HANDLE causing the synchronous I/O to return prematurely without having actually completed.

Worse though is that by the time the asynchronous I/O that was initiated in response to the synchronous I/O request completes it will update the temporary OVERLAPPED structure that no longer exists. The result is memory corruption.

The full story can be found at The Old New Thing.

眼眸里的那抹悲凉 2024-09-01 21:30:22

看起来您正处于一种故意调用 API 的情况,这违反了记录的最佳实践。在这种情况下,所有的赌注都失败了。它可能有效,也可能无效。如果可以在此操作系统上运行,但不能在操作系统的下一个迭代或同一操作系统的下一个服务包上运行。当移植到 Win64 时会发生什么?到时候还能用吗?

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

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

Seems like you are in a situation where you are deliberately calling an API in contravention of the documented best practices. In such situations all bets are off. It may work, it may not. If may work on this OS, but not on the next iteration of the OS, or the next service pack of the same OS. What happens when you port to Win64? Will it still work then?

Does calling GetLastError() (or looking at @ERR,hr in the debugger) give any value that is useful in addition to the error code?

I recommend that you call it with a valid OVERLAPPED structure, get it working and remove all doubt (and possibility of random failure). Why have possibly buggy code (and very hard to reproduce bugs) in your software when you can fix the problem easily by using a valid OVERLAPPED structure?

漆黑的白昼 2024-09-01 21:30:22

为什么要问这个问题而不是修复代码以按预期调用 API?

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

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

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

Why ask the question rather than fix the code to call the API as it was intended?

I suspect it always appears to work because, even though this is an asynchronous I/O, it completes very quickly. Depending on how you're testing for success, it's possible the function is incorrectly reporting that the operation completed, but it actually completes before you test the results.

The real test would be to do a read on the pipe before there's data to be read.

But really, you should just fix the code. If your architecture cannot handle asynchronous I/O, then remove the FILE_FLAG_OVERLAPPED from the creation of the named pipe.

椵侞 2024-09-01 21:30:22

当他们说

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

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

如果您确实想重现此问题,请保持代码不变并继续您的生活。就在您完全忘记这个问题的时候,奇怪的行为就会浮现出来,它与调用 ReadFile 没有任何明显的关系。你会花费数天的时间来拔掉你的头发,而问题似乎会随机出现和消失。最终你会找到它,并因为没有遵循说明而自责。去过那里,做过那件事,没有乐趣!

重现问题的另一种方法是为您的客户安排一次重要的演示。到时候肯定会失败!

如果您不想在代码中添加重叠结构和所有相关的返回值检查、等待、事件等,您可以编写一个包装函数,该函数接受要读取的句柄和超时。只需用这个方便的包装器替换对 ReadFile 的调用即可。

When they say

Blockquote
If hFile is opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must point to a valid and unique OVERLAPPED structure, otherwise the function can incorrectly report that the read operation is complete.

they mean that there's nothing in the code preventing it working, but there's also a path through their code that can produce erroneous results. Just because you can't reproduce the problem with your particular hardware does not mean there is no problem.

If you really want to reproduce this problem, leave the code as is and go on with your life. Right about the time you've forgotten all about this problem, strange behavior will surface that will not have any obvious relations to calling ReadFile. You'll spend days pulling your hair out, and the problem will appear to come and go randomly. Eventually you'll find it and kick yourself for not following the instructions. Been there, done that, no fun!

The other way to recreate the problem is to schedule an important demo for your customer. It's sure to fail then!

If you don't want to splatter your code with OVERLAPPED structures and all of the related return value checks, Waits, Events, etc, you can write a wrapper function that takes a handle from which to read, and a timeout. Simply replace your calls to ReadFile with this handy-dandy wrapper.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文