IO 完了ルーチンを使用して、パイプを介して異なるマシン上の 2 つのプロセス間で通信しています。
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];
編集:
ハリーが提案したように重複した構造を再初期化しようとした後、私は奇妙なことに気付きました. memset
各の前にOVERLAPPED
構造体をゼロにしWriteFileEx
、約 1/5000 の完了ルーチン コールバックで、cbWritten
パラメーターとOVERLAPPED
構造体のInternalHigh
メンバーが、最新のメッセージではなく、前のメッセージのサイズに設定されました。完了ルーチン内のパイプのクライアント側とサーバー側の両方でファイルにログを追加したところ、両端で送受信されたデータは完全に一致しました (そして正しい期待データ)。これにより、データをファイルに書き込むのにかかる時間InternalHigh
で、構造内のメンバーOVERLAPPED
が変更され、予想していたメッセージのサイズが反映されたことが明らかになりました (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);
Windows 7 64ビットで呼び出される完了ルーチンに使用しています。
このMSDNページには次のように書かれています:
「完了ルーチンが呼び出された後、システムは OVERLAPPED 構造を使用しないため、完了ルーチンはオーバーラップされた構造によって使用されているメモリの割り当てを解除できます。」
...どうやら、このコードが再現できることは決して起こらないはずですか?
これは WINAPI のバグですか?