File: System\IO\Pipes\AnonymousPipeServerStream.cs
Web Access
Project: src\src\libraries\System.IO.Pipes\src\System.IO.Pipes.csproj (System.IO.Pipes)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.Win32.SafeHandles;
 
namespace System.IO.Pipes
{
    /// <summary>
    /// Anonymous pipe server stream
    /// </summary>
    public sealed partial class AnonymousPipeServerStream : PipeStream
    {
        private SafePipeHandle _clientHandle = null!;
        private bool _clientHandleExposed, _clientHandleExposedAsString;
        private readonly HandleInheritability _inheritability;
 
        public AnonymousPipeServerStream()
            : this(PipeDirection.Out, HandleInheritability.None, 0)
        {
        }
 
        public AnonymousPipeServerStream(PipeDirection direction)
            : this(direction, HandleInheritability.None, 0)
        {
        }
 
        public AnonymousPipeServerStream(PipeDirection direction, HandleInheritability inheritability)
            : this(direction, inheritability, 0)
        {
        }
 
        // Create an AnonymousPipeServerStream from two existing pipe handles.
        public AnonymousPipeServerStream(PipeDirection direction, SafePipeHandle serverSafePipeHandle, SafePipeHandle clientSafePipeHandle)
            : base(direction, 0)
        {
            if (direction == PipeDirection.InOut)
            {
                throw new NotSupportedException(SR.NotSupported_AnonymousPipeUnidirectional);
            }
            ArgumentNullException.ThrowIfNull(serverSafePipeHandle);
            ArgumentNullException.ThrowIfNull(clientSafePipeHandle);
 
            if (serverSafePipeHandle.IsInvalid)
            {
                throw new ArgumentException(SR.Argument_InvalidHandle, nameof(serverSafePipeHandle));
            }
            if (clientSafePipeHandle.IsInvalid)
            {
                throw new ArgumentException(SR.Argument_InvalidHandle, nameof(clientSafePipeHandle));
            }
            ValidateHandleIsPipe(serverSafePipeHandle);
            ValidateHandleIsPipe(clientSafePipeHandle);
 
            InitializeHandle(serverSafePipeHandle, true, false);
 
            _clientHandle = clientSafePipeHandle;
            _clientHandleExposed = true;
            State = PipeState.Connected;
        }
 
        // bufferSize is used as a suggestion; specify 0 to let OS decide
        // This constructor instantiates the PipeSecurity using just the inheritability flag
        public AnonymousPipeServerStream(PipeDirection direction, HandleInheritability inheritability, int bufferSize)
            : base(direction, bufferSize)
        {
            if (direction == PipeDirection.InOut)
            {
                throw new NotSupportedException(SR.NotSupported_AnonymousPipeUnidirectional);
            }
            if (inheritability < HandleInheritability.None || inheritability > HandleInheritability.Inheritable)
            {
                throw new ArgumentOutOfRangeException(nameof(inheritability), SR.ArgumentOutOfRange_HandleInheritabilityNoneOrInheritable);
            }
 
            Create(direction, inheritability, bufferSize);
            _inheritability = inheritability;
        }
 
        ~AnonymousPipeServerStream()
        {
            Dispose(false);
        }
 
        // This method should exist until we add a first class way of passing handles between parent and child
        // processes. For now, people do it via command line arguments.
        public string GetClientHandleAsString()
        {
            _clientHandleExposedAsString = _clientHandleExposed = true;
            GC.SuppressFinalize(_clientHandle);
            return _clientHandle.DangerousGetHandle().ToString();
        }
 
        public SafePipeHandle ClientSafePipeHandle
        {
            get
            {
                _clientHandleExposed = true;
                return _clientHandle;
            }
        }
 
        // This method is an annoying one but it has to exist at least until we make passing handles between
        // processes first class.  We need this because once the child handle is inherited, the OS considers
        // the parent and child's handles to be different.  Therefore, if a child closes its handle, our
        // Read/Write methods won't throw because the OS will think that there is still a child handle around
        // that can still Write/Read to/from the other end of the pipe.
        //
        // Ideally, we would want the Process class to close this handle after it has been inherited.  See
        // the pipe spec future features section for more information.
        //
        // Right now, this is the best signal to set the anonymous pipe as connected; if this is called, we
        // know the client has been passed the handle and so the connection is live.
        public void DisposeLocalCopyOfClientHandle()
        {
            if (_clientHandle != null && !_clientHandle.IsClosed)
            {
                _clientHandle.Dispose();
            }
        }
 
        protected override void Dispose(bool disposing)
        {
            try
            {
                // We should dispose of the client handle when it was not exposed at all OR
                // it was exposed as a string (handle finalization has been suppressed) and created inheritable (out-of-proc communication).
                if (!_clientHandleExposed || (_clientHandleExposedAsString && _inheritability == HandleInheritability.Inheritable))
                {
                    DisposeLocalCopyOfClientHandle();
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }
 
        // Anonymous pipes do not support message mode so there is no need to use the base version that P/Invokes here.
        public override PipeTransmissionMode TransmissionMode
        {
            get { return PipeTransmissionMode.Byte; }
        }
 
        public override PipeTransmissionMode ReadMode
        {
            set
            {
                CheckPipePropertyOperations();
 
                if (value < PipeTransmissionMode.Byte || value > PipeTransmissionMode.Message)
                {
                    throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_TransmissionModeByteOrMsg);
                }
                if (value == PipeTransmissionMode.Message)
                {
                    throw new NotSupportedException(SR.NotSupported_AnonymousPipeMessagesNotSupported);
                }
            }
        }
    }
}