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

WriteFileEx completion routine succeeds, but bytes transferred is incorrect

本文关键字:传输 字节 不正确 例程 成功 WriteFileEx      更新时间:2023-10-16

我使用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 

有人明白为什么会发生这种事吗?

感谢

编辑:

相关的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
}

其中LPPIPEINST声明为:

typedef struct 
{ 
OVERLAPPED oOverlap;      // must remain first item
HANDLE hPipeInst;
TCHAR chData[BUFSIZE];
DWORD cbDataSize;
} PIPEINST, *LPPIPEINST;

对CompletedWriteRoutine的初始调用给出了这样声明的lpOverlap参数:

PIPEINST pipeInstWrite        = {0};
pipeInstWrite.hPipeInst       = client.getPipeHandle();
pipeInstWrite.oOverlap.hEvent = hEvent[eventWriteComplete];

编辑:

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

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
}
...
}

有时,完成例程似乎在OVERLAPPED结构之前被调用,并且完成例程输入参数被更新。我使用MsgWaitForMultipleObjectsEx(eventLast, hEvent, INFINITE, QS_POSTMESSAGE, MWMO_ALERTABLE);来完成在Windows7 64位上调用的例程。

这个MSDN页面上写着:

"调用完成例程后,系统不使用OVERLAPPED结构,因此完成例程可以解除分配重叠结构使用的内存。">

。。。显然,这个代码所能复制的东西永远不会发生?

这是WINAPI错误吗?

FILE_FLAG_NO_BUFFERING添加到CreateFile调用中-此后再也没有发现问题。感谢所有评论您的时间。