Hi,

I noticed a handful of differences in the "lifecycle" of named pipes between Windows and Wine, and I figured I'd post about them here.������ I wrote a standalone one-file C++ program that demonstrates the differences I found. ������(In that test, the pipe handles are always overlapped and PIPE_WAIT.) ������I tested using������wine-1.9.12.

The differences were:

1. With Windows, there can be multiple concurrent ConnectNamedPipe operations on a pipe.������ With Wine, the second ConnectNamedPipe call fails with ERROR_INVALID_HANDLE.

2. With Windows, DisconnectNamedPipe aborts ConnectNamedPipe operations with the error ERROR_PIPE_NOT_CONNECTED.������ The DisconnectNamedPipe call succeeds, and the pipe is no longer listening.������ With Wine, the DisconnectNamedPipe call itself fails with ERROR_PIPE_LISTENING, and the pipe is still listening.

3. With Windows, if the server breaks the pipe by closing its handle, the client can finish reading the remaining data in the pipe.������ Once the data is gone, reading from a broken pipe fails with ERROR_BROKEN_PIPE.������ Writing to a broken pipe fails with ERROR_NO_DATA.������ With Wine, once the server breaks the pipe, the client cannot read the remaining data, and I/O on a broken pipe fails with ERROR_PIPE_NOT_CONNECTED. ������(Exception: ReadFile on a server pipe still returns ERROR_BROKEN_PIPE.)

4. Calling������ConnectNamedPipe on a broken server pipe fails with������ERROR_NO_DATA on Windows, but������ERROR_NO_DATA_DETECTED on Wine.

My test program is at:������https://gist.github.com/rprichard/8dd8ca134b39534b7da2733994aa07ba.������ It passes on XP, Vista, Win7, and Win10, with the exception of testChangeMaxInstancesAfterClosingLastServerHandle, which passes on Vista and up.������ A couple of the more interesting tests are������testDisconnectAtStartup and������testReadFromBrokenClientPipe:

testDisconnectAtStartup:

// Calling DisconnectNamedPipe on a new pipe successfully transitions it to a
// "Not Connected" state.������ The second DisconnectNamedPipe call fails, and the
// client can't connect.
void testDisconnectAtStartup() {
������ ������ const HANDLE server = assertValid(openServer(name(0)));
������ ������ CHECK_SUCCESS(DisconnectNamedPipe(server));
������ ������ CHECK(createFile(name(0)) == INVALID_HANDLE_VALUE);
������ ������ CHECK_FAILURE(DisconnectNamedPipe(server), ERROR_PIPE_NOT_CONNECTED);
������ ������ CHECK(createFile(name(0)) == INVALID_HANDLE_VALUE);
������ ������ CHECK_SUCCESS(CloseHandle(server));
}

// If a client pipe is broken, rather than disconnected, the remaining data can
// still be read.
void testReadFromBrokenClientPipe() {
������ ������ DWORD actual = 0;
������ ������ Over over;
������ ������ char buf[2] = {};

������ ������ const HANDLE server = assertValid(openServer(name(0)));
������ ������ const HANDLE client = assertValid(createFile(name(0)));
������ ������ CHECK_SUCCESS(WriteFile(server, buf, 1, &actual, &over));
������ ������ CHECK_SUCCESS(CloseHandle(server));

������ ������ CHECK_SUCCESS(ReadFile(client, buf, 2, &actual, &over));
������ ������ CHECK(actual == 1);
������ ������ CHECK_FAILURE(ReadFile(client, buf, 2, &actual, &over), ERROR_BROKEN_PIPE);

������ ������ CHECK_SUCCESS(CloseHandle(client));
}

I know that item #3 above breaks my winpty project (in principle), where a pipe server signals EOF by closing a pipe without disconnecting it.������ The winpty client can read all the output before hitting the broken pipe.������ I suspect winpty may not run on Wine anyway, though.

-Ryan