protected override async Task WaitAndProcessAsync( Func <Stream, CancellationToken, Task> process, CancellationToken cancellationToken) { if (process is null) { throw new ArgumentNullException(nameof(process)); } // https://github.com/PowerShell/PowerShellEditorServices/blob/f45c6312a859cde4aa25ea347a345e1d35238350/src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeServerListener.cs#L38-L67 // Unfortunately, .NET Core does not support passing in a PipeSecurity object into the constructor for // NamedPipeServerStream so we are creating native Named Pipes and securing them using native APIs. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { PipeSecurity pipeSecurity = new PipeSecurity(); PipeAccessRule psRule = new PipeAccessRule(@"Everyone", PipeAccessRights.FullControl, System.Security.AccessControl.AccessControlType.Allow); pipeSecurity.AddAccessRule(psRule); using (var server = NamedPipeNative.CreateNamedPipe(_options.PipeName, (uint)_options.MaxConcurrentCalls, pipeSecurity)) { await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false); await process(server, cancellationToken).ConfigureAwait(false); } } // Use original logic on other platforms. else { using (var server = new NamedPipeServerStream(_options.PipeName, PipeDirection.InOut, _options.MaxConcurrentCalls, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)) { await server.WaitForConnectionAsync(cancellationToken).ConfigureAwait(false); await process(server, cancellationToken).ConfigureAwait(false); } } }
/// <summary> /// Helper method to create a PowerShell transport named pipe via native API, along /// with a returned .Net NamedPipeServerStream object wrapping the named pipe. /// </summary> /// <param name="pipeName">Named pipe core name.</param> /// <param name="securityDesc"></param> /// <returns>NamedPipeServerStream</returns> internal static NamedPipeServerStream CreateNamedPipe( string pipeName, uint maxNumberOfServerInstances, PipeSecurity pipeSecurity) { string fullPipeName = @"\\.\pipe\" + pipeName; CommonSecurityDescriptor securityDesc = new CommonSecurityDescriptor(false, false, pipeSecurity.GetSecurityDescriptorBinaryForm(), 0); // Create optional security attributes based on provided PipeSecurity. NamedPipeNative.SECURITY_ATTRIBUTES securityAttributes = null; GCHandle?securityDescHandle = null; if (securityDesc != null) { byte[] securityDescBuffer = new byte[securityDesc.BinaryLength]; securityDesc.GetBinaryForm(securityDescBuffer, 0); securityDescHandle = GCHandle.Alloc(securityDescBuffer, GCHandleType.Pinned); securityAttributes = NamedPipeNative.GetSecurityAttributes(securityDescHandle.Value); } uint openMode = NamedPipeNative.PIPE_ACCESS_DUPLEX | NamedPipeNative.FILE_FLAG_OVERLAPPED; if (maxNumberOfServerInstances == 1) { openMode |= NamedPipeNative.FILE_FLAG_FIRST_PIPE_INSTANCE; } // Create named pipe. #pragma warning disable CA2000 // Dispose objects before losing scope SafePipeHandle pipeHandle = NamedPipeNative.CreateNamedPipe( fullPipeName, openMode, NamedPipeNative.PIPE_TYPE_BYTE | NamedPipeNative.PIPE_READMODE_BYTE, maxNumberOfServerInstances, 1, 1, 0, securityAttributes); #pragma warning restore CA2000 // Dispose objects before losing scope int lastError = Marshal.GetLastWin32Error(); if (securityDescHandle != null) { securityDescHandle.Value.Free(); } if (pipeHandle.IsInvalid) { throw new InvalidOperationException(); } // Create the .Net NamedPipeServerStream wrapper. try { return(new NamedPipeServerStream( PipeDirection.InOut, true, // IsAsync false, // IsConnected pipeHandle)); } catch (Exception) { pipeHandle.Dispose(); throw; } }