WriteFileEx完成例程成功,但传输的字节不正确

par*_*ice 9 c c++ windows winapi named-pipes

我正在使用IO完成例程通过管道在不同机器上的两个进程之间进行通信.

有时,当调用WriteFileEx的完成例程时,完成例程参数dwErrorCode为0(即无错误),GetOverlappedResult返回true(即无错误),但dwNumberOfBytesTransfered与WriteFileEx调用中的nNumberOfBytesToWrite不匹配.我只在管道的客户端看到这个.

如果传输的字节数与请求传输的字节数不匹配,那么如何才能认为这是成功的?

这是客户端管道句柄的创建方式:

mHPipe = CreateFile(pipeName,                 // pipe name 
                    GENERIC_READ |            // read and write access 
                    GENERIC_WRITE, 
                    0,                        // no sharing 
                    NULL,                     // default security attributes
                    OPEN_EXISTING,            // opens existing pipe 
                    FILE_FLAG_OVERLAPPED |    // overlapped
                    FILE_FLAG_WRITE_THROUGH,  // write through mode
                    NULL);                    // no template file 

// do some checking...

// The pipe connected; change to message-read mode. 
DWORD dwMode = PIPE_READMODE_MESSAGE; 
BOOL fSuccess = SetNamedPipeHandleState(mHPipe,   // pipe handle 
                                        &dwMode,  // new pipe mode 
                                        NULL,     // don't set maximum bytes 
                                        NULL);    // don't set maximum time 
Run Code Online (Sandbox Code Playgroud)

谁能明白为什么会这样呢?

谢谢

编辑:

相关的WriteFileEx代码如下:

void WINAPI CompletedWriteRoutine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverLap)
{
  BOOL fWrite = FALSE;
  LPPIPEINST lpPipeInst = (LPPIPEINST)lpOverLap;

  //
  // !  99.9% of the time, dwNumberOfBytesTransfered == lpPipeInst->cbDataSize
  //    but 0.1% of the time, they do not match
  //

  // Some stuff

  // Copy next message to send
  memcpy_s(lpPipeInst->chData, sizeof(lpPipeInst->chData), pMsg->msg, pMsg->size);
  lpPipeInst->cbDataSize = pMsg->size;

  // Some other stuff

  fWrite = WriteFileEx(lpPipeInst->hPipeInst,
                       lpPipeInst->chData,
                       lpPipeInst->cbDataSize,
                       (LPOVERLAPPED) lpPipeInst,
                       (LPOVERLAPPED_COMPLETION_ROUTINE)CompletedWriteRoutine);

  // Some other, other stuff
}
Run Code Online (Sandbox Code Playgroud)

LPPIPEINST声明为:

typedef struct 
{ 
  OVERLAPPED oOverlap;      // must remain first item
  HANDLE hPipeInst;
  TCHAR chData[BUFSIZE];
  DWORD cbDataSize;
} PIPEINST, *LPPIPEINST;
Run Code Online (Sandbox Code Playgroud)

并且对CompletedWriteRoutine的初始调用给出了如此声明的lpOverlap参数:

PIPEINST pipeInstWrite        = {0};
pipeInstWrite.hPipeInst       = client.getPipeHandle();
pipeInstWrite.oOverlap.hEvent = hEvent[eventWriteComplete];
Run Code Online (Sandbox Code Playgroud)

编辑:

在尝试重新初始化重叠的结构后,哈利建议,我注意到一些特殊的东西.我memsetOVERLAPPED结构在每个之前为零WriteFileEx,并且大约是1/5000完成例程回调,cbWritten参数和OVERLAPPED结构的InternalHigh成员现在设置为前一个消息的大小,而不是最近的消息.我在完成例程内的管道的客户端和服务器端添加了一些日志记录文件,两端发送和接收的数据完全匹配(以及正确的预期数据).然后,它公布了在将数据写入文件所花费的时间InternalHigh内,OVERLAPPED结构中的成员已更改为现在反映我期望的消息的大小(cbWritten仍然是旧邮件大小).我删除了文件记录,现在我可以使用以下代码重现像clockwork一样的问题:

void WINAPI CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten, LPOVERLAPPED lpOverLap)
{
  LPPIPEINST lpPipeInst = (LPPIPEINST)lpOverLap;

  // Completion routine says it wrote the amount of data from the previous callback
  if (cbWritten != lpPipeInst->cbDataSize)
  {
    // Roughly 1 in 5000 callbacks ends up in here

    OVERLAPPED ovl1 = lpPipeInst->oOverlap; // Contains size of previous message, i.e. cbWritten
    Sleep(100);
    OVERLAPPED ovl2 = lpPipeInst->oOverlap; // Contains size of most recent message, i.e lpPipeInst->cbDataSize
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

有时,在OVERLAPPED结构和完成例程输入参数更新之前,有时会调用完成例程.我MsgWaitForMultipleObjectsEx(eventLast, hEvent, INFINITE, QS_POSTMESSAGE, MWMO_ALERTABLE);用于在Windows 7 64位上调用完成例程.

这个MSDN页面说:

"在调用完成例程后,系统不使用OVERLAPPED结构,因此完成例程可以释放重叠结构使用的内存."

......显然,这段代码可以重现的东西永远不会发生?

这是一个WINAPI错误吗?

par*_*ice 3

添加FILE_FLAG_NO_BUFFERINGCreateFile通话中 - 此后再也没有看到问题。感谢所有抽出宝贵时间发表评论的人。