/// <summary> /// Writes the prompt. /// </summary> private static void WritePrompt([CanBeNull] NamedPipeServerInfo server) { if (Console.CursorLeft != 0) { ConsoleTextWriter.Default.WriteLine(); } _promptBuilder.WriteToConsole( null, (_, c) => { Debug.Assert(c != null); Debug.Assert(c.Tag != null); switch (c.Tag.ToLowerInvariant()) { case "time": return(DateTime.Now); case "server": return(server); default: return(Resolution.Unknown); } }); }
public static IEnumerable <NamedPipeServerInfo> GetServices() { // Note: Directory.GetFiles() can fail if there are pipes on the system with invalid characters, // to be safe we use the underlying kernel methods instead. IntPtr invalid = new IntPtr(-1); IntPtr handle = IntPtr.Zero; try { WIN32_FIND_DATA data; handle = FindFirstFile(@"\\.\pipe\*", out data); if (handle == invalid) { yield break; } do { NamedPipeServerInfo nps = new NamedPipeServerInfo(@"\\.\pipe\" + data.cFileName); if (nps.IsValid) { yield return(nps); } } while (FindNextFile(handle, out data) != 0); FindClose(handle); handle = invalid; } finally { if (handle != invalid) { FindClose(handle); } } }
/// <summary> /// Runs the client, optionally connecting to the given service. /// If no service is given, or the service is invalid, the user will be prompted to select a service to connect to. /// </summary> /// <param name="description">The description.</param> /// <param name="server">The server.</param> public static void Run([NotNull] string description, [CanBeNull] NamedPipeServerInfo server = null) { if (!ConsoleHelper.IsConsole) { return; } RunAsync(description, server).Wait(); }
public static Task <NamedPipeClient> Connect( [NotNull] string description, [CanBeNull] string pipe, [NotNull] Action <Message> onReceive, CancellationToken token = default(CancellationToken)) { NamedPipeServerInfo server = FindService(pipe); if (server == null || !server.IsValid) { return(null); } return(Connect(description, server, onReceive, token)); }
public static Task <NamedPipeClient> Connect( [NotNull] string description, [CanBeNull] NamedPipeServerInfo server, [NotNull] Action <Message> onReceive, CancellationToken token = default(CancellationToken)) { if (server == null || !server.IsValid) { return(TaskResult <NamedPipeClient> .Default); } NamedPipeClient npc = new NamedPipeClient(description, server, onReceive, token); TaskCompletionSource <NamedPipeClient> ccs = npc._connectionCompletionSource; // ReSharper disable once AssignNullToNotNullAttribute return(ccs != null ? ccs.Task : Task.FromResult(npc)); }
/// <summary> /// Initializes a new instance of the <see cref="NamedPipeClient" /> class. /// </summary> /// <param name="description">The client description.</param> /// <param name="server">The server.</param> /// <param name="onReceive">The action to call on receipt of a message.</param> /// <param name="token">The token.</param> private NamedPipeClient( [NotNull] string description, [NotNull] NamedPipeServerInfo server, [NotNull] Action <Message> onReceive, CancellationToken token = default(CancellationToken)) { if (description == null) { throw new ArgumentNullException("description"); } if (server == null) { throw new ArgumentNullException("server"); } if (onReceive == null) { throw new ArgumentNullException("onReceive"); } _server = server; _cancellationTokenSource = new CancellationTokenSource(); CancellationToken disposeToken = _cancellationTokenSource.Token; _clientTask = Task.Run( async() => { try { using (ITokenSource tokenSource = token.CreateLinked(disposeToken)) using ( OverlappingPipeClientStream stream = new OverlappingPipeClientStream( _server.Host, _server.FullName, PipeTransmissionMode.Message)) { _state = PipeState.Open; token = tokenSource.Token; // We need to support cancelling the connect. await stream.Connect(token).ConfigureAwait(false); ConnectResponse connectResponse = null; DisconnectResponse disconnectResponse = null; if (!token.IsCancellationRequested) { // Set the stream. _stream = stream; _state = PipeState.AwaitingConnect; // Kick off a connect request, but don't wait for it's result as we're the task that will receive it! ConnectRequest connectRequest = new ConnectRequest(description); await stream.WriteAsync(connectRequest.Serialize(), token).ConfigureAwait(false); // Keep going as long as we're connected. try { while (stream.IsConnected && !disposeToken.IsCancellationRequested) { // Read data in. byte[] data = await stream.ReadAsync(disposeToken).ConfigureAwait(false); if (data == null) { break; } // Deserialize the incoming message. Message message = Message.Deserialize(data); if (connectResponse == null) { // We require a connect response to start connectResponse = message as ConnectResponse; if (connectResponse == null || connectResponse.ID != connectRequest.ID) { break; } _state = PipeState.Connected; _serviceName = connectResponse.ServiceName; Log.Add( LoggingLevel.Notification, () => ClientResources.Not_NamedPipeClient_Connection, connectResponse.ServiceName); TaskCompletionSource <NamedPipeClient> ccs = Interlocked.Exchange(ref _connectionCompletionSource, null); if (ccs != null) { ccs.TrySetResult(this); } // Observer the message. onReceive(message); continue; } // Check for disconnect, we don't observe the message until the disconnect is complete. disconnectResponse = message as DisconnectResponse; if (disconnectResponse != null) { break; } // Observe the message. onReceive(message); Response response = message as Response; if (response == null) { continue; } ConnectedCommand connectedCommand; // Check for cancellation responses. CommandCancelResponse cancelResponse = response as CommandCancelResponse; if (cancelResponse != null) { // Cancel the associated request if (_commandRequests.TryGetValue( cancelResponse.CancelledCommandId, out connectedCommand)) { // ReSharper disable once PossibleNullReferenceException connectedCommand.Cancel(cancelResponse); } } // And fall through to complete the response... // Find command the response is related to, and notify it of the response. if (!_commandRequests.TryGetValue(response.ID, out connectedCommand)) { continue; } Debug.Assert(connectedCommand != null); if (connectedCommand.Received(response)) { _commandRequests.TryRemove(response.ID, out connectedCommand); } } } catch (TaskCanceledException) { } } // If we're still connected, and we haven't received a disconnect response, try to send a disconnect request. if (stream.IsConnected && disconnectResponse == null) { CancellationTokenSource cts = token.IsCancellationRequested ? new CancellationTokenSource(500) : null; try { CancellationToken t = cts != null ? cts.Token : token; // Try to send disconnect request. // ReSharper disable once PossibleNullReferenceException await Send(new DisconnectRequest(), t) .ToTask(t) .ConfigureAwait(false); } catch (TaskCanceledException) { } finally { if (cts != null) { cts.Dispose(); } } } // Remove the stream. _stream = null; _state = PipeState.Closed; _serviceName = null; // If we had a disconnect message observe it now that the disconnect has been actioned, // this prevents the receiver thinking the connection is still active. if (disconnectResponse != null) { onReceive(disconnectResponse); ConnectedCommand connectedCommand; if (_commandRequests.TryGetValue(disconnectResponse.ID, out connectedCommand)) { Debug.Assert(connectedCommand != null); if (connectedCommand.Received(disconnectResponse)) { _commandRequests.TryRemove(disconnectResponse.ID, out connectedCommand); } } } } } catch (IOException ioe) { if (!token.IsCancellationRequested) { // Common exception caused by sudden disconnect, lower level Log.Add( ioe, LoggingLevel.Information, () => ClientResources.Err_NamedPipeClient_Failed); } } catch (Exception exception) { TaskCanceledException tce = exception as TaskCanceledException; TaskCompletionSource <NamedPipeClient> ccs = Interlocked.Exchange( ref _connectionCompletionSource, null); if (ccs != null) { if (tce != null) { ccs.TrySetCanceled(); } else { ccs.TrySetException(exception); } } // We only log if this wasn't a cancellation exception. if (tce == null && !token.IsCancellationRequested) { Log.Add( exception, LoggingLevel.Error, () => ClientResources.Err_NamedPipeClient_Failed); } } finally { Dispose(); } }, disposeToken); }
public static async Task RunAsync( [NotNull] string description, [CanBeNull] NamedPipeServerInfo service = null, CancellationToken token = default(CancellationToken)) { if (description == null) { throw new ArgumentNullException("description"); } if (!ConsoleHelper.IsConsole) { return; } try { Log.SetTrace(validLevels: LoggingLevels.None); Log.SetConsole(_logFormatBuilder); Console.Title = description; NamedPipeClient client = null; while (client == null) { token.ThrowIfCancellationRequested(); while (service == null || !service.IsValid) { Console.Clear(); NamedPipeServerInfo[] services = null; await Log.Flush(token).ConfigureAwait(false); // ReSharper disable once AssignNullToNotNullAttribute ConsoleTextWriter.Default.WriteLine(ClientResources.ConsoleClient_RunAsync_ScanningForService); while (services == null || services.Length < 1) { services = NamedPipeClient.GetServices().ToArray(); if (Console.KeyAvailable) { return; } // ReSharper disable once PossibleNullReferenceException await Task.Delay(500, token).ConfigureAwait(false); token.ThrowIfCancellationRequested(); } if (services.Length > 0) { WriteServerList(services); } // ReSharper disable once AssignNullToNotNullAttribute ConsoleTextWriter.Default.WriteLine(ClientResources.ConsoleClient_RunAsync_EnterServiceName); string serviceName = Console.ReadLine(); service = !string.IsNullOrWhiteSpace(serviceName) ? NamedPipeClient.FindService(serviceName) : NamedPipeClient.GetServices().FirstOrDefault(); } Console.Clear(); // ReSharper disable once AssignNullToNotNullAttribute ConsoleTextWriter.Default.WriteLine( ClientResources.ConsoleClient_RunAsync_ConnectingToService, service.Name); try { // TODO Remove constant timeout using (ITokenSource tokenSource = token.WithTimeout(10000)) client = await NamedPipeClient.Connect( description, service, OnReceive, tokenSource.Token) .ConfigureAwait(false); } catch (TaskCanceledException) { // ReSharper disable once AssignNullToNotNullAttribute ConsoleTextWriter.Default.WriteLine( ClientResources.ConsoleClient_RunAsync_TimedOut, service.Name); // ReSharper disable once AssignNullToNotNullAttribute ConsoleTextWriter.Default.WriteLine(ClientResources.ConsoleClient_RunAsync_PressAnyKeyContinue); client = null; service = null; Console.ReadKey(true); } } // ReSharper disable once AssignNullToNotNullAttribute Console.Title = string.Format( ClientResources.ConsoleClient_RunAsync_ConnectedTitle, description, service.Name); _connected.WriteToConsole( null, new Dictionary <string, object> { { "ServiceName", client.ServiceName } }); // ReSharper disable once PossibleNullReferenceException await Task.Delay(1100, token).ConfigureAwait(false); await Log.Flush(token).ConfigureAwait(false); while (client.State != PipeState.Closed) { token.ThrowIfCancellationRequested(); WritePrompt(service); string command = Console.ReadLine(); if (!string.IsNullOrWhiteSpace(command)) { Guid commandGuid; bool completed = false; client.Execute(command, out commandGuid, token) .Subscribe( c => new FormatBuilder(c).WriteToConsole(), e => { Debug.Assert(e != null); if (!(e is TaskCanceledException)) { new FormatBuilder() .AppendForegroundColor(ConsoleColor.Red) .AppendLine(e.Message) .AppendResetForegroundColor() .WriteToConsole(); } completed = true; }, () => { completed = true; }, token); if (commandGuid != Guid.Empty) { do { if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape) { // Cancel command ConsoleTextWriter.Default.Write(ClientResources.ConsoleClient_RunAsync_Cancelling); await client.CancelCommand(commandGuid, token).ConfigureAwait(false); break; } // ReSharper disable once PossibleNullReferenceException await Task.Delay(100, token).ConfigureAwait(false); } while (!completed); } } // Wait to allow any disconnects or logs to come through. // ReSharper disable once PossibleNullReferenceException await Task.Delay(1100, token).ConfigureAwait(false); await Log.Flush(token).ConfigureAwait(false); } } catch (TaskCanceledException) { } catch (Exception e) { if (!token.IsCancellationRequested) { Log.Add(e); } } await Log.Flush(token).ConfigureAwait(false); // ReSharper disable once PossibleNullReferenceException await Task.Delay(200, token).ConfigureAwait(false); Console.WriteLine(ClientResources.ConsoleClient_RunAsync_PressAnyKeyExit); Console.ReadKey(true); }
public static IEnumerable<NamedPipeServerInfo> GetServices() { // Note: Directory.GetFiles() can fail if there are pipes on the system with invalid characters, // to be safe we use the underlying kernel methods instead. IntPtr invalid = new IntPtr(-1); IntPtr handle = IntPtr.Zero; try { WIN32_FIND_DATA data; handle = FindFirstFile(@"\\.\pipe\*", out data); if (handle == invalid) yield break; do { NamedPipeServerInfo nps = new NamedPipeServerInfo(@"\\.\pipe\" + data.cFileName); if (nps.IsValid) yield return nps; } while (FindNextFile(handle, out data) != 0); FindClose(handle); handle = invalid; } finally { if (handle != invalid) FindClose(handle); } }
/// <summary> /// Initializes a new instance of the <see cref="NamedPipeClient" /> class. /// </summary> /// <param name="description">The client description.</param> /// <param name="server">The server.</param> /// <param name="onReceive">The action to call on receipt of a message.</param> /// <param name="token">The token.</param> private NamedPipeClient( [NotNull] string description, [NotNull] NamedPipeServerInfo server, [NotNull] Action<Message> onReceive, CancellationToken token = default(CancellationToken)) { if (description == null) throw new ArgumentNullException("description"); if (server == null) throw new ArgumentNullException("server"); if (onReceive == null) throw new ArgumentNullException("onReceive"); _server = server; _cancellationTokenSource = new CancellationTokenSource(); CancellationToken disposeToken = _cancellationTokenSource.Token; _clientTask = Task.Run( async () => { try { using (ITokenSource tokenSource = token.CreateLinked(disposeToken)) using ( OverlappingPipeClientStream stream = new OverlappingPipeClientStream( _server.Host, _server.FullName, PipeTransmissionMode.Message)) { _state = PipeState.Open; token = tokenSource.Token; // We need to support cancelling the connect. await stream.Connect(token).ConfigureAwait(false); ConnectResponse connectResponse = null; DisconnectResponse disconnectResponse = null; if (!token.IsCancellationRequested) { // Set the stream. _stream = stream; _state = PipeState.AwaitingConnect; // Kick off a connect request, but don't wait for it's result as we're the task that will receive it! ConnectRequest connectRequest = new ConnectRequest(description); await stream.WriteAsync(connectRequest.Serialize(), token).ConfigureAwait(false); // Keep going as long as we're connected. try { while (stream.IsConnected && !disposeToken.IsCancellationRequested) { // Read data in. byte[] data = await stream.ReadAsync(disposeToken).ConfigureAwait(false); if (data == null) break; // Deserialize the incoming message. Message message = Message.Deserialize(data); if (connectResponse == null) { // We require a connect response to start connectResponse = message as ConnectResponse; if (connectResponse == null || connectResponse.ID != connectRequest.ID) break; _state = PipeState.Connected; _serviceName = connectResponse.ServiceName; Log.Add( LoggingLevel.Notification, () => ClientResources.Not_NamedPipeClient_Connection, connectResponse.ServiceName); TaskCompletionSource<NamedPipeClient> ccs = Interlocked.Exchange(ref _connectionCompletionSource, null); if (ccs != null) ccs.TrySetResult(this); // Observer the message. onReceive(message); continue; } // Check for disconnect, we don't observe the message until the disconnect is complete. disconnectResponse = message as DisconnectResponse; if (disconnectResponse != null) break; // Observe the message. onReceive(message); Response response = message as Response; if (response == null) continue; ConnectedCommand connectedCommand; // Check for cancellation responses. CommandCancelResponse cancelResponse = response as CommandCancelResponse; if (cancelResponse != null) // Cancel the associated request if (_commandRequests.TryGetValue( cancelResponse.CancelledCommandId, out connectedCommand)) // ReSharper disable once PossibleNullReferenceException connectedCommand.Cancel(cancelResponse); // And fall through to complete the response... // Find command the response is related to, and notify it of the response. if (!_commandRequests.TryGetValue(response.ID, out connectedCommand)) continue; Debug.Assert(connectedCommand != null); if (connectedCommand.Received(response)) _commandRequests.TryRemove(response.ID, out connectedCommand); } } catch (TaskCanceledException) { } } // If we're still connected, and we haven't received a disconnect response, try to send a disconnect request. if (stream.IsConnected && disconnectResponse == null) { CancellationTokenSource cts = token.IsCancellationRequested ? new CancellationTokenSource(500) : null; try { CancellationToken t = cts != null ? cts.Token : token; // Try to send disconnect request. // ReSharper disable once PossibleNullReferenceException await Send(new DisconnectRequest(), t) .ToTask(t) .ConfigureAwait(false); } catch (TaskCanceledException) { } finally { if (cts != null) cts.Dispose(); } } // Remove the stream. _stream = null; _state = PipeState.Closed; _serviceName = null; // If we had a disconnect message observe it now that the disconnect has been actioned, // this prevents the receiver thinking the connection is still active. if (disconnectResponse != null) { onReceive(disconnectResponse); ConnectedCommand connectedCommand; if (_commandRequests.TryGetValue(disconnectResponse.ID, out connectedCommand)) { Debug.Assert(connectedCommand != null); if (connectedCommand.Received(disconnectResponse)) _commandRequests.TryRemove(disconnectResponse.ID, out connectedCommand); } } } } catch (IOException ioe) { if (!token.IsCancellationRequested) // Common exception caused by sudden disconnect, lower level Log.Add( ioe, LoggingLevel.Information, () => ClientResources.Err_NamedPipeClient_Failed); } catch (Exception exception) { TaskCanceledException tce = exception as TaskCanceledException; TaskCompletionSource<NamedPipeClient> ccs = Interlocked.Exchange( ref _connectionCompletionSource, null); if (ccs != null) if (tce != null) ccs.TrySetCanceled(); else ccs.TrySetException(exception); // We only log if this wasn't a cancellation exception. if (tce == null && !token.IsCancellationRequested) Log.Add( exception, LoggingLevel.Error, () => ClientResources.Err_NamedPipeClient_Failed); } finally { Dispose(); } }, disposeToken); }