問題は、常にCreateNamedPipe() または DisconnectNamedPipe()の後、I/O を試行する前に呼び出す必要がある ConnectNamedPipe() を省略したことです。
クライアントが接続するのを待っている間にブロックしたくない場合は、非同期 I/O モードでパイプを作成できます。この場合、ConnectNamedPipe() の呼び出しには、クライアントの接続時に設定されるイベント オブジェクトが必要です。または、PIPE_NOWAIT を設定して、成功するまで定期的に ConnectNamedPipe() を呼び出すこともできますが、これはレガシー機能であり、使用しないことをお勧めします。(ほとんどの場合、イベント オブジェクトを使用すると、ポーリングよりもはるかに効率的になります。)
お気づきのとおり、Windows では ConnectNamedPipe() を呼び出さなくても問題を解決できますが、この動作は文書化されていないため、おそらく回避する必要があります。同様に、成功を待たずに ConnectNamedPipe() を呼び出すと、パイプの接続状態がリセットされるという事実は文書化されておらず、依存すべきではありません。
リクエストに応じて、パイプのサーバー側の使用を示す実際のコードを次に示します。このコードは GUI アプリケーションから取得したものであるため、非同期 I/O を使用していますが、一度に 1 つのクライアントとしか通信できないことに注意してください。(ただし、わずかな変更を加えるだけで、複数のスレッドで実行できます。)
void wait_for_object(HANDLE object)
{
DWORD dw;
MSG msg;
for (;;)
{
dw = MsgWaitForMultipleObjectsEx(1, &object, INFINITE, QS_ALLINPUT, 0);
if (dw == WAIT_OBJECT_0) break;
if (dw == WAIT_OBJECT_0 + 1)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
continue;
}
srvfail(L"sleep() messageloop", GetLastError());
}
}
HANDLE server_pipe;
HANDLE io_event;
void pipe_connection(void)
{
OVERLAPPED overlapped;
DWORD dw, err;
SecureZeroMemory(&overlapped, sizeof(overlapped));
overlapped.hEvent = io_event;
if (!ReadFile(server_pipe, input_buffer, sizeof(input_buffer) - 1, NULL, &overlapped))
{
err = GetLastError();
if (err == ERROR_IO_PENDING)
{
wait_for_object(io_event);
if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE))
{
srvfail(L"Read from pipe failed asynchronously.", GetLastError());
}
}
else
{
srvfail(L"Read from pipe failed synchronously.", GetLastError());
}
}
else
{
if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE))
{
srvfail(L"GetOverlappedResult failed reading from pipe.", GetLastError());
}
}
input_buffer[dw] = '\0';
process_command();
if (!WriteFile(server_pipe, &output_struct,
((char *)&output_struct.output_string - (char *)&output_struct) + output_struct.string_length,
NULL, &overlapped))
{
err = GetLastError();
if (err == ERROR_IO_PENDING)
{
wait_for_object(io_event);
if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE))
{
srvfail(L"Write to pipe failed asynchronously.", GetLastError());
}
}
else
{
srvfail(L"Write to pipe failed synchronously.", GetLastError());
}
}
else
{
if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE))
{
srvfail(L"GetOverlappedResult failed writing to pipe.", GetLastError());
}
}
if (!FlushFileBuffers(server_pipe)) srvfail(L"FlushFileBuffers failed.", GetLastError());
if (!DisconnectNamedPipe(server_pipe)) srvfail(L"DisconnectNamedPipe failed.", GetLastError());
}
void server(void)
{
OVERLAPPED overlapped;
DWORD err, dw;
// Create the named pipe
server_pipe = CreateNamedPipe(pipe_name, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, buffer_size, buffer_size, 0, NULL);
if (server_pipe == INVALID_HANDLE_VALUE) srvfail(L"CreateNamedPipe failed.", GetLastError());
// Wait for connections
io_event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (io_event == NULL) srvfail(L"CreateEvent(io_event) failed.", GetLastError());
for (;;)
{
SecureZeroMemory(&overlapped, sizeof(overlapped));
overlapped.hEvent = io_event;
if (!ConnectNamedPipe(server_pipe, &overlapped))
{
err = GetLastError();
if (err == ERROR_PIPE_CONNECTED)
{
pipe_connection();
}
else if (err == ERROR_IO_PENDING)
{
wait_for_object(io_event);
if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE))
{
srvfail(L"Pipe connection failed asynchronously.", GetLastError());
}
pipe_connection();
}
else
{
srvfail(L"Pipe connection failed synchronously.", GetLastError());
}
}
else
{
if (!GetOverlappedResult(server_pipe, &overlapped, &dw, FALSE))
{
srvfail(L"GetOverlappedResult failed connecting pipe.", GetLastError());
}
pipe_connection();
}
}
}
(このコードは、余分なロジックを削除するために元のコードから編集されています。編集されたバージョンをコンパイルしようとはしていないため、小さな問題がいくつかある可能性があります。)