如何知道何时ReadFileEx()重叠I / O已完成?

HasOverlappedIoCompleted()不能用于使用ReadFileEx()WriteFileEx()开始的asynchronousI / O。 底部的代码片段演示了这一点。 在这个例子中, ReadFileEx()从没有input的pipe道中读取,所以读取不会完成。 但HasOverlappedIoCompleted()返回TRUE。 如果我将调用更改为重叠的ReadFile()HasOverlappedIoCompleted()将按预期返回FALSE。

我的问题是:我怎样才能找出一个重叠的callbackI / O请求是否已经完成,而不依赖于callback本身? 在我的应用程序中,APC可能已经排队,但不一定已经运行,因为应用程序可能尚未处于可警告状态。

谢谢。

(注意GetOverlappedResult()没有帮助 – 它也返回TRUE。)

多一点背景:在我使用ReadFileEx()的例子中,因为它很容易certificate问题​​。 在我的应用程序中,我在pipe道实例上重复调用WriteFileEx() 。 如果以前的WriteFileEx()还没有完成,我必须删除消息而不是发送它(我不能在同一个pipe道实例上有多个挂起的写入),但是如果先前的WriteFileEx() 已经完成,那么我必须启动下一个, 即使完成callback尚未运行

编辑:问题场景的描述

  1. 线程进入可警告状态(一个读取APC排队)。
  2. 读取APC开始:它将WriteFileEx()队列并设置“写入挂起”标志。 然后排队一个ReadFileEx()。
  3. 主线程开始工作(不可警告)。
  4. 排队的阅读完成。
  5. 排队的写入完成(读取之后)。
  6. 主线程进入可警告状态。
  7. 读取的APC首先在队列中首先运行:它查看“写入挂起”标志,并且由于它仍然被设置,它将丢弃写入。 事实上,虽然WriteFileEx() 已经完成,但它还没有调用APC,因为ReadFileEx()首先完成。

我不想testing自定义的“写入挂起”标志,而是想从操作系统中找出WriteFileEx()是否已经实际完成,即使APC尚未运行。


 #include <Windows.h> #include <stdio.h> #include <assert.h> VOID CALLBACK readComplete(DWORD err, DWORD bytes, LPOVERLAPPED ovlp) { } int main(int argc, char *argv[]) { HANDLE hServer; OVERLAPPED serverOvlp = { 0 }; HANDLE hClient; DWORD bytes; BYTE buffer[16]; BOOL result; hServer = CreateNamedPipe("\\\\.\\pipe\\testpipe", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 0, 0, 5000, NULL); serverOvlp.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); ConnectNamedPipe(hServer, &serverOvlp); assert(GetLastError() == ERROR_IO_PENDING); hClient = CreateFile("\\\\.\\pipe\\testpipe", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); GetOverlappedResult(hServer, &serverOvlp, &bytes, TRUE); /* Server starts an overlapped read */ // result = ReadFile(hServer, buffer, sizeof(buffer), &bytes, &serverOvlp); result = ReadFileEx(hServer, buffer, sizeof(buffer), &serverOvlp, readComplete); if (HasOverlappedIoCompleted(&serverOvlp)) { puts("Completed"); } else { puts("Not completed"); } return EXIT_SUCCESS; } 

你所要求的行为似乎对我来说是错误的,因为它涉及到竞争条件。 只有当您仍在发送消息B时收到消息A时才会出现此问题。目前,A始终被忽略,即不会发送其他消息。 您正在尝试获取的行为将导致发送额外的消息,当且仅当在到达和B完成之间的时间间隔内,服务器正忙于处理工作。 我想你应该一直忽略A,或者一旦B完成,总是发送回复。

但是,如果您确定这是您想要的,则可以获得此行为。 一种解决方案是从ReadFileEx完成例程中调用QueueUserAPC,以将调用排队到发送答复的函数。 由于APC以FIFO顺序运行,所以新的APC肯定会在APC之后运行,即WriteFileEx已经排队。

根据具体情况,对于两个完成例程来设置标志,或将项目添加到队列中,都可能更清晰,并让主循环完成实际的工作。

如果您需要轮询APC(例如,因为您的主循环没有任何自然发生的等待操作),您可以在超时值为0的虚拟事件上使用WaitForSingleObjectEx。事件是否发送信号无关紧要,不是,所有排队的装甲运兵车仍将被召唤。

我已经改变了你的代码来实现CALLBACK。 对于这个例子工作,管道缓冲区的长度不应该是0.另外,我认为管道的两端应该设置OVERLAPPED标志。

 #include <Windows.h> #include <stdio.h> #include <assert.h> #define BUFFSIZE 100 #define MYPIPE "\\\\.\\pipe\\testpipe" typedef struct { OVERLAPPED serverOvlp; // Overlapped should always be first in structure CHAR buffer[20]; } OVLP; VOID CALLBACK readComplete(DWORD err, DWORD bytes, LPOVERLAPPED ovlp) { OVLP *temp = (OVLP *) ovlp; printf("readComplete err=%d bytes=%d buffer=%s\n", err, bytes, temp->buffer); } int main(void) { HANDLE hserver; HANDLE hClient; OVLP oserver; DWORD bytes; CHAR ClientBuffer[20] = "Test message"; hserver = CreateNamedPipe(MYPIPE, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, BUFFSIZE, BUFFSIZE, 5000, NULL); //-------------------------------------- CLIENT hClient = CreateFile(MYPIPE, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); WriteFile(hClient,ClientBuffer,strlen(ClientBuffer)+1,&bytes,NULL); //-------------------------------------- SERVER ConnectNamedPipe(hserver, &oserver.serverOvlp); if (HasOverlappedIoCompleted(&oserver.serverOvlp)) assert(GetLastError() != 0 ); puts("Client Pipe connected\n"); ReadFileEx(hserver, oserver.buffer, sizeof(oserver.buffer), (LPOVERLAPPED)&oserver, readComplete); SleepEx(INFINITE,TRUE); // Creates an alertable event so CALLBACK is triggered if (HasOverlappedIoCompleted(&oserver.serverOvlp)) { puts("Completed"); } else { puts("Not completed"); } return EXIT_SUCCESS; }