3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 /*============================================================
8 ** Classes: AnonymousPipeServerStream
9 ** AnonymousPipeClientStream
10 ** NamedPipeServerStream
11 ** NamedPipeClientStream
13 ** Purpose: pipe stream classes.
16 ===========================================================*/
19 using System
.Diagnostics
;
20 using System
.Diagnostics
.CodeAnalysis
;
22 using System
.Runtime
.InteropServices
;
23 using System
.Runtime
.CompilerServices
;
24 using System
.Runtime
.ConstrainedExecution
;
25 using System
.Runtime
.Versioning
;
26 using System
.Security
;
27 using System
.Security
.Permissions
;
28 using System
.Security
.Principal
;
30 using System
.Threading
;
31 using System
.Threading
.Tasks
;
32 using Microsoft
.Win32
;
33 using Microsoft
.Win32
.SafeHandles
;
35 namespace System
.IO
.Pipes
{
38 /// Anonymous pipe server stream
40 [System
.Security
.Permissions
.HostProtection(MayLeakOnAbort
= true)]
41 public sealed class AnonymousPipeServerStream
: PipeStream
{
43 private SafePipeHandle m_clientHandle
;
44 private bool m_clientHandleExposed
;
46 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
47 [SecuritySafeCritical
]
48 public AnonymousPipeServerStream()
49 : this(PipeDirection
.Out
, HandleInheritability
.None
, 0, null) { }
51 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
52 [SecuritySafeCritical
]
53 public AnonymousPipeServerStream(PipeDirection direction
)
54 : this(direction
, HandleInheritability
.None
, 0) { }
56 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
57 [SecuritySafeCritical
]
58 public AnonymousPipeServerStream(PipeDirection direction
, HandleInheritability inheritability
)
59 : this(direction
, inheritability
, 0) { }
61 // bufferSize is used as a suggestion; specify 0 to let OS decide
62 // This constructor instantiates the PipeSecurity using just the inheritability flag
63 [SecuritySafeCritical
]
64 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
65 public AnonymousPipeServerStream(PipeDirection direction
, HandleInheritability inheritability
, int bufferSize
)
66 : base(direction
, bufferSize
) {
67 if (direction
== PipeDirection
.InOut
) {
68 throw new NotSupportedException(SR
.GetString(SR
.NotSupported_AnonymousPipeUnidirectional
));
70 if (inheritability
< HandleInheritability
.None
|| inheritability
> HandleInheritability
.Inheritable
) {
71 throw new ArgumentOutOfRangeException("inheritability", SR
.GetString(SR
.ArgumentOutOfRange_HandleInheritabilityNoneOrInheritable
));
74 UnsafeNativeMethods
.SECURITY_ATTRIBUTES secAttrs
= PipeStream
.GetSecAttrs(inheritability
);
75 Create(direction
, secAttrs
, bufferSize
);
78 // bufferSize is used as a suggestion; specify 0 to let OS decide
79 // pipeSecurity of null is default security descriptor
80 [SecuritySafeCritical
]
81 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
82 public AnonymousPipeServerStream(PipeDirection direction
, HandleInheritability inheritability
, int bufferSize
, PipeSecurity pipeSecurity
)
83 : base(direction
, bufferSize
) {
84 if (direction
== PipeDirection
.InOut
) {
85 throw new NotSupportedException(SR
.GetString(SR
.NotSupported_AnonymousPipeUnidirectional
));
87 if (inheritability
< HandleInheritability
.None
|| inheritability
> HandleInheritability
.Inheritable
) {
88 throw new ArgumentOutOfRangeException("inheritability", SR
.GetString(SR
.ArgumentOutOfRange_HandleInheritabilityNoneOrInheritable
));
92 UnsafeNativeMethods
.SECURITY_ATTRIBUTES secAttrs
= PipeStream
.GetSecAttrs(inheritability
, pipeSecurity
, out pinningHandle
);
95 Create(direction
, secAttrs
, bufferSize
);
98 if (pinningHandle
!= null) {
99 GCHandle pinHandle
= (GCHandle
)pinningHandle
;
105 ~
AnonymousPipeServerStream() {
109 // Create an AnonymousPipeServerStream from two existing pipe handles.
110 [SecuritySafeCritical
]
111 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
112 public AnonymousPipeServerStream(PipeDirection direction
, SafePipeHandle serverSafePipeHandle
, SafePipeHandle clientSafePipeHandle
)
113 : base(direction
, 0) {
114 if (direction
== PipeDirection
.InOut
) {
115 throw new NotSupportedException(SR
.GetString(SR
.NotSupported_AnonymousPipeUnidirectional
));
117 if (serverSafePipeHandle
== null) {
118 throw new ArgumentNullException("serverSafePipeHandle");
120 if (clientSafePipeHandle
== null) {
121 throw new ArgumentNullException("clientSafePipeHandle");
123 if (serverSafePipeHandle
.IsInvalid
) {
124 throw new ArgumentException(SR
.GetString(SR
.Argument_InvalidHandle
), "serverSafePipeHandle");
126 if (clientSafePipeHandle
.IsInvalid
) {
127 throw new ArgumentException(SR
.GetString(SR
.Argument_InvalidHandle
), "clientSafePipeHandle");
130 // Check that these handles are in fact a handles to a pipe.
131 if (UnsafeNativeMethods
.GetFileType(serverSafePipeHandle
) != UnsafeNativeMethods
.FILE_TYPE_PIPE
) {
132 throw new IOException(SR
.GetString(SR
.IO_IO_InvalidPipeHandle
));
134 if (UnsafeNativeMethods
.GetFileType(clientSafePipeHandle
) != UnsafeNativeMethods
.FILE_TYPE_PIPE
) {
135 throw new IOException(SR
.GetString(SR
.IO_IO_InvalidPipeHandle
));
138 InitializeHandle(serverSafePipeHandle
, true, false);
140 m_clientHandle
= clientSafePipeHandle
;
141 m_clientHandleExposed
= true;
142 State
= PipeState
.Connected
;
145 // This method should exist until we add a first class way of passing handles between parent and child
146 // processes. For now, people do it via command line arguments.
147 [System
.Security
.SecurityCritical
]
148 [SuppressMessage("Microsoft.Reliability","CA2001:AvoidCallingProblematicMethods", MessageId
="System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Justification
="By design")]
149 public String
GetClientHandleAsString() {
150 m_clientHandleExposed
= true;
151 return m_clientHandle
.DangerousGetHandle().ToString();
154 public SafePipeHandle ClientSafePipeHandle
{
155 [System
.Security
.SecurityCritical
]
157 m_clientHandleExposed
= true;
158 return m_clientHandle
;
162 // This method is an annoying one but it has to exist at least until we make passing handles between
163 // processes first class. We need this because once the child handle is inherited, the OS considers
164 // the parent and child's handles to be different. Therefore, if a child closes its handle, our
165 // Read/Write methods won't throw because the OS will think that there is still a child handle around
166 // that can still Write/Read to/from the other end of the pipe.
168 // Ideally, we would want the Process class to close this handle after it has been inherited. See
169 // the pipe spec future features section for more information.
171 // Right now, this is the best signal to set the anonymous pipe as connected; if this is called, we
172 // know the client has been passed the handle and so the connection is live.
173 [System
.Security
.SecurityCritical
]
174 public void DisposeLocalCopyOfClientHandle() {
175 if (m_clientHandle
!= null && !m_clientHandle
.IsClosed
) {
176 m_clientHandle
.Dispose();
180 [System
.Security
.SecurityCritical
]
181 protected override void Dispose(bool disposing
) {
183 // We should dispose of the client handle if it was not exposed.
184 if (!m_clientHandleExposed
&& m_clientHandle
!= null && !m_clientHandle
.IsClosed
) {
185 m_clientHandle
.Dispose();
189 base.Dispose(disposing
);
193 // Creates the anonymous pipe.
194 [System
.Security
.SecurityCritical
]
195 private void Create(PipeDirection direction
, UnsafeNativeMethods
.SECURITY_ATTRIBUTES secAttrs
, int bufferSize
) {
196 Debug
.Assert(direction
!= PipeDirection
.InOut
, "Anonymous pipe direction shouldn't be InOut");
197 Debug
.Assert(bufferSize
>= 0, "bufferSize is negative");
200 SafePipeHandle serverHandle
;
201 SafePipeHandle newServerHandle
;
203 // Create the two pipe handles that make up the anonymous pipe.
204 if (direction
== PipeDirection
.In
) {
205 bSuccess
= UnsafeNativeMethods
.CreatePipe(out serverHandle
, out m_clientHandle
, secAttrs
, bufferSize
);
208 bSuccess
= UnsafeNativeMethods
.CreatePipe(out m_clientHandle
, out serverHandle
, secAttrs
, bufferSize
);
212 __Error
.WinIOError(Marshal
.GetLastWin32Error(), String
.Empty
);
215 // Duplicate the server handle to make it not inheritable. Note: We need to do this so that the child
216 // process doesn't end up getting another copy of the server handle. If it were to get a copy, the
217 // OS wouldn't be able to inform the child that the server has closed its handle because it will see
218 // that there is still one server handle that is open.
219 bSuccess
= UnsafeNativeMethods
.DuplicateHandle(UnsafeNativeMethods
.GetCurrentProcess(), serverHandle
, UnsafeNativeMethods
.GetCurrentProcess(),
220 out newServerHandle
, 0, false, UnsafeNativeMethods
.DUPLICATE_SAME_ACCESS
);
223 __Error
.WinIOError(Marshal
.GetLastWin32Error(), String
.Empty
);
226 // Close the inheritable server handle.
227 serverHandle
.Dispose();
229 InitializeHandle(newServerHandle
, false, false);
231 State
= PipeState
.Connected
;
234 // Anonymous pipes do not support message mode so there is no need to use the base version that P/Invokes here.
235 public override PipeTransmissionMode TransmissionMode
{
236 [System
.Security
.SecurityCritical
]
238 return PipeTransmissionMode
.Byte
;
242 public override PipeTransmissionMode ReadMode
{
243 [System
.Security
.SecurityCritical
]
245 CheckPipePropertyOperations();
247 if (value < PipeTransmissionMode
.Byte
|| value > PipeTransmissionMode
.Message
) {
248 throw new ArgumentOutOfRangeException("value", SR
.GetString(SR
.ArgumentOutOfRange_TransmissionModeByteOrMsg
));
251 if (value == PipeTransmissionMode
.Message
) {
252 throw new NotSupportedException(SR
.GetString(SR
.NotSupported_AnonymousPipeMessagesNotSupported
));
260 /// Anonymous pipe client. Use this to open the client end of an anonymous pipes created with AnonymousPipeServerStream.
262 [System
.Security
.Permissions
.HostProtection(MayLeakOnAbort
= true)]
263 public sealed class AnonymousPipeClientStream
: PipeStream
{
265 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
266 [SecuritySafeCritical
]
267 [SuppressMessage("Microsoft.Naming","CA1720:IdentifiersShouldNotContainTypeNames", MessageId
="string", Justification
="By design")]
268 public AnonymousPipeClientStream(String pipeHandleAsString
)
269 : this(PipeDirection
.In
, pipeHandleAsString
) { }
271 [System
.Security
.SecurityCritical
]
272 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
273 [SuppressMessage("Microsoft.Naming","CA1720:IdentifiersShouldNotContainTypeNames", MessageId
="string", Justification
="By design")]
274 public AnonymousPipeClientStream(PipeDirection direction
, String pipeHandleAsString
)
275 : base(direction
, 0) {
277 if (direction
== PipeDirection
.InOut
) {
278 throw new NotSupportedException(SR
.GetString(SR
.NotSupported_AnonymousPipeUnidirectional
));
280 if (pipeHandleAsString
== null) {
281 throw new ArgumentNullException("pipeHandleAsString");
284 // Initialize SafePipeHandle from String and check if it's valid. First see if it's parseable
286 bool parseable
= long.TryParse(pipeHandleAsString
, out result
);
288 throw new ArgumentException(SR
.GetString(SR
.Argument_InvalidHandle
), "pipeHandleAsString");
291 // next check whether the handle is invalid
292 SafePipeHandle safePipeHandle
= new SafePipeHandle((IntPtr
)result
, true);
293 if (safePipeHandle
.IsInvalid
) {
294 throw new ArgumentException(SR
.GetString(SR
.Argument_InvalidHandle
), "pipeHandleAsString");
297 Init(direction
, safePipeHandle
);
300 [System
.Security
.SecurityCritical
]
301 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
302 public AnonymousPipeClientStream(PipeDirection direction
, SafePipeHandle safePipeHandle
)
303 : base(direction
, 0) {
305 if (direction
== PipeDirection
.InOut
) {
306 throw new NotSupportedException(SR
.GetString(SR
.NotSupported_AnonymousPipeUnidirectional
));
308 if (safePipeHandle
== null) {
309 throw new ArgumentNullException("safePipeHandle");
311 if (safePipeHandle
.IsInvalid
) {
312 throw new ArgumentException(SR
.GetString(SR
.Argument_InvalidHandle
), "safePipeHandle");
315 Init(direction
, safePipeHandle
);
318 [System
.Security
.SecuritySafeCritical
]
319 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
320 private void Init(PipeDirection direction
, SafePipeHandle safePipeHandle
) {
321 Debug
.Assert(direction
!= PipeDirection
.InOut
, "anonymous pipes are unidirectional, caller should have verified before calling Init");
322 Debug
.Assert(safePipeHandle
!= null && !safePipeHandle
.IsInvalid
, "safePipeHandle must be valid");
324 // Check that this handle is infact a handle to a pipe.
325 if (UnsafeNativeMethods
.GetFileType(safePipeHandle
) != UnsafeNativeMethods
.FILE_TYPE_PIPE
) {
326 throw new IOException(SR
.GetString(SR
.IO_IO_InvalidPipeHandle
));
329 InitializeHandle(safePipeHandle
, true, false);
330 State
= PipeState
.Connected
;
334 ~
AnonymousPipeClientStream() {
338 // Anonymous pipes do not support message readmode so there is no need to use the base version
339 // which P/Invokes (and sometimes fails).
340 public override PipeTransmissionMode TransmissionMode
{
341 [System
.Security
.SecurityCritical
]
343 return PipeTransmissionMode
.Byte
;
347 public override PipeTransmissionMode ReadMode
{
348 [System
.Security
.SecurityCritical
]
350 CheckPipePropertyOperations();
352 if (value < PipeTransmissionMode
.Byte
|| value > PipeTransmissionMode
.Message
) {
353 throw new ArgumentOutOfRangeException("value", SR
.GetString(SR
.ArgumentOutOfRange_TransmissionModeByteOrMsg
));
355 if (value == PipeTransmissionMode
.Message
) {
356 throw new NotSupportedException(SR
.GetString(SR
.NotSupported_AnonymousPipeMessagesNotSupported
));
363 // Users will use this delegate to specify a method to call while impersonating the client
364 // (see NamedPipeServerStream.RunAsClient).
365 public delegate void PipeStreamImpersonationWorker();
370 /// Named pipe server
372 [System
.Security
.Permissions
.HostProtection(MayLeakOnAbort
= true)]
373 public sealed class NamedPipeServerStream
: PipeStream
{
375 // Use the maximum number of server instances that the system resources allow
376 public const int MaxAllowedServerInstances
= -1;
379 private unsafe static readonly IOCompletionCallback WaitForConnectionCallback
=
380 new IOCompletionCallback(NamedPipeServerStream
.AsyncWaitForConnectionCallback
);
382 [System
.Security
.SecurityCritical
]
383 static NamedPipeServerStream()
387 [SecuritySafeCritical
]
388 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
389 public NamedPipeServerStream(String pipeName
)
390 : this(pipeName
, PipeDirection
.InOut
, 1, PipeTransmissionMode
.Byte
, PipeOptions
.None
, 0, 0, null,
391 HandleInheritability
.None
, (PipeAccessRights
)0) { }
393 [SecuritySafeCritical
]
394 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
395 public NamedPipeServerStream(String pipeName
, PipeDirection direction
)
396 : this(pipeName
, direction
, 1, PipeTransmissionMode
.Byte
, PipeOptions
.None
, 0, 0, null,
397 HandleInheritability
.None
, (PipeAccessRights
)0) { }
399 [SecuritySafeCritical
]
400 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
401 public NamedPipeServerStream(String pipeName
, PipeDirection direction
, int maxNumberOfServerInstances
)
402 : this(pipeName
, direction
, maxNumberOfServerInstances
, PipeTransmissionMode
.Byte
, PipeOptions
.None
,
403 0, 0, null, HandleInheritability
.None
, (PipeAccessRights
)0) { }
405 [SecuritySafeCritical
]
406 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
407 public NamedPipeServerStream(String pipeName
, PipeDirection direction
, int maxNumberOfServerInstances
,
408 PipeTransmissionMode transmissionMode
)
409 : this(pipeName
, direction
, maxNumberOfServerInstances
, transmissionMode
, PipeOptions
.None
, 0, 0,
410 null, HandleInheritability
.None
, (PipeAccessRights
)0) { }
412 [SecuritySafeCritical
]
413 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
414 public NamedPipeServerStream(String pipeName
, PipeDirection direction
, int maxNumberOfServerInstances
,
415 PipeTransmissionMode transmissionMode
, PipeOptions options
)
416 : this(pipeName
, direction
, maxNumberOfServerInstances
, transmissionMode
, options
, 0, 0,
417 null, HandleInheritability
.None
, (PipeAccessRights
)0) { }
419 [SecuritySafeCritical
]
420 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
421 public NamedPipeServerStream(String pipeName
, PipeDirection direction
, int maxNumberOfServerInstances
,
422 PipeTransmissionMode transmissionMode
, PipeOptions options
, int inBufferSize
, int outBufferSize
)
423 : this(pipeName
, direction
, maxNumberOfServerInstances
, transmissionMode
, options
, inBufferSize
, outBufferSize
,
424 null, HandleInheritability
.None
, (PipeAccessRights
)0) { }
426 [SecuritySafeCritical
]
427 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
428 public NamedPipeServerStream(String pipeName
, PipeDirection direction
, int maxNumberOfServerInstances
,
429 PipeTransmissionMode transmissionMode
, PipeOptions options
, int inBufferSize
, int outBufferSize
,
430 PipeSecurity pipeSecurity
)
431 : this(pipeName
, direction
, maxNumberOfServerInstances
, transmissionMode
, options
, inBufferSize
, outBufferSize
,
432 pipeSecurity
, HandleInheritability
.None
, (PipeAccessRights
)0) { }
434 [SecuritySafeCritical
]
435 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
436 public NamedPipeServerStream(String pipeName
, PipeDirection direction
, int maxNumberOfServerInstances
,
437 PipeTransmissionMode transmissionMode
, PipeOptions options
, int inBufferSize
, int outBufferSize
,
438 PipeSecurity pipeSecurity
, HandleInheritability inheritability
)
439 : this(pipeName
, direction
, maxNumberOfServerInstances
, transmissionMode
, options
, inBufferSize
, outBufferSize
,
440 pipeSecurity
, inheritability
, (PipeAccessRights
)0) { }
443 /// Full named pipe server constructor
445 /// <param name="pipeName">Pipe name</param>
446 /// <param name="direction">Pipe direction: In, Out or InOut (duplex).
447 /// Win32 note: this gets OR'd into dwOpenMode to CreateNamedPipe
449 /// <param name="maxNumberOfServerInstances">Maximum number of server instances. Specify a fixed value between
450 /// 1 and 254, or use NamedPipeServerStream.MaxAllowedServerInstances to use the maximum amount allowed by
451 /// system resources.</param>
452 /// <param name="transmissionMode">Byte mode or message mode.
453 /// Win32 note: this gets used for dwPipeMode. CreateNamedPipe allows you to specify PIPE_TYPE_BYTE/MESSAGE
454 /// and PIPE_READMODE_BYTE/MESSAGE independently, but this sets type and readmode to match.
456 /// <param name="options">PipeOption enum: None, Asynchronous, or Writethrough
457 /// Win32 note: this gets passed in with dwOpenMode to CreateNamedPipe. Asynchronous corresponds to
458 /// FILE_FLAG_OVERLAPPED option. PipeOptions enum doesn't expose FIRST_PIPE_INSTANCE option because
459 /// this sets that automatically based on the number of instances specified.
461 /// <param name="inBufferSize">Incoming buffer size, 0 or higher.
462 /// Note: this size is always advisory; OS uses a suggestion.
464 /// <param name="outBufferSize">Outgoing buffer size, 0 or higher (see above)</param>
465 /// <param name="pipeSecurity">PipeSecurity, or null for default security descriptor</param>
466 /// <param name="inheritability">Whether handle is inheritable</param>
467 /// <param name="additionalAccessRights">Combination (logical OR) of PipeAccessRights.TakeOwnership,
468 /// PipeAccessRights.AccessSystemSecurity, and PipeAccessRights.ChangePermissions</param>
469 [SecuritySafeCritical
]
470 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
471 public NamedPipeServerStream(String pipeName
, PipeDirection direction
, int maxNumberOfServerInstances
,
472 PipeTransmissionMode transmissionMode
, PipeOptions options
, int inBufferSize
, int outBufferSize
,
473 PipeSecurity pipeSecurity
, HandleInheritability inheritability
, PipeAccessRights additionalAccessRights
)
474 : base(direction
, transmissionMode
, outBufferSize
) {
475 if (pipeName
== null) {
476 throw new ArgumentNullException("pipeName");
478 if (pipeName
.Length
== 0) {
479 throw new ArgumentException(SR
.GetString(SR
.Argument_NeedNonemptyPipeName
));
481 if ((options
& ~
(PipeOptions
.WriteThrough
| PipeOptions
.Asynchronous
)) != 0) {
482 throw new ArgumentOutOfRangeException("options", SR
.GetString(SR
.ArgumentOutOfRange_OptionsInvalid
));
484 if (inBufferSize
< 0) {
485 throw new ArgumentOutOfRangeException("inBufferSize", SR
.GetString(SR
.ArgumentOutOfRange_NeedNonNegNum
));
487 // win32 allows fixed values of 1-254 or 255 to mean max allowed by system. We expose 255 as -1 (unlimited)
488 // through the MaxAllowedServerInstances constant. This is consistent e.g. with -1 as infinite timeout, etc
489 if ((maxNumberOfServerInstances
< 1 || maxNumberOfServerInstances
> 254) && (maxNumberOfServerInstances
!= MaxAllowedServerInstances
)) {
490 throw new ArgumentOutOfRangeException("maxNumberOfServerInstances", SR
.GetString(SR
.ArgumentOutOfRange_MaxNumServerInstances
));
492 if (inheritability
< HandleInheritability
.None
|| inheritability
> HandleInheritability
.Inheritable
) {
493 throw new ArgumentOutOfRangeException("inheritability", SR
.GetString(SR
.ArgumentOutOfRange_HandleInheritabilityNoneOrInheritable
));
495 // ChangePermissions, TakeOwnership, and AccessSystemSecurity are only legal values user may provide;
496 // internally this is set to 0 if not provided. This handles both cases.
497 if ((additionalAccessRights
& ~
(PipeAccessRights
.ChangePermissions
| PipeAccessRights
.TakeOwnership
|
498 PipeAccessRights
.AccessSystemSecurity
)) != 0) {
499 throw new ArgumentOutOfRangeException("additionalAccessRights", SR
.GetString(SR
.ArgumentOutOfRange_AdditionalAccessLimited
));
502 // Named Pipe Servers require Windows NT
503 if (Environment
.OSVersion
.Platform
== PlatformID
.Win32Windows
) {
504 throw new PlatformNotSupportedException(SR
.GetString(SR
.PlatformNotSupported_NamedPipeServers
));
507 string normalizedPipePath
= Path
.GetFullPath(@"\\.\pipe\" + pipeName
);
509 // Make sure the pipe name isn't one of our reserved names for anonymous pipes.
510 if (String
.Compare(normalizedPipePath
, @"\\.\pipe\anonymous", StringComparison
.OrdinalIgnoreCase
) == 0) {
511 throw new ArgumentOutOfRangeException("pipeName", SR
.GetString(SR
.ArgumentOutOfRange_AnonymousReserved
));
514 Object pinningHandle
= null;
515 UnsafeNativeMethods
.SECURITY_ATTRIBUTES secAttrs
= PipeStream
.GetSecAttrs(inheritability
, pipeSecurity
, out pinningHandle
);
518 Create(normalizedPipePath
, direction
, maxNumberOfServerInstances
, transmissionMode
,
519 options
, inBufferSize
, outBufferSize
, additionalAccessRights
, secAttrs
);
522 if (pinningHandle
!= null) {
523 GCHandle pinHandle
= (GCHandle
)pinningHandle
;
529 // Create a NamedPipeServerStream from an existing server pipe handle.
530 [System
.Security
.SecuritySafeCritical
]
531 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
532 public NamedPipeServerStream(PipeDirection direction
, bool isAsync
, bool isConnected
, SafePipeHandle safePipeHandle
)
533 : base(direction
, PipeTransmissionMode
.Byte
, 0) {
535 if (safePipeHandle
== null) {
536 throw new ArgumentNullException("safePipeHandle");
538 if (safePipeHandle
.IsInvalid
) {
539 throw new ArgumentException(SR
.GetString(SR
.Argument_InvalidHandle
), "safePipeHandle");
541 // Check that this handle is infact a handle to a pipe.
542 if (UnsafeNativeMethods
.GetFileType(safePipeHandle
) != UnsafeNativeMethods
.FILE_TYPE_PIPE
) {
543 throw new IOException(SR
.GetString(SR
.IO_IO_InvalidPipeHandle
));
546 InitializeHandle(safePipeHandle
, true, isAsync
);
549 State
= PipeState
.Connected
;
553 ~
NamedPipeServerStream() {
557 [System
.Security
.SecurityCritical
]
558 private void Create(String fullPipeName
, PipeDirection direction
, int maxNumberOfServerInstances
,
559 PipeTransmissionMode transmissionMode
, PipeOptions options
, int inBufferSize
, int outBufferSize
,
560 PipeAccessRights rights
, UnsafeNativeMethods
.SECURITY_ATTRIBUTES secAttrs
) {
561 Debug
.Assert(fullPipeName
!= null && fullPipeName
.Length
!= 0, "fullPipeName is null or empty");
562 Debug
.Assert(direction
>= PipeDirection
.In
&& direction
<= PipeDirection
.InOut
, "invalid pipe direction");
563 Debug
.Assert(inBufferSize
>= 0, "inBufferSize is negative");
564 Debug
.Assert(outBufferSize
>= 0, "outBufferSize is negative");
565 Debug
.Assert((maxNumberOfServerInstances
>= 1 && maxNumberOfServerInstances
<= 254) || (maxNumberOfServerInstances
== MaxAllowedServerInstances
), "maxNumberOfServerInstances is invalid");
566 Debug
.Assert(transmissionMode
>= PipeTransmissionMode
.Byte
&& transmissionMode
<= PipeTransmissionMode
.Message
, "transmissionMode is out of range");
568 int openMode
= ((int)direction
) |
569 (maxNumberOfServerInstances
== 1 ? UnsafeNativeMethods
.FILE_FLAG_FIRST_PIPE_INSTANCE
: 0) |
573 // We automatically set the ReadMode to match the TransmissionMode.
574 int pipeModes
= (int)transmissionMode
<< 2 | (int)transmissionMode
<< 1;
576 // Convert -1 to 255 to match win32 (we asserted that it is between -1 and 254).
577 if (maxNumberOfServerInstances
== MaxAllowedServerInstances
) {
578 maxNumberOfServerInstances
= 255;
581 SafePipeHandle handle
= UnsafeNativeMethods
.CreateNamedPipe(fullPipeName
, openMode
, pipeModes
,
582 maxNumberOfServerInstances
, outBufferSize
, inBufferSize
, 0, secAttrs
);
584 if (handle
.IsInvalid
) {
585 __Error
.WinIOError(Marshal
.GetLastWin32Error(), String
.Empty
);
588 InitializeHandle(handle
, false, (options
& PipeOptions
.Asynchronous
) != 0);
591 // This will wait until the client calls Connect(). If we return from this method, we guarantee that
592 // the client has returned from its Connect call. The client may have done so before this method
593 // was called (but not before this server is been created, or, if we were servicing another client,
594 // not before we called Disconnect), in which case, there may be some buffer already in the pipe waiting
595 // for us to read. See NamedPipeClientStream.Connect for more information.
596 [System
.Security
.SecurityCritical
]
597 [SuppressMessage("Microsoft.Security","CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification
="Security model of pipes: demand at creation but no subsequent demands")]
598 public void WaitForConnection() {
599 CheckConnectOperationsServer();
602 IAsyncResult result
= BeginWaitForConnection(null, null);
603 EndWaitForConnection(result
);
606 if (!UnsafeNativeMethods
.ConnectNamedPipe(InternalHandle
, UnsafeNativeMethods
.NULL
)) {
607 int errorCode
= Marshal
.GetLastWin32Error();
609 if (errorCode
!= UnsafeNativeMethods
.ERROR_PIPE_CONNECTED
) {
610 __Error
.WinIOError(errorCode
, String
.Empty
);
613 // pipe already connected
614 if (errorCode
== UnsafeNativeMethods
.ERROR_PIPE_CONNECTED
&& State
== PipeState
.Connected
) {
615 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeAlreadyConnected
));
618 // If we reach here then a connection has been established. This can happen if a client
619 // connects in the interval between the call to CreateNamedPipe and the call to ConnectNamedPipe.
620 // In this situation, there is still a good connection between client and server, even though
621 // ConnectNamedPipe returns zero.
623 State
= PipeState
.Connected
;
627 public Task
WaitForConnectionAsync(CancellationToken cancellationToken
) {
628 if (cancellationToken
.IsCancellationRequested
) {
629 return Task
.FromCancellation(cancellationToken
);
633 return Task
.Factory
.StartNew(WaitForConnection
, cancellationToken
);
636 // Avoiding allocation if the task cannot be cancelled
637 IOCancellationHelper cancellationHelper
= cancellationToken
.CanBeCanceled
? new IOCancellationHelper(cancellationToken
) : null;
638 return Task
.Factory
.FromAsync(BeginWaitForConnection
, EndWaitForConnection
, cancellationHelper
);
641 public Task
WaitForConnectionAsync() {
642 return WaitForConnectionAsync(CancellationToken
.None
);
645 // Async version of WaitForConnection. See the comments above for more info.
646 [System
.Security
.SecurityCritical
]
647 [HostProtection(ExternalThreading
= true)]
648 public unsafe IAsyncResult
BeginWaitForConnection(AsyncCallback callback
, Object state
) {
649 CheckConnectOperationsServer();
652 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeNotAsync
));
655 // Create and store async stream class library specific data in the
657 PipeAsyncResult asyncResult
= new PipeAsyncResult();
658 asyncResult
._handle
= InternalHandle
;
659 asyncResult
._userCallback
= callback
;
660 asyncResult
._userStateObject
= state
;
662 IOCancellationHelper cancellationHelper
= state
as IOCancellationHelper
;
664 // Create wait handle and store in async result
665 ManualResetEvent waitHandle
= new ManualResetEvent(false);
666 asyncResult
._waitHandle
= waitHandle
;
668 // Create a managed overlapped class
669 // We will set the file offsets later
670 Overlapped overlapped
= new Overlapped(0, 0, IntPtr
.Zero
, asyncResult
);
672 // Pack the Overlapped class, and store it in the async result
673 NativeOverlapped
* intOverlapped
= overlapped
.Pack(WaitForConnectionCallback
, null);
674 asyncResult
._overlapped
= intOverlapped
;
676 if (!UnsafeNativeMethods
.ConnectNamedPipe(InternalHandle
, intOverlapped
)) {
677 int errorCode
= Marshal
.GetLastWin32Error();
679 if (errorCode
== UnsafeNativeMethods
.ERROR_IO_PENDING
) {
680 if (cancellationHelper
!= null) {
681 cancellationHelper
.AllowCancellation(InternalHandle
, intOverlapped
);
686 // WaitForConnectionCallback will not be called becasue we completed synchronously.
687 // Either the pipe is already connected, or there was an error. Unpin and free the overlapped again.
688 Overlapped
.Free(intOverlapped
);
689 asyncResult
._overlapped
= null;
691 // Did the client already connect to us?
692 if (errorCode
== UnsafeNativeMethods
.ERROR_PIPE_CONNECTED
) {
694 if (State
== PipeState
.Connected
) {
695 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeAlreadyConnected
));
697 asyncResult
.CallUserCallback();
701 __Error
.WinIOError(errorCode
, String
.Empty
);
703 // will set state to Connected when EndWait is called
704 if (cancellationHelper
!= null) {
705 cancellationHelper
.AllowCancellation(InternalHandle
, intOverlapped
);
711 // Async version of WaitForConnection. See comments for WaitForConnection for more info.
712 [System
.Security
.SecurityCritical
]
713 public unsafe void EndWaitForConnection(IAsyncResult asyncResult
) {
714 CheckConnectOperationsServer();
716 if (asyncResult
== null) {
717 throw new ArgumentNullException("asyncResult");
720 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeNotAsync
));
723 PipeAsyncResult afsar
= asyncResult
as PipeAsyncResult
;
725 __Error
.WrongAsyncResult();
728 // Ensure we can't get into any ----s by doing an interlocked
729 // CompareExchange here. Avoids corrupting memory via freeing the
730 // NativeOverlapped class or GCHandle twice. --
731 if (1 == Interlocked
.CompareExchange(ref afsar
._EndXxxCalled
, 1, 0)) {
732 __Error
.EndWaitForConnectionCalledTwice();
735 IOCancellationHelper cancellationHelper
= afsar
.AsyncState
as IOCancellationHelper
;
736 if (cancellationHelper
!= null) {
737 cancellationHelper
.SetOperationCompleted();
740 // Obtain the WaitHandle, but don't use public property in case we
741 // delay initialize the manual reset event in the future.
742 WaitHandle wh
= afsar
._waitHandle
;
744 // We must block to ensure that ConnectionIOCallback has completed,
745 // and we should close the WaitHandle in here. AsyncFSCallback
746 // and the hand-ported imitation version in COMThreadPool.cpp
747 // are the only places that set this event.
750 Debug
.Assert(afsar
._isComplete
== true, "NamedPipeServerStream::EndWaitForConnection - AsyncFSCallback didn't set _isComplete to true!");
757 // We should have freed the overlapped and set it to null either in the Begin
758 // method (if ConnectNamedPipe completed synchronously) or in AsyncWaitForConnectionCallback.
759 // If it is not nulled out, we should not be past the above wait:
760 Debug
.Assert(afsar
._overlapped
== null);
762 // Now check for any error during the read.
763 if (afsar
._errorCode
!= 0) {
764 if (afsar
._errorCode
== UnsafeNativeMethods
.ERROR_OPERATION_ABORTED
) {
765 if (cancellationHelper
!= null) {
766 cancellationHelper
.ThrowIOOperationAborted();
769 __Error
.WinIOError(afsar
._errorCode
, String
.Empty
);
773 State
= PipeState
.Connected
;
776 [System
.Security
.SecurityCritical
]
777 public void Disconnect() {
778 CheckDisconnectOperations();
780 // Disconnect the pipe.
781 if (!UnsafeNativeMethods
.DisconnectNamedPipe(InternalHandle
)) {
782 __Error
.WinIOError(Marshal
.GetLastWin32Error(), String
.Empty
);
785 State
= PipeState
.Disconnected
;
789 // This method calls a delegate while impersonating the client. Note that we will not have
790 // access to the client's security token until it has written at least once to the pipe
791 // (and has set its impersonationLevel argument appropriately).
792 [System
.Security
.SecurityCritical
]
793 [SecurityPermissionAttribute(SecurityAction
.Demand
, Flags
= SecurityPermissionFlag
.ControlPrincipal
)]
794 public void RunAsClient(PipeStreamImpersonationWorker impersonationWorker
) {
795 CheckWriteOperations();
796 ExecuteHelper execHelper
= new ExecuteHelper(impersonationWorker
, InternalHandle
);
797 RuntimeHelpers
.ExecuteCodeWithGuaranteedCleanup(tryCode
, cleanupCode
, execHelper
);
799 // now handle win32 impersonate/revert specific errors by throwing corresponding exceptions
800 if (execHelper
.m_impersonateErrorCode
!= 0) {
801 WinIOError(execHelper
.m_impersonateErrorCode
);
803 else if (execHelper
.m_revertImpersonateErrorCode
!= 0) {
804 WinIOError(execHelper
.m_revertImpersonateErrorCode
);
808 // the following are needed for CER
810 private static RuntimeHelpers
.TryCode tryCode
= new RuntimeHelpers
.TryCode(ImpersonateAndTryCode
);
811 private static RuntimeHelpers
.CleanupCode cleanupCode
= new RuntimeHelpers
.CleanupCode(RevertImpersonationOnBackout
);
813 [System
.Security
.SecurityCritical
]
814 private static void ImpersonateAndTryCode(Object helper
) {
815 ExecuteHelper execHelper
= (ExecuteHelper
)helper
;
817 RuntimeHelpers
.PrepareConstrainedRegions();
820 if (UnsafeNativeMethods
.ImpersonateNamedPipeClient(execHelper
.m_handle
)) {
821 execHelper
.m_mustRevert
= true;
824 execHelper
.m_impersonateErrorCode
= Marshal
.GetLastWin32Error();
829 if (execHelper
.m_mustRevert
) { // impersonate passed so run user code
830 execHelper
.m_userCode();
834 [System
.Security
.SecurityCritical
]
836 private static void RevertImpersonationOnBackout(Object helper
, bool exceptionThrown
) {
837 ExecuteHelper execHelper
= (ExecuteHelper
)helper
;
839 if (execHelper
.m_mustRevert
) {
840 if (!UnsafeNativeMethods
.RevertToSelf()) {
841 execHelper
.m_revertImpersonateErrorCode
= Marshal
.GetLastWin32Error();
846 internal class ExecuteHelper
{
847 internal PipeStreamImpersonationWorker m_userCode
;
848 internal SafePipeHandle m_handle
;
849 internal bool m_mustRevert
;
850 internal int m_impersonateErrorCode
;
851 internal int m_revertImpersonateErrorCode
;
853 [System
.Security
.SecurityCritical
]
854 internal ExecuteHelper(PipeStreamImpersonationWorker userCode
, SafePipeHandle handle
) {
855 m_userCode
= userCode
;
861 // Gets the username of the connected client. Not that we will not have access to the client's
862 // username until it has written at least once to the pipe (and has set its impersonationLevel
863 // argument appropriately).
864 [System
.Security
.SecurityCritical
]
865 [SecurityPermissionAttribute(SecurityAction
.Demand
, Flags
= SecurityPermissionFlag
.ControlPrincipal
)]
866 public String
GetImpersonationUserName() {
867 CheckWriteOperations();
869 StringBuilder userName
= new StringBuilder(UnsafeNativeMethods
.CREDUI_MAX_USERNAME_LENGTH
+ 1);
871 if (!UnsafeNativeMethods
.GetNamedPipeHandleState(InternalHandle
, UnsafeNativeMethods
.NULL
, UnsafeNativeMethods
.NULL
,
872 UnsafeNativeMethods
.NULL
, UnsafeNativeMethods
.NULL
, userName
, userName
.Capacity
)) {
873 WinIOError(Marshal
.GetLastWin32Error());
876 return userName
.ToString();
879 // Callback to be called by the OS when completing the async WaitForConnection operation.
880 [System
.Security
.SecurityCritical
]
881 unsafe private static void AsyncWaitForConnectionCallback(uint errorCode
, uint numBytes
, NativeOverlapped
* pOverlapped
) {
883 Overlapped overlapped
= Overlapped
.Unpack(pOverlapped
);
885 // Extract async result from overlapped
886 PipeAsyncResult asyncResult
= (PipeAsyncResult
)overlapped
.AsyncResult
;
888 // Free the pinned overlapped:
889 Debug
.Assert(asyncResult
._overlapped
== pOverlapped
);
890 Overlapped
.Free(pOverlapped
);
891 asyncResult
._overlapped
= null;
893 // Special case for when the client has already connected to us.
894 if (errorCode
== UnsafeNativeMethods
.ERROR_PIPE_CONNECTED
) {
898 asyncResult
._errorCode
= (int)errorCode
;
900 // Call the user-provided callback. It can and often should
901 // call EndWaitForConnection. There's no reason to use an async
902 // delegate here - we're already on a threadpool thread.
903 // IAsyncResult's completedSynchronously property must return
904 // false here, saying the user callback was called on another thread.
905 asyncResult
._completedSynchronously
= false;
906 asyncResult
._isComplete
= true;
908 // The OS does not signal this event. We must do it ourselves.
909 ManualResetEvent wh
= asyncResult
._waitHandle
;
911 Debug
.Assert(!wh
.SafeWaitHandle
.IsClosed
, "ManualResetEvent already closed!");
913 Debug
.Assert(r
, "ManualResetEvent::Set failed!");
915 __Error
.WinIOError();
919 AsyncCallback userCallback
= asyncResult
._userCallback
;
921 if (userCallback
!= null) {
922 userCallback(asyncResult
);
926 // Server can only connect from Disconnected state
927 [System
.Security
.SecurityCritical
]
928 [SuppressMessage("Microsoft.Security","CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification
="Consistent with security model")]
929 private void CheckConnectOperationsServer() {
930 // we're not checking whether already connected; this allows us to throw IOException
931 // "pipe is being closed" if other side is closing (as does win32) or no-op if
934 if (InternalHandle
== null) {
935 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeHandleNotSet
));
938 if (State
== PipeState
.Closed
) {
939 __Error
.PipeNotOpen();
941 if (InternalHandle
.IsClosed
) {
942 __Error
.PipeNotOpen();
945 if (State
== PipeState
.Broken
) {
946 throw new IOException(SR
.GetString(SR
.IO_IO_PipeBroken
));
950 // Server is allowed to disconnect from connected and broken states
951 [System
.Security
.SecurityCritical
]
952 private void CheckDisconnectOperations() {
955 if (State
== PipeState
.WaitingToConnect
) {
956 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeNotYetConnected
));
958 if (State
== PipeState
.Disconnected
) {
959 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeAlreadyDisconnected
));
961 if (InternalHandle
== null) {
962 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeHandleNotSet
));
965 if (State
== PipeState
.Closed
) {
966 __Error
.PipeNotOpen();
968 if (InternalHandle
.IsClosed
) {
969 __Error
.PipeNotOpen();
975 // Named pipe client. Use this to open the client end of a named pipes created with
976 // NamedPipeServerStream.
977 [System
.Security
.Permissions
.HostProtection(MayLeakOnAbort
= true)]
978 public sealed class NamedPipeClientStream
: PipeStream
{
980 // Maximum interval in miliseconds between which cancellation is checked.
981 // Used by ConnectInternal. 50ms is fairly responsive time but really long time for processor.
982 private const int CancellationCheckIntervalInMilliseconds
= 50;
983 private string m_normalizedPipePath
;
984 private TokenImpersonationLevel m_impersonationLevel
;
985 private PipeOptions m_pipeOptions
;
986 private HandleInheritability m_inheritability
;
987 private int m_access
;
989 // Creates a named pipe client using default server (same machine, or "."), and PipeDirection.InOut
990 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
991 [SecuritySafeCritical
]
992 public NamedPipeClientStream(String pipeName
)
993 : this(".", pipeName
, PipeDirection
.InOut
, PipeOptions
.None
, TokenImpersonationLevel
.None
, HandleInheritability
.None
) { }
995 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
996 [SecuritySafeCritical
]
997 public NamedPipeClientStream(String serverName
, String pipeName
)
998 : this(serverName
, pipeName
, PipeDirection
.InOut
, PipeOptions
.None
, TokenImpersonationLevel
.None
, HandleInheritability
.None
) { }
1000 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
1001 [SecuritySafeCritical
]
1002 public NamedPipeClientStream(String serverName
, String pipeName
, PipeDirection direction
)
1003 : this(serverName
, pipeName
, direction
, PipeOptions
.None
, TokenImpersonationLevel
.None
, HandleInheritability
.None
) { }
1005 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
1006 [SecuritySafeCritical
]
1007 public NamedPipeClientStream(String serverName
, String pipeName
, PipeDirection direction
,
1008 PipeOptions options
)
1009 : this(serverName
, pipeName
, direction
, options
, TokenImpersonationLevel
.None
, HandleInheritability
.None
) { }
1011 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
1012 [SecuritySafeCritical
]
1013 public NamedPipeClientStream(String serverName
, String pipeName
, PipeDirection direction
,
1014 PipeOptions options
, TokenImpersonationLevel impersonationLevel
)
1015 : this(serverName
, pipeName
, direction
, options
, impersonationLevel
, HandleInheritability
.None
) { }
1017 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
1018 [SecuritySafeCritical
]
1019 public NamedPipeClientStream(String serverName
, String pipeName
, PipeDirection direction
,
1020 PipeOptions options
, TokenImpersonationLevel impersonationLevel
, HandleInheritability inheritability
)
1021 : base(direction
, 0) {
1023 if (pipeName
== null) {
1024 throw new ArgumentNullException("pipeName");
1026 if (serverName
== null) {
1027 throw new ArgumentNullException("serverName", SR
.GetString(SR
.ArgumentNull_ServerName
));
1029 if (pipeName
.Length
== 0) {
1030 throw new ArgumentException(SR
.GetString(SR
.Argument_NeedNonemptyPipeName
));
1032 if (serverName
.Length
== 0) {
1033 throw new ArgumentException(SR
.GetString(SR
.Argument_EmptyServerName
));
1035 if ((options
& ~
(PipeOptions
.WriteThrough
| PipeOptions
.Asynchronous
)) != 0) {
1036 throw new ArgumentOutOfRangeException("options", SR
.GetString(SR
.ArgumentOutOfRange_OptionsInvalid
));
1038 if (impersonationLevel
< TokenImpersonationLevel
.None
|| impersonationLevel
> TokenImpersonationLevel
.Delegation
) {
1039 throw new ArgumentOutOfRangeException("impersonationLevel", SR
.GetString(SR
.ArgumentOutOfRange_ImpersonationInvalid
));
1041 if (inheritability
< HandleInheritability
.None
|| inheritability
> HandleInheritability
.Inheritable
) {
1042 throw new ArgumentOutOfRangeException("inheritability", SR
.GetString(SR
.ArgumentOutOfRange_HandleInheritabilityNoneOrInheritable
));
1045 m_normalizedPipePath
= Path
.GetFullPath(@"\\" + serverName
+ @"\pipe\" + pipeName
);
1047 if (String
.Compare(m_normalizedPipePath
, @"\\.\pipe\anonymous", StringComparison
.OrdinalIgnoreCase
) == 0) {
1048 throw new ArgumentOutOfRangeException("pipeName", SR
.GetString(SR
.ArgumentOutOfRange_AnonymousReserved
));
1051 m_inheritability
= inheritability
;
1052 m_impersonationLevel
= impersonationLevel
;
1053 m_pipeOptions
= options
;
1055 if ((PipeDirection
.In
& direction
) != 0) {
1056 m_access
|= UnsafeNativeMethods
.GENERIC_READ
;
1058 if ((PipeDirection
.Out
& direction
) != 0) {
1059 m_access
|= UnsafeNativeMethods
.GENERIC_WRITE
;
1063 // This constructor is for advanced users that want to specify their PipeAccessRights explcitly. It can be used
1064 // to open pipes with, for example, WritePermissions access in the case that they want to play with the pipe's
1066 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
1067 [SecuritySafeCritical
]
1068 public NamedPipeClientStream(String serverName
, String pipeName
, PipeAccessRights desiredAccessRights
,
1069 PipeOptions options
, TokenImpersonationLevel impersonationLevel
, HandleInheritability inheritability
)
1070 : base(DirectionFromRights(desiredAccessRights
), 0) {
1072 if (pipeName
== null) {
1073 throw new ArgumentNullException("pipeName");
1075 if (serverName
== null) {
1076 throw new ArgumentNullException("serverName", SR
.GetString(SR
.ArgumentNull_ServerName
));
1078 if (pipeName
.Length
== 0) {
1079 throw new ArgumentException(SR
.GetString(SR
.Argument_NeedNonemptyPipeName
));
1081 if (serverName
.Length
== 0) {
1082 throw new ArgumentException(SR
.GetString(SR
.Argument_EmptyServerName
));
1084 if ((options
& ~
(PipeOptions
.WriteThrough
| PipeOptions
.Asynchronous
)) != 0) {
1085 throw new ArgumentOutOfRangeException("options", SR
.GetString(SR
.ArgumentOutOfRange_OptionsInvalid
));
1087 if (impersonationLevel
< TokenImpersonationLevel
.None
|| impersonationLevel
> TokenImpersonationLevel
.Delegation
) {
1088 throw new ArgumentOutOfRangeException("impersonationLevel", SR
.GetString(SR
.ArgumentOutOfRange_ImpersonationInvalid
));
1090 if (inheritability
< HandleInheritability
.None
|| inheritability
> HandleInheritability
.Inheritable
) {
1091 throw new ArgumentOutOfRangeException("inheritability", SR
.GetString(SR
.ArgumentOutOfRange_HandleInheritabilityNoneOrInheritable
));
1093 if ((desiredAccessRights
& ~
(PipeAccessRights
.FullControl
| PipeAccessRights
.AccessSystemSecurity
)) != 0) {
1094 throw new ArgumentOutOfRangeException("desiredAccessRights", SR
.GetString(SR
.ArgumentOutOfRange_InvalidPipeAccessRights
));
1097 m_normalizedPipePath
= Path
.GetFullPath(@"\\" + serverName
+ @"\pipe\" + pipeName
);
1099 if (String
.Compare(m_normalizedPipePath
, @"\\.\pipe\anonymous", StringComparison
.OrdinalIgnoreCase
) == 0) {
1100 throw new ArgumentOutOfRangeException("pipeName", SR
.GetString(SR
.ArgumentOutOfRange_AnonymousReserved
));
1103 m_inheritability
= inheritability
;
1104 m_impersonationLevel
= impersonationLevel
;
1105 m_pipeOptions
= options
;
1106 m_access
= (int)desiredAccessRights
;
1109 // Helper method for the constructor above. The PipeStream protected constructor takes in a PipeDirection so we need
1110 // to convert the access rights to a direction. Usually, PipeDirection.In/Out maps to GENERIC_READ/WRITE but in the
1111 // other direction, READ/WRITE_DATA is sufficient.
1112 private static PipeDirection
DirectionFromRights(PipeAccessRights rights
) {
1113 PipeDirection direction
= 0;
1114 if ((rights
& PipeAccessRights
.ReadData
) != 0) {
1115 direction
|= PipeDirection
.In
;
1117 if ((rights
& PipeAccessRights
.WriteData
) != 0) {
1118 direction
|= PipeDirection
.Out
;
1124 // Create a NamedPipeClientStream from an existing server pipe handle.
1125 [System
.Security
.SecuritySafeCritical
]
1126 [PermissionSet(SecurityAction
.Demand
, Name
= "FullTrust")]
1127 public NamedPipeClientStream(PipeDirection direction
, bool isAsync
, bool isConnected
,
1128 SafePipeHandle safePipeHandle
)
1129 : base(direction
, 0) {
1131 if (safePipeHandle
== null) {
1132 throw new ArgumentNullException("safePipeHandle");
1135 if (safePipeHandle
.IsInvalid
) {
1136 throw new ArgumentException(SR
.GetString(SR
.Argument_InvalidHandle
), "safePipeHandle");
1138 // Check that this handle is infact a handle to a pipe.
1139 if (UnsafeNativeMethods
.GetFileType(safePipeHandle
) != UnsafeNativeMethods
.FILE_TYPE_PIPE
) {
1140 throw new IOException(SR
.GetString(SR
.IO_IO_InvalidPipeHandle
));
1143 InitializeHandle(safePipeHandle
, true, isAsync
);
1145 State
= PipeState
.Connected
;
1149 ~
NamedPipeClientStream() {
1154 public void Connect() {
1155 Connect(Timeout
.Infinite
);
1158 // Waits for a pipe instance to become available. This method may return before WaitForConnection is called
1159 // on the server end, but WaitForConnection will not return until we have returned. Any data writen to the
1160 // pipe by us after we have connected but before the server has called WaitForConnection will be available
1161 // to the server after it calls WaitForConnection.
1162 [System
.Security
.SecurityCritical
]
1163 public void Connect(int timeout
) {
1164 CheckConnectOperationsClient();
1166 if (timeout
< 0 && timeout
!= Timeout
.Infinite
) {
1167 throw new ArgumentOutOfRangeException("timeout", SR
.GetString(SR
.ArgumentOutOfRange_InvalidTimeout
));
1170 UnsafeNativeMethods
.SECURITY_ATTRIBUTES secAttrs
= PipeStream
.GetSecAttrs(m_inheritability
);
1172 int _pipeFlags
= (int)m_pipeOptions
;
1173 if (m_impersonationLevel
!= TokenImpersonationLevel
.None
) {
1174 _pipeFlags
|= UnsafeNativeMethods
.SECURITY_SQOS_PRESENT
;
1175 _pipeFlags
|= (((int)m_impersonationLevel
- 1) << 16);
1178 // This is the main connection loop. It will loop until the timeout expires. Most of the
1179 // time, we will be waiting in the WaitNamedPipe win32 blocking function; however, there are
1180 // cases when we will need to loop: 1) The server is not created (WaitNamedPipe returns
1181 // straight away in such cases), and 2) when another client connects to our server in between
1182 // our WaitNamedPipe and CreateFile calls.
1183 int startTime
= Environment
.TickCount
;
1185 var sw
= new SpinWait();
1187 // Wait for pipe to become free (this will block unless the pipe does not exist).
1188 if (!UnsafeNativeMethods
.WaitNamedPipe(m_normalizedPipePath
, timeout
- elapsed
)) {
1189 int errorCode
= Marshal
.GetLastWin32Error();
1191 // Server is not yet created so let's keep looping.
1192 if (errorCode
== UnsafeNativeMethods
.ERROR_FILE_NOT_FOUND
) {
1197 // The timeout has expired.
1198 if (errorCode
== UnsafeNativeMethods
.ERROR_SUCCESS
) {
1202 __Error
.WinIOError(errorCode
, String
.Empty
);
1205 // Pipe server should be free. Let's try to connect to it.
1206 SafePipeHandle handle
= UnsafeNativeMethods
.CreateNamedPipeClient(m_normalizedPipePath
,
1207 m_access
, // read and write access
1209 secAttrs
, // security attributes
1210 FileMode
.Open
, // open existing
1211 _pipeFlags
, // impersonation flags
1212 UnsafeNativeMethods
.NULL
); // template file: null
1214 if (handle
.IsInvalid
) {
1215 int errorCode
= Marshal
.GetLastWin32Error();
1217 // Handle the possible race condition of someone else connecting to the server
1218 // between our calls to WaitNamedPipe & CreateFile.
1219 if (errorCode
== UnsafeNativeMethods
.ERROR_PIPE_BUSY
) {
1224 __Error
.WinIOError(errorCode
, String
.Empty
);
1228 InitializeHandle(handle
, false, (m_pipeOptions
& PipeOptions
.Asynchronous
) != 0);
1229 State
= PipeState
.Connected
;
1233 while (timeout
== Timeout
.Infinite
|| (elapsed
= unchecked(Environment
.TickCount
- startTime
)) < timeout
);
1234 // BUGBUG: SerialPort does not use unchecked arithmetic when calculating elapsed times. This is needed
1235 // because Environment.TickCount can overflow (though only every 49.7 days).
1237 throw new TimeoutException();
1240 public Task
ConnectAsync() {
1241 // We cannot avoid creating lambda here by using Connect method
1242 // unless we don't care about start time to be measured before the thread is started
1243 return ConnectAsync(Timeout
.Infinite
, CancellationToken
.None
);
1246 public Task
ConnectAsync(int timeout
) {
1247 return ConnectAsync(timeout
, CancellationToken
.None
);
1250 public Task
ConnectAsync(CancellationToken cancellationToken
) {
1251 return ConnectAsync(Timeout
.Infinite
, cancellationToken
);
1254 public Task
ConnectAsync(int timeout
, CancellationToken cancellationToken
) {
1255 CheckConnectOperationsClient();
1257 if (timeout
< 0 && timeout
!= Timeout
.Infinite
) {
1258 throw new ArgumentOutOfRangeException("timeout", SR
.GetString(SR
.ArgumentOutOfRange_InvalidTimeout
));
1261 if (cancellationToken
.IsCancellationRequested
) {
1262 return Task
.FromCancellation(cancellationToken
);
1265 // We need to measure time here, not in the lambda
1266 int startTime
= Environment
.TickCount
;
1267 return Task
.Factory
.StartNew(() => ConnectInternal(timeout
, cancellationToken
, startTime
), cancellationToken
);
1270 // Waits for a pipe instance to become available. This method may return before WaitForConnection is called
1271 // on the server end, but WaitForConnection will not return until we have returned. Any data writen to the
1272 // pipe by us after we have connected but before the server has called WaitForConnection will be available
1273 // to the server after it calls WaitForConnection.
1274 [System
.Security
.SecuritySafeCritical
]
1275 private void ConnectInternal(int timeout
, CancellationToken cancellationToken
, int startTime
) {
1276 UnsafeNativeMethods
.SECURITY_ATTRIBUTES secAttrs
= PipeStream
.GetSecAttrs(m_inheritability
);
1278 int _pipeFlags
= (int)m_pipeOptions
;
1279 if (m_impersonationLevel
!= TokenImpersonationLevel
.None
) {
1280 _pipeFlags
|= UnsafeNativeMethods
.SECURITY_SQOS_PRESENT
;
1281 _pipeFlags
|= (((int)m_impersonationLevel
- 1) << 16);
1284 // This is the main connection loop. It will loop until the timeout expires. Most of the
1285 // time, we will be waiting in the WaitNamedPipe win32 blocking function; however, there are
1286 // cases when we will need to loop: 1) The server is not created (WaitNamedPipe returns
1287 // straight away in such cases), and 2) when another client connects to our server in between
1288 // our WaitNamedPipe and CreateFile calls.
1290 var sw
= new SpinWait();
1292 // We want any other exception and and success to have priority over cancellation.
1293 cancellationToken
.ThrowIfCancellationRequested();
1295 // Wait for pipe to become free (this will block unless the pipe does not exist).
1296 int timeLeft
= timeout
- elapsed
;
1298 if (cancellationToken
.CanBeCanceled
) {
1299 waitTime
= Math
.Min(CancellationCheckIntervalInMilliseconds
, timeLeft
);
1302 waitTime
= timeLeft
;
1305 if (!UnsafeNativeMethods
.WaitNamedPipe(m_normalizedPipePath
, waitTime
)) {
1306 int errorCode
= Marshal
.GetLastWin32Error();
1308 // Server is not yet created so let's keep looping.
1309 if (errorCode
== UnsafeNativeMethods
.ERROR_FILE_NOT_FOUND
) {
1314 // The timeout has expired.
1315 if (errorCode
== UnsafeNativeMethods
.ERROR_SUCCESS
) {
1316 if (cancellationToken
.CanBeCanceled
) {
1317 // It may not be real timeout and only checking for cancellation
1318 // let the while condition check it and decide
1327 __Error
.WinIOError(errorCode
, String
.Empty
);
1330 // Pipe server should be free. Let's try to connect to it.
1331 SafePipeHandle handle
= UnsafeNativeMethods
.CreateNamedPipeClient(m_normalizedPipePath
,
1332 m_access
, // read and write access
1334 secAttrs
, // security attributes
1335 FileMode
.Open
, // open existing
1336 _pipeFlags
, // impersonation flags
1337 UnsafeNativeMethods
.NULL
); // template file: null
1339 if (handle
.IsInvalid
) {
1340 int errorCode
= Marshal
.GetLastWin32Error();
1342 // Handle the possible race condition of someone else connecting to the server
1343 // between our calls to WaitNamedPipe & CreateFile.
1344 if (errorCode
== UnsafeNativeMethods
.ERROR_PIPE_BUSY
) {
1349 __Error
.WinIOError(errorCode
, String
.Empty
);
1353 InitializeHandle(handle
, false, (m_pipeOptions
& PipeOptions
.Asynchronous
) != 0);
1354 State
= PipeState
.Connected
;
1358 while (timeout
== Timeout
.Infinite
|| (elapsed
= unchecked(Environment
.TickCount
- startTime
)) < timeout
);
1359 // BUGBUG: SerialPort does not use unchecked arithmetic when calculating elapsed times. This is needed
1360 // because Environment.TickCount can overflow (though only every 49.7 days).
1362 throw new TimeoutException();
1365 public int NumberOfServerInstances
{
1366 [System
.Security
.SecurityCritical
]
1367 [SuppressMessage("Microsoft.Security","CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification
="Security model of pipes: demand at creation but no subsequent demands")]
1369 CheckPipePropertyOperations();
1371 // NOTE: MSDN says that GetNamedPipeHandleState requires that the pipe handle has
1372 // GENERIC_READ access, but we don't check for that because sometimes it works without
1373 // GERERIC_READ access. [Edit: Seems like CreateFile slaps on a READ_ATTRIBUTES
1374 // access request before calling NTCreateFile, so all NamedPipeClientStreams can read
1375 // this if they are created (on WinXP SP2 at least)]
1377 if (!UnsafeNativeMethods
.GetNamedPipeHandleState(InternalHandle
, UnsafeNativeMethods
.NULL
, out numInstances
,
1378 UnsafeNativeMethods
.NULL
, UnsafeNativeMethods
.NULL
, UnsafeNativeMethods
.NULL
, 0)) {
1379 WinIOError(Marshal
.GetLastWin32Error());
1382 return numInstances
;
1386 // override because named pipe clients can't get/set properties when waiting to connect
1388 [System
.Security
.SecurityCritical
]
1389 protected override internal void CheckPipePropertyOperations() {
1390 base.CheckPipePropertyOperations();
1392 // Invalid operation
1393 if (State
== PipeState
.WaitingToConnect
) {
1394 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeNotYetConnected
));
1398 if (State
== PipeState
.Broken
) {
1399 throw new IOException(SR
.GetString(SR
.IO_IO_PipeBroken
));
1403 // named client is allowed to connect from broken
1404 private void CheckConnectOperationsClient() {
1406 if (State
== PipeState
.Connected
) {
1407 throw new InvalidOperationException(SR
.GetString(SR
.InvalidOperation_PipeAlreadyConnected
));
1409 if (State
== PipeState
.Closed
) {
1410 __Error
.PipeNotOpen();
1415 unsafe internal sealed class PipeAsyncResult
: IAsyncResult
{
1416 internal AsyncCallback _userCallback
; // User code callback
1417 internal Object _userStateObject
;
1418 internal ManualResetEvent _waitHandle
;
1420 internal SafePipeHandle _handle
;
1422 internal NativeOverlapped
* _overlapped
;
1424 internal int _EndXxxCalled
; // Whether we've called EndXxx already.
1425 internal int _errorCode
;
1427 internal bool _isComplete
; // Value for IsCompleted property
1428 internal bool _completedSynchronously
; // Which thread called callback
1430 public Object AsyncState
{
1432 return _userStateObject
;
1436 public bool IsCompleted
{
1442 public WaitHandle AsyncWaitHandle
{
1443 [System
.Security
.SecurityCritical
]
1445 if (_waitHandle
== null) {
1446 ManualResetEvent mre
= new ManualResetEvent(false);
1447 if (_overlapped
!= null && _overlapped
->EventHandle
!= IntPtr
.Zero
) {
1448 mre
.SafeWaitHandle
= new SafeWaitHandle(_overlapped
->EventHandle
, true);
1459 public bool CompletedSynchronously
{
1461 return _completedSynchronously
;
1465 private void CallUserCallbackWorker(Object callbackState
) {
1467 if (_waitHandle
!= null) {
1470 _userCallback(this);
1473 internal void CallUserCallback() {
1474 if (_userCallback
!= null) {
1475 _completedSynchronously
= false;
1476 ThreadPool
.QueueUserWorkItem(new WaitCallback(CallUserCallbackWorker
));
1480 if (_waitHandle
!= null) {