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