diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs index 83d4f66d0cf4..7a9f6860d45f 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs @@ -43,6 +43,10 @@ private bool TryConnect(int timeout) { access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE; } + else + { + access |= Interop.Kernel32.FileOperations.FILE_WRITE_ATTRIBUTES; + } SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access); diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs index 2b6421d1f09a..206dad113edc 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.Specific.cs @@ -472,6 +472,62 @@ public async Task PipeTransmissionMode_Returns_Byte() } } + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // Unix doesn't currently support message mode + public void Windows_SetReadModeTo__PipeTransmissionModeMessage() + { + string pipeName = PipeStreamConformanceTests.GetUniquePipeName(); + using (var server = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous)) + using (var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out)) + { + Task clientConnect = client.ConnectAsync(); + server.WaitForConnection(); + clientConnect.Wait(); + + // Throws regardless of connection status for the pipe that is set to PipeDirection.In. This is because + // as per MSDN, "This mode gives the server the equivalent of GENERIC_READ access to the pipe", which + // isn't sufficient to call SetNamedPipeHandleState, as we need FILE_WRITE_ATTRIBUTES as well, or + // GENERIC_WRITE instead which includes this permission. Since the readmode can be set for the server + // side when creating the named pipe, this issue can be avoided by specifying the desired mode from the + // start. In the theoretical case of wanting to create a pipe in message mode, then downgrade the + // readmode to byte mode on the server however, this isn't currently possible in the Win32 API. + Assert.Throws(() => server.ReadMode = PipeTransmissionMode.Byte); + Assert.Throws(() => server.ReadMode = PipeTransmissionMode.Message); + + // Can select either message or byte mode on client end of the pipe + client.ReadMode = PipeTransmissionMode.Byte; + client.ReadMode = PipeTransmissionMode.Message; + } + + using (var server = new NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous)) + using (var client = new NamedPipeClientStream(".", pipeName, PipeDirection.In)) + { + Task clientConnect = client.ConnectAsync(); + server.WaitForConnection(); + clientConnect.Wait(); + + // Can select either message or byte mode on either end of the pipe + server.ReadMode = PipeTransmissionMode.Byte; + client.ReadMode = PipeTransmissionMode.Byte; + server.ReadMode = PipeTransmissionMode.Message; + client.ReadMode = PipeTransmissionMode.Message; + } + + using (var server = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous)) + using (var client = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous)) + { + Task clientConnect = client.ConnectAsync(); + server.WaitForConnection(); + clientConnect.Wait(); + + // Can select either message or byte mode on either end of the pipe + server.ReadMode = PipeTransmissionMode.Byte; + client.ReadMode = PipeTransmissionMode.Byte; + server.ReadMode = PipeTransmissionMode.Message; + client.ReadMode = PipeTransmissionMode.Message; + } + } + [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Unix doesn't currently support message mode public void Windows_SetReadModeTo__PipeTransmissionModeByte() @@ -484,9 +540,20 @@ public void Windows_SetReadModeTo__PipeTransmissionModeByte() server.WaitForConnection(); clientConnect.Wait(); - // Throws regardless of connection status for the pipe that is set to PipeDirection.In - Assert.Throws(() => server.ReadMode = PipeTransmissionMode.Byte); client.ReadMode = PipeTransmissionMode.Byte; + + // Throws regardless of connection status for the pipe that is set to PipeDirection.In. This is because + // as per MSDN, "This mode gives the server the equivalent of GENERIC_READ access to the pipe", which + // isn't sufficient to call SetNamedPipeHandleState, as we need FILE_WRITE_ATTRIBUTES as well, or + // GENERIC_WRITE instead which includes this permission. Since the readmode can be set for the server + // side when creating the named pipe, this issue can be avoided by specifying the desired mode from the + // start. In the theoretical case of wanting to create a pipe in message mode, then downgrade the + // readmode to byte mode on the server however, this isn't currently possible in the Win32 API. + Assert.Throws(() => server.ReadMode = PipeTransmissionMode.Byte); + Assert.Throws(() => server.ReadMode = PipeTransmissionMode.Message); + + // Can't change to message mode on the client for a byte pipe + Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message); } using (var server = new NamedPipeServerStream(pipeName, PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) @@ -496,9 +563,14 @@ public void Windows_SetReadModeTo__PipeTransmissionModeByte() server.WaitForConnection(); clientConnect.Wait(); - // Throws regardless of connection status for the pipe that is set to PipeDirection.In - Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Byte); + client.ReadMode = PipeTransmissionMode.Byte; server.ReadMode = PipeTransmissionMode.Byte; + + // Can't change to message mode on the server for a byte pipe + Assert.Throws(() => server.ReadMode = PipeTransmissionMode.Message); + + // Can't change to message mode on the client for a byte pipe + Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message); } using (var server = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) @@ -510,6 +582,12 @@ public void Windows_SetReadModeTo__PipeTransmissionModeByte() server.ReadMode = PipeTransmissionMode.Byte; client.ReadMode = PipeTransmissionMode.Byte; + + // Can't change to message mode on the server for a byte pipe + Assert.Throws(() => server.ReadMode = PipeTransmissionMode.Message); + + // Can't change to message mode on the client for a byte pipe + Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message); } }