Skip to content

Commit 5032dc5

Browse files
Improve NamedPipeClientStream.Connect edge cases handling (#65553)
* don't try to call WaitNamedPipeW if we know that no instances exist and the sys-call will quit immediately * remove code duplication Co-authored-by: Stephen Toub <[email protected]>
1 parent d46618c commit 5032dc5

File tree

1 file changed

+22
-23
lines changed

1 file changed

+22
-23
lines changed

src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,23 @@ private bool TryConnect(int timeout, CancellationToken cancellationToken)
4444
access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE;
4545
}
4646

47-
// Let's try to connect first
48-
SafePipeHandle handle = Interop.Kernel32.CreateNamedPipeClient(_normalizedPipePath,
49-
access, // read and write access
50-
0, // sharing: none
51-
ref secAttrs, // security attributes
52-
FileMode.Open, // open existing
53-
_pipeFlags, // impersonation flags
54-
IntPtr.Zero); // template file: null
47+
SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access);
5548

5649
if (handle.IsInvalid)
5750
{
5851
int errorCode = Marshal.GetLastPInvokeError();
5952

60-
if (errorCode != Interop.Errors.ERROR_PIPE_BUSY &&
61-
errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND)
53+
// CreateFileW: "If the CreateNamedPipe function was not successfully called on the server prior to this operation,
54+
// a pipe will not exist and CreateFile will fail with ERROR_FILE_NOT_FOUND"
55+
// WaitNamedPipeW: "If no instances of the specified named pipe exist,
56+
// the WaitNamedPipe function returns immediately, regardless of the time-out value."
57+
// We know that no instances exist, so we just quit without calling WaitNamedPipeW.
58+
if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND)
59+
{
60+
return false;
61+
}
62+
63+
if (errorCode != Interop.Errors.ERROR_PIPE_BUSY)
6264
{
6365
throw Win32Marshal.GetExceptionForWin32Error(errorCode);
6466
}
@@ -67,8 +69,7 @@ private bool TryConnect(int timeout, CancellationToken cancellationToken)
6769
{
6870
errorCode = Marshal.GetLastPInvokeError();
6971

70-
// Server is not yet created or a timeout occurred before a pipe instance was available.
71-
if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND ||
72+
if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND || // server has been closed
7273
errorCode == Interop.Errors.ERROR_SEM_TIMEOUT)
7374
{
7475
return false;
@@ -77,22 +78,17 @@ private bool TryConnect(int timeout, CancellationToken cancellationToken)
7778
throw Win32Marshal.GetExceptionForWin32Error(errorCode);
7879
}
7980

80-
// Pipe server should be free. Let's try to connect to it.
81-
handle = Interop.Kernel32.CreateNamedPipeClient(_normalizedPipePath,
82-
access, // read and write access
83-
0, // sharing: none
84-
ref secAttrs, // security attributes
85-
FileMode.Open, // open existing
86-
_pipeFlags, // impersonation flags
87-
IntPtr.Zero); // template file: null
81+
// Pipe server should be free. Let's try to connect to it.
82+
handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access);
8883

8984
if (handle.IsInvalid)
9085
{
9186
errorCode = Marshal.GetLastPInvokeError();
9287

93-
// Handle the possible race condition of someone else connecting to the server
94-
// between our calls to WaitNamedPipe & CreateFile.
95-
if (errorCode == Interop.Errors.ERROR_PIPE_BUSY)
88+
// WaitNamedPipe: "A subsequent CreateFile call to the pipe can fail,
89+
// because the instance was closed by the server or opened by another client."
90+
if (errorCode == Interop.Errors.ERROR_PIPE_BUSY || // opened by another client
91+
errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND) // server has been closed
9692
{
9793
return false;
9894
}
@@ -106,6 +102,9 @@ private bool TryConnect(int timeout, CancellationToken cancellationToken)
106102
State = PipeState.Connected;
107103
ValidateRemotePipeUser();
108104
return true;
105+
106+
static SafePipeHandle CreateNamedPipeClient(string? path, ref Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs, int pipeFlags, int access)
107+
=> Interop.Kernel32.CreateNamedPipeClient(path, access, FileShare.None, ref secAttrs, FileMode.Open, pipeFlags, hTemplateFile: IntPtr.Zero);
109108
}
110109

111110
[SupportedOSPlatform("windows")]

0 commit comments

Comments
 (0)