2

HasOverlappedIoCompleted()不适用于以ReadFileEx()and开头的异步 I/O WriteFileEx()。底部的代码片段演示了这一点。在此示例中,ReadFileEx()从没有输入的管道读取,因此读取不会完成。但HasOverlappedIoCompleted()返回 TRUE。如果我将调用更改为重叠ReadFile()HasOverlappedIoCompleted()则按预期返回 FALSE。

我的问题是:如何在不依赖回调本身的情况下确定带有回调的重叠 I/O 请求是否已完成?在我的应用程序中,APC 可能已排队,但不一定已经运行,因为应用程序可能尚未在警报状态下等待。

谢谢。

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

更多背景知识:在我使用的示例中,ReadFileEx()因为它很容易演示问题。在我的应用程序中,我WriteFileEx()在管道实例上反复调用。如果前一个WriteFileEx()还没有完成,我必须丢弃消息而不是发送它(我在同一个管道实例上不能有多个挂起的写入),但是如果前一个WriteFileEx() 已经完成,那么我必须开始下一个,即使完成回调尚未运行

编辑:问题场景的描述

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

我不想测试我的自定义“写入挂起”标志,而是想从操作系统中找出 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;
}
4

2 回答 2

1

The behaviour you are asking for seems wrong to me, because it involves a race condition. The issue only arises if you receive a message A while you are still sending a message B. At present, A is always ignored, i.e., no additional message is sent. The behaviour you are trying to get would result in an additional message being sent if and only if the server was busy processing work during the interval between A arriving and B completing. I think you should either always be ignoring A or always sending a response once B is completed.

However, you can get this behaviour if you're sure it is what you want. One solution would be to call QueueUserAPC from the ReadFileEx completion routine, to queue a call to a function which sends the reply. Since APCs are run in FIFO order, the new APC will definitely run after the APC that WriteFileEx has already queued.

Depending on the details, it might be cleaner for both completion routines to set flags, or add items to a queue, and have your main loop do the actual work.

If you need to poll for APCs (for example, because your main loop doesn't have any naturally occurring wait operations) you can use WaitForSingleObjectEx on a dummy event with a timeout of 0. It doesn't matter whether the event is signaled or not, all queued APCs will still be called.

于 2011-12-05T20:58:09.367 回答
1

我已经稍微更改了您的代码以实现 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;
}
于 2011-12-24T07:42:40.057 回答