diff --git a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.TypeForwards.cs b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.TypeForwards.cs
new file mode 100644
index 00000000000000..4012db03b63b1d
--- /dev/null
+++ b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.TypeForwards.cs
@@ -0,0 +1,4 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.IO.Pipes.PipeAccessRights))]
diff --git a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs
index 775004246a5e00..4d35d22a6719a4 100644
--- a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs
+++ b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs
@@ -14,27 +14,6 @@ public static class NamedPipeServerStreamAcl
{
public static System.IO.Pipes.NamedPipeServerStream Create(string pipeName, System.IO.Pipes.PipeDirection direction, int maxNumberOfServerInstances, System.IO.Pipes.PipeTransmissionMode transmissionMode, System.IO.Pipes.PipeOptions options, int inBufferSize, int outBufferSize, System.IO.Pipes.PipeSecurity? pipeSecurity, System.IO.HandleInheritability inheritability = System.IO.HandleInheritability.None, System.IO.Pipes.PipeAccessRights additionalAccessRights = default) { throw null; }
}
- [System.FlagsAttribute]
- public enum PipeAccessRights
- {
- ReadData = 1,
- WriteData = 2,
- CreateNewInstance = 4,
- ReadExtendedAttributes = 8,
- WriteExtendedAttributes = 16,
- ReadAttributes = 128,
- WriteAttributes = 256,
- Write = 274,
- Delete = 65536,
- ReadPermissions = 131072,
- Read = 131209,
- ReadWrite = 131483,
- ChangePermissions = 262144,
- TakeOwnership = 524288,
- Synchronize = 1048576,
- FullControl = 2032031,
- AccessSystemSecurity = 16777216,
- }
public sealed partial class PipeAccessRule : System.Security.AccessControl.AccessRule
{
public PipeAccessRule(System.Security.Principal.IdentityReference identity, System.IO.Pipes.PipeAccessRights rights, System.Security.AccessControl.AccessControlType type) : base (default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AccessControlType)) { }
diff --git a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj
index 24b3086a23d098..d16c599454b656 100644
--- a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj
+++ b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj
@@ -5,6 +5,7 @@
+
diff --git a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs
index f7b404b93a502e..85387dbbd4b635 100644
--- a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs
+++ b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs
@@ -45,6 +45,8 @@ public sealed partial class NamedPipeClientStream : System.IO.Pipes.PipeStream
public NamedPipeClientStream(System.IO.Pipes.PipeDirection direction, bool isAsync, bool isConnected, Microsoft.Win32.SafeHandles.SafePipeHandle safePipeHandle) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
public NamedPipeClientStream(string pipeName) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
public NamedPipeClientStream(string serverName, string pipeName) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
+ [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")]
+ public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeAccessRights desiredAccessRights, PipeOptions options, System.Security.Principal.TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) : base(default(System.IO.Pipes.PipeDirection), default(int)) { }
public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction, System.IO.Pipes.PipeOptions options) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction, System.IO.Pipes.PipeOptions options, System.Security.Principal.TokenImpersonationLevel impersonationLevel) : base (default(System.IO.Pipes.PipeDirection), default(int)) { }
@@ -82,6 +84,27 @@ public void WaitForConnection() { }
public System.Threading.Tasks.Task WaitForConnectionAsync() { throw null; }
public System.Threading.Tasks.Task WaitForConnectionAsync(System.Threading.CancellationToken cancellationToken) { throw null; }
}
+ [System.FlagsAttribute]
+ public enum PipeAccessRights
+ {
+ ReadData = 1,
+ WriteData = 2,
+ CreateNewInstance = 4,
+ ReadExtendedAttributes = 8,
+ WriteExtendedAttributes = 16,
+ ReadAttributes = 128,
+ WriteAttributes = 256,
+ Write = 274,
+ Delete = 65536,
+ ReadPermissions = 131072,
+ Read = 131209,
+ ReadWrite = 131483,
+ ChangePermissions = 262144,
+ TakeOwnership = 524288,
+ Synchronize = 1048576,
+ FullControl = 2032031,
+ AccessSystemSecurity = 16777216,
+ }
public enum PipeDirection
{
In = 1,
diff --git a/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml b/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml
index a047c21f84fff4..db08f006c4c52e 100644
--- a/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml
+++ b/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml
@@ -1,7 +1,6 @@
-
CP0001
T:System.IO.Pipes.AnonymousPipeServerStreamAcl
@@ -14,12 +13,6 @@
ref/net9.0/System.IO.Pipes.dll
runtimes/win/lib/net9.0/System.IO.Pipes.dll
-
- CP0001
- T:System.IO.Pipes.PipeAccessRights
- ref/net9.0/System.IO.Pipes.dll
- runtimes/win/lib/net9.0/System.IO.Pipes.dll
-
CP0001
T:System.IO.Pipes.PipeAccessRule
diff --git a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx
index 8910eb4d7ac455..e50c4aa314412a 100644
--- a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx
+++ b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx
@@ -120,6 +120,9 @@
Invalid PipeAccessRights value.
+
+ Specifying PipeAccessRights is not supported on this platform.
+
This flag may not be set on a pipe.
diff --git a/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj b/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj
index d3d1d3152e2e33..b36765a036839e 100644
--- a/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj
+++ b/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj
@@ -19,6 +19,7 @@
+
@@ -116,7 +117,6 @@
-
diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs
index 2dc43385bb6ee3..4c7df8d63e73a4 100644
--- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs
+++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs
@@ -7,6 +7,7 @@
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
+using System.Security.Principal;
using System.Threading;
using Microsoft.Win32.SafeHandles;
@@ -18,6 +19,16 @@ namespace System.IO.Pipes
///
public sealed partial class NamedPipeClientStream : PipeStream
{
+ [System.Runtime.Versioning.SupportedOSPlatform("windows")]
+ public NamedPipeClientStream(string serverName, string pipeName, PipeAccessRights desiredAccessRights,
+ PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability)
+ : base(PipeDirection.InOut, 0)
+ {
+ throw new PlatformNotSupportedException(SR.PlatformNotSupported_PipeAccessRights);
+ }
+
+ private static int AccessRightsFromDirection(PipeDirection _) => 0;
+
private bool TryConnect(int _ /* timeout */)
{
// timeout isn't used as Connect will be very fast,
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 6f612bbc904bc3..7ffb560a359003 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
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Principal;
@@ -16,6 +17,81 @@ namespace System.IO.Pipes
///
public sealed partial class NamedPipeClientStream : PipeStream
{
+ ///
+ /// Initializes a new instance of the class with the specified pipe and server names,
+ /// the desired , and the specified impersonation level and inheritability.
+ ///
+ /// The name of the remote computer to connect to, or "." to specify the local computer.
+ /// The name of the pipe.
+ /// One of the enumeration values that specifies the desired access rights of the pipe.
+ /// One of the enumeration values that determines how to open or create the pipe.
+ /// One of the enumeration values that determines the security impersonation level.
+ /// One of the enumeration values that determines whether the underlying handle will be inheritable by child processes.
+ /// or is null.
+ /// or is a zero-length string.
+ /// is set to "anonymous".
+ /// is not a valid value.
+ /// is not a valid value.
+ /// is not a valid value.
+ /// is not a valid value.
+ ///
+ /// The pipe direction for this constructor is determined by the parameter.
+ /// If the parameter specifies ,
+ /// the pipe direction is . If the parameter
+ /// specifies , the pipe direction is .
+ /// If the value of specifies both
+ /// and , the pipe direction is .
+ ///
+ [System.Runtime.Versioning.SupportedOSPlatform("windows")]
+ public NamedPipeClientStream(string serverName, string pipeName, PipeAccessRights desiredAccessRights,
+ PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability)
+ : this(serverName, pipeName, DirectionFromRights(desiredAccessRights), options, impersonationLevel, inheritability)
+ {
+ _accessRights = (int)desiredAccessRights;
+ }
+
+ private static PipeDirection DirectionFromRights(PipeAccessRights desiredAccessRights, [CallerArgumentExpression(nameof(desiredAccessRights))] string? argumentName = null)
+ {
+ // Validate the desiredAccessRights parameter here to ensure an invalid value does not result
+ // in an argument exception being thrown for the direction argument
+ // Throw if there are any unrecognized bits
+ // Throw if neither ReadData nor WriteData are specified, as this will result in an invalid PipeDirection
+ if ((desiredAccessRights & ~(PipeAccessRights.FullControl | PipeAccessRights.AccessSystemSecurity)) != 0 ||
+ ((desiredAccessRights & (PipeAccessRights.ReadData | PipeAccessRights.WriteData)) == 0))
+ {
+ throw new ArgumentOutOfRangeException(argumentName, SR.ArgumentOutOfRange_NeedValidPipeAccessRights);
+ }
+
+ PipeDirection direction = 0;
+
+ if ((desiredAccessRights & PipeAccessRights.ReadData) != 0)
+ {
+ direction |= PipeDirection.In;
+ }
+ if ((desiredAccessRights & PipeAccessRights.WriteData) != 0)
+ {
+ direction |= PipeDirection.Out;
+ }
+
+ return direction;
+ }
+
+ private static int AccessRightsFromDirection(PipeDirection direction)
+ {
+ int access = 0;
+
+ if ((PipeDirection.In & direction) != 0)
+ {
+ access |= Interop.Kernel32.GenericOperations.GENERIC_READ;
+ }
+ if ((PipeDirection.Out & direction) != 0)
+ {
+ access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE;
+ }
+
+ return access;
+ }
+
// Waits for a pipe instance to become available. This method may return before WaitForConnection is called
// on the server end, but WaitForConnection will not return until we have returned. Any data written to the
// pipe by us after we have connected but before the server has called WaitForConnection will be available
@@ -34,17 +110,7 @@ private bool TryConnect(int timeout)
_pipeFlags |= (((int)_impersonationLevel - 1) << 16);
}
- int access = 0;
- if ((PipeDirection.In & _direction) != 0)
- {
- access |= Interop.Kernel32.GenericOperations.GENERIC_READ;
- }
- if ((PipeDirection.Out & _direction) != 0)
- {
- access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE;
- }
-
- SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access);
+ SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, _accessRights);
if (handle.IsInvalid)
{
@@ -81,7 +147,7 @@ private bool TryConnect(int timeout)
}
// Pipe server should be free. Let's try to connect to it.
- handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access);
+ handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, _accessRights);
if (handle.IsInvalid)
{
diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs
index 35681b35b89b0b..26db5533031718 100644
--- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs
+++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs
@@ -24,6 +24,7 @@ public sealed partial class NamedPipeClientStream : PipeStream
private readonly PipeOptions _pipeOptions;
private readonly HandleInheritability _inheritability;
private readonly PipeDirection _direction;
+ private readonly int _accessRights;
// Creates a named pipe client using default server (same machine, or "."), and PipeDirection.InOut
public NamedPipeClientStream(string pipeName)
@@ -84,6 +85,7 @@ public NamedPipeClientStream(string serverName, string pipeName, PipeDirection d
_inheritability = inheritability;
_impersonationLevel = impersonationLevel;
_pipeOptions = options;
+ _accessRights = AccessRightsFromDirection(direction);
}
// Create a NamedPipeClientStream from an existing server pipe handle.
diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Unix.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Unix.cs
new file mode 100644
index 00000000000000..15e84f2ffeff40
--- /dev/null
+++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Unix.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Security.Principal;
+using Microsoft.Win32.SafeHandles;
+using Xunit;
+
+namespace System.IO.Pipes.Tests
+{
+ ///
+ /// Unix-specific tests for the constructors for NamedPipeClientStream
+ ///
+ public partial class NamedPipeTest_CreateClient
+ {
+ [Fact]
+ public static void NotSupportedPipeAccessRights_Throws_PlatformNotSupportedException()
+ {
+ Assert.Throws(() => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
+ }
+
+ [Fact]
+ public static void NotSupportedPipePath_Throws_PlatformNotSupportedException()
+ {
+ string hostName;
+ Assert.True(InteropTest.TryGetHostName(out hostName));
+
+ Assert.Throws(() => new NamedPipeClientStream("foobar" + hostName, "foobar"));
+ Assert.Throws(() => new NamedPipeClientStream(hostName, "foobar" + Path.GetInvalidFileNameChars()[0]));
+ Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foo\0bar"));
+ Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foobar/"));
+ Assert.Throws(() => new NamedPipeClientStream(hostName, "/"));
+ Assert.Throws(() => new NamedPipeClientStream(hostName, "\0"));
+ }
+ }
+}
diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Windows.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Windows.cs
new file mode 100644
index 00000000000000..93fef211c8816d
--- /dev/null
+++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Windows.cs
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Security.Principal;
+using Microsoft.Win32.SafeHandles;
+using Xunit;
+
+namespace System.IO.Pipes.Tests
+{
+ ///
+ /// Windows-specific tests for the constructors for NamedPipeClientStream
+ ///
+ public partial class NamedPipeTest_CreateClient
+ {
+ [Fact]
+ public static void EmptyStringPipeName_Throws_ArgumentException_WithAccessRights()
+ {
+ AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
+ }
+
+ [Fact]
+ public static void NullServerName_Throws_ArgumentNullException_WithAccessRights()
+ {
+ AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
+ }
+
+ [Fact]
+ public static void EmptyStringServerName_Throws_ArgumentException_WithAccessRights()
+ {
+ AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
+ }
+
+ [Fact]
+ public static void ReservedPipeName_Throws_ArgumentOutOfRangeException_WithAccessRights()
+ {
+ AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "anonymous", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
+ }
+
+ [Theory]
+ [InlineData(0)] // No bits set
+ [InlineData(32)] // Invalid bit
+ [InlineData(32 + (int)PipeAccessRights.ReadData)] // ReadData plus an invalid bit
+ [InlineData(32 + (int)PipeAccessRights.WriteData)] // WriteData plus an invalid bit
+ [InlineData((int)PipeAccessRights.WriteAttributes)] // Missing ReadData and WriteData (no direction can be determined)
+ public static void InvalidPipeAccessRights_Throws_ArgumentOutOfRangeException(int rights)
+ {
+ AssertExtensions.Throws("desiredAccessRights", () => new NamedPipeClientStream(".", "client1", (PipeAccessRights)rights, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
+ }
+
+ [Fact]
+ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException_WithAccessRights()
+ {
+ AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, (PipeOptions)255, TokenImpersonationLevel.None, HandleInheritability.None));
+ }
+
+ [Fact]
+ public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException_WithAccessRights()
+ {
+ AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, (TokenImpersonationLevel)999, HandleInheritability.None));
+ }
+
+ [Fact]
+ public static void NamedPipeClientStream_InvalidHandleInerhitability_WithAccessRights()
+ {
+ AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1));
+ AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1));
+ }
+ }
+}
diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs
index 362ddd80be8812..5b164b849fac3c 100644
--- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs
+++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs
@@ -10,7 +10,7 @@ namespace System.IO.Pipes.Tests
///
/// Tests for the constructors for NamedPipeClientStream
///
- public class NamedPipeTest_CreateClient
+ public partial class NamedPipeTest_CreateClient
{
[Fact]
public static void NullPipeName_Throws_ArgumentNullException()
@@ -65,21 +65,6 @@ public static void ReservedPipeName_Throws_ArgumentOutOfRangeException(PipeDirec
AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None, TokenImpersonationLevel.Impersonation));
}
- [Fact]
- [PlatformSpecific(TestPlatforms.AnyUnix)] // Not supported pipe path throws PNSE on Unix
- public static void NotSupportedPipePath_Throws_PlatformNotSupportedException()
- {
- string hostName;
- Assert.True(InteropTest.TryGetHostName(out hostName));
-
- Assert.Throws(() => new NamedPipeClientStream("foobar" + hostName, "foobar"));
- Assert.Throws(() => new NamedPipeClientStream(hostName, "foobar" + Path.GetInvalidFileNameChars()[0]));
- Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foo\0bar"));
- Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foobar/"));
- Assert.Throws(() => new NamedPipeClientStream(hostName, "/"));
- Assert.Throws(() => new NamedPipeClientStream(hostName, "\0"));
- }
-
[Theory]
[InlineData((PipeDirection)123)]
public static void InvalidPipeDirection_Throws_ArgumentOutOfRangeException(PipeDirection direction)
@@ -155,7 +140,7 @@ public static void BadHandleKind_Throws_IOException(PipeDirection direction)
}
[Fact]
- public void NamedPipeClientStream_InvalidHandleInerhitability()
+ public static void NamedPipeClientStream_InvalidHandleInerhitability()
{
AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1));
AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1));
diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs
new file mode 100644
index 00000000000000..ff28c95855873e
--- /dev/null
+++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs
@@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.IO.Tests;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.IO.Pipes.Tests
+{
+ // Support for PipeAccessRights and setting ReadMode to Message is only supported on Windows
+ public class NamedPipeTest_MessageMode_Windows
+ {
+ private const PipeAccessRights MinimumMessageAccessRights = PipeAccessRights.ReadData | PipeAccessRights.WriteAttributes;
+
+ private static NamedPipeClientStream CreateClientStream(string pipeName, PipeOptions options) =>
+ new NamedPipeClientStream(".", pipeName, MinimumMessageAccessRights, options, Security.Principal.TokenImpersonationLevel.None, HandleInheritability.None);
+
+ [Theory]
+ [InlineData(PipeDirection.Out, PipeOptions.None)]
+ [InlineData(PipeDirection.InOut, PipeOptions.Asynchronous)]
+ public async Task Client_DetectsMessageCompleted(PipeDirection serverDirection, PipeOptions options)
+ {
+ string pipeName = PipeStreamConformanceTests.GetUniquePipeName();
+
+ using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, serverDirection, 1, PipeTransmissionMode.Message, options);
+ using NamedPipeClientStream client = CreateClientStream(pipeName, options);
+
+ Task.WaitAll(server.WaitForConnectionAsync(), client.ConnectAsync());
+ client.ReadMode = PipeTransmissionMode.Message;
+
+ ValueTask serverWrite = server.WriteAsync(new byte[] { 1, 2, 3, 4, 5 });
+
+ byte[] buffer1 = new byte[2], buffer2 = new byte[2], buffer3 = new byte[2];
+ bool[] messageCompleted = new bool[3];
+
+ int bytesRead = client.Read(buffer1, 0, 2);
+ messageCompleted[0] = client.IsMessageComplete;
+
+ bytesRead += client.Read(buffer2, 0, 2);
+ messageCompleted[1] = client.IsMessageComplete;
+
+ bytesRead += client.Read(buffer3, 0, 2);
+ messageCompleted[2] = client.IsMessageComplete;
+
+ Assert.Equal(5, bytesRead);
+ Assert.Equal(new byte[] { 1, 2, 3, 4, 5, 0 }, buffer1.Concat(buffer2).Concat(buffer3));
+ Assert.Equal(new bool[] { false, false, true }, messageCompleted);
+
+ await serverWrite;
+ }
+
+ [Theory]
+ [InlineData(PipeTransmissionMode.Byte, PipeOptions.None)]
+ [InlineData(PipeTransmissionMode.Message, PipeOptions.None)]
+ [InlineData(PipeTransmissionMode.Byte, PipeOptions.Asynchronous)]
+ [InlineData(PipeTransmissionMode.Message, PipeOptions.Asynchronous)]
+ public void ServerIn_ClientConnect_Throws(PipeTransmissionMode serverMode, PipeOptions options)
+ {
+ string pipeName = PipeStreamConformanceTests.GetUniquePipeName();
+
+ using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, serverMode, options);
+ using NamedPipeClientStream client = CreateClientStream(pipeName, options);
+
+ Assert.Throws(() => client.Connect());
+ }
+
+ [Theory]
+ [InlineData(PipeDirection.Out, PipeTransmissionMode.Byte, PipeOptions.None)]
+ [InlineData(PipeDirection.Out, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)]
+ [InlineData(PipeDirection.InOut, PipeTransmissionMode.Byte, PipeOptions.None)]
+ [InlineData(PipeDirection.InOut, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)]
+ public void ServerByteMode_ClientReadModeMessage_Throws(PipeDirection serverDirection, PipeTransmissionMode serverMode, PipeOptions options)
+ {
+ string pipeName = PipeStreamConformanceTests.GetUniquePipeName();
+
+ using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, serverDirection, 1, serverMode, options);
+ using NamedPipeClientStream client = CreateClientStream(pipeName, options);
+
+ Task.WaitAll(server.WaitForConnectionAsync(), client.ConnectAsync());
+
+ Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message);
+ }
+
+ [Fact]
+ public void PipeAccessRights_Without_WriteAttributes_ClientReadModeMessage_Throws()
+ {
+ string pipeName = PipeStreamConformanceTests.GetUniquePipeName();
+ PipeAccessRights rights = MinimumMessageAccessRights & ~PipeAccessRights.WriteAttributes;
+
+ using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message);
+ using NamedPipeClientStream client = new NamedPipeClientStream(".", pipeName, rights, PipeOptions.None, Security.Principal.TokenImpersonationLevel.None, HandleInheritability.None);
+
+ Task.WaitAll(server.WaitForConnectionAsync(), client.ConnectAsync());
+
+ Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message);
+ }
+ }
+}
diff --git a/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj b/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj
index ef930d859fe193..6d498058d8315b 100644
--- a/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj
+++ b/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj
@@ -25,7 +25,9 @@
+
+
@@ -36,6 +38,7 @@
+