Exemplo n.º 1
0
        /// <summary>
        /// Runs the specified service using the command console as a user interface.
        /// </summary>
        /// <param name="service">The service.</param>
        /// <param name="runMode">The run mode.</param>
        /// <param name="defaultLogFormat">The default log format.</param>
        /// <param name="defaultLoggingLevels">The default logging levels.</param>
        /// <param name="token">The token.</param>
        /// <returns>
        /// An awaitable task.
        /// </returns>
        // ReSharper disable once CodeAnnotationAnalyzer
        public static async Task RunAsync(
            [NotNull] BaseService service,
            RunMode runMode = RunMode.Default,
            [CanBeNull] FormatBuilder defaultLogFormat = null,
            LoggingLevels defaultLoggingLevels = LoggingLevels.All,
            CancellationToken token = default(CancellationToken))
        {
            if (service == null) throw new ArgumentNullException("service");

            if (!ConsoleHelper.IsConsole)
                return;
            Console.Clear();
            Log.SetTrace(validLevels: LoggingLevels.None);
            Log.SetConsole(defaultLogFormat ?? Log.ShortFormat, defaultLoggingLevels);
            await Log.Flush(token).ConfigureAwait(false);

            Impersonator impersonator = null;
            try
            {
                if (runMode.HasFlag(RunMode.Prompt))
                {
                    Debug.Assert(service.ServiceName != null);

                    // Whether we start will depend on the selected option in prompt.
                    runMode = runMode.Clear(RunMode.Start, true);

                    Console.Title = ServiceResources.ConsoleConnection_RunAsync_ConfigureTitle + service.ServiceName;
                    bool done = false;

                    do
                    {
                        if (token.IsCancellationRequested) return;

                        Dictionary<string, string> options = new Dictionary<string, string>
                        {
                            { "I", ServiceResources.ConsoleConnection_RunAsync_OptionInstall },
                            { "U", ServiceResources.ConsoleConnection_RunAsync_OptionUninstall },
                            { "S", ServiceResources.ConsoleConnection_RunAsync_OptionStart },
                            { "R", ServiceResources.ConsoleConnection_RunAsync_OptionRestart },
                            { "T", ServiceResources.ConsoleConnection_RunAsync_OptionStop },
                            { "P", ServiceResources.ConsoleConnection_RunAsync_OptionPause },
                            { "C", ServiceResources.ConsoleConnection_RunAsync_OptionContinue },
                            { "Y", ServiceResources.ConsoleConnection_RunAsync_OptionRunCmd },
                            { "V", ServiceResources.ConsoleConnection_RunAsync_OptionStartCmd },
                            { "W", ServiceResources.ConsoleConnection_RunAsync_OptionRunCmdNewCredentials },
                            { "Z", ServiceResources.ConsoleConnection_RunAsync_OptionRunNoInteraction },
                            { "X", ServiceResources.ConsoleConnection_RunAsync_OptionExit }
                        };

                        if (!runMode.HasFlag(RunMode.Interactive))
                        {
                            options.Remove("V");
                            options.Remove("Y");
                            options.Remove("W");
                        }

                        bool isAdmin;
                        string currentUser;
                        try
                        {
                            WindowsIdentity identity = WindowsIdentity.GetCurrent();
                            currentUser = identity.Name;
                            Debug.Assert(identity != null);
                            WindowsPrincipal principal = new WindowsPrincipal(identity);
                            isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
                        }
                        catch
                        {
                            isAdmin = false;
                            currentUser = null;
                        }

                        if (!string.IsNullOrEmpty(currentUser))
                        {
                            FormatBuilder fb = new FormatBuilder()
                                .AppendForegroundColor(ConsoleColor.Cyan)
                                .Append("Current User: "******" [Admin]");
                            fb.AppendLine().WriteToConsole();
                        }

                        if (!isAdmin)
                        {
                            options.Remove("I");
                            options.Remove("U");
                        }

                        if (Controller.ServiceIsInstalled(service.ServiceName))
                        {
                            ServiceControllerStatus state = Controller.GetServiceStatus(service.ServiceName);
                            new FormatBuilder()
                                .AppendForegroundColor(ConsoleColor.White)
                                .AppendFormatLine(
                                    ServiceResources.ConsoleConnection_RunAsync_ServiceInstalledState,
                                    service.ServiceName,
                                    state)
                                .AppendResetForegroundColor()
                                .WriteToConsole();

                            options.Remove("I");

                            switch (state)
                            {
                                case ServiceControllerStatus.StopPending:
                                case ServiceControllerStatus.Stopped:
                                    // Service is stopped or stopping.
                                    options.Remove("C");
                                    options.Remove("R");
                                    options.Remove("T");
                                    break;
                                case ServiceControllerStatus.StartPending:
                                case ServiceControllerStatus.ContinuePending:
                                case ServiceControllerStatus.Running:
                                    // Service is starting or running.
                                    options.Remove("S");
                                    options.Remove("C");
                                    break;
                                case ServiceControllerStatus.PausePending:
                                case ServiceControllerStatus.Paused:
                                    // Service is paused or pausing.
                                    options.Remove("S");
                                    options.Remove("R");
                                    options.Remove("T");
                                    options.Remove("P");
                                    break;
                                default:
                                    // Service is not installed - shouldn't happen.
                                    options.Remove("U");
                                    options.Remove("S");
                                    options.Remove("R");
                                    options.Remove("T");
                                    options.Remove("C");
                                    options.Remove("P");
                                    break;
                            }
                            options.Remove("V");
                            options.Remove("Y");
                            options.Remove("Z");
                        }
                        else
                        {
                            // No service installed.
                            options.Remove("U");
                            options.Remove("S");
                            options.Remove("R");
                            options.Remove("T");
                            options.Remove("P");
                            options.Remove("C");
                        }

                        _promptInstall.WriteToConsole(
                            null,
                            // ReSharper disable once PossibleNullReferenceException
                            (_, c) => !string.Equals(c.Tag, "options", StringComparison.CurrentCultureIgnoreCase)
                                ? Resolution.Unknown
                                : options);

                        string key;
                        do
                        {
                            key = Char.ToUpperInvariant(Console.ReadKey(true).KeyChar)
                                .ToString(CultureInfo.InvariantCulture);
                        } while (!options.ContainsKey(key));

                        try
                        {
                            string userName;
                            string password;

                            switch (key)
                            {
                                case "I":
                                    GetUserNamePassword(out userName, out password);

                                    service.Install(ConsoleTextWriter.Default, userName, password);

                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_WaitInstall);
                                    while (!Controller.ServiceIsInstalled(service.ServiceName))
                                    {
                                        await Task.Delay(250, token).ConfigureAwait(false);
                                        Console.Write('.');
                                    }
                                    Console.WriteLine(ServiceResources.Done);
                                    Console.WriteLine();
                                    break;

                                case "U":
                                    await service.Uninstall(ConsoleTextWriter.Default, token).ConfigureAwait(false);

                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_WaitUninstall);
                                    while (Controller.ServiceIsInstalled(service.ServiceName))
                                    {
                                        await Task.Delay(250, token).ConfigureAwait(false);
                                        Console.Write('.');
                                    }
                                    Console.WriteLine(ServiceResources.Done);
                                    Console.WriteLine();
                                    break;

                                case "R":
                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStop);
                                    await Controller.StopService(service.ServiceName, token).ConfigureAwait(false);
                                    Console.WriteLine(ServiceResources.Done);
                                    Console.WriteLine();
                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStart);
                                    await
                                        Controller.StartService(service.ServiceName, null, token).ConfigureAwait(false);
                                    Console.WriteLine(ServiceResources.Done);
                                    Console.WriteLine();
                                    break;

                                case "S":
                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStart);
                                    await
                                        Controller.StartService(service.ServiceName, null, token).ConfigureAwait(false);
                                    Console.WriteLine(ServiceResources.Done);
                                    Console.WriteLine();
                                    break;

                                case "T":
                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingStop);
                                    await Controller.StopService(service.ServiceName, token).ConfigureAwait(false);
                                    Console.WriteLine(ServiceResources.Done);
                                    Console.WriteLine();
                                    break;

                                case "P":
                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingPause);
                                    await Controller.PauseService(service.ServiceName, token).ConfigureAwait(false);
                                    Console.WriteLine(ServiceResources.Done);
                                    break;

                                case "C":
                                    Console.Write(ServiceResources.ConsoleConnection_RunAsync_AttemptingContinue);
                                    await Controller.ContinueService(service.ServiceName, token).ConfigureAwait(false);
                                    Console.WriteLine(ServiceResources.Done);
                                    Console.WriteLine();
                                    break;

                                case "V":
                                    runMode = runMode.Set(RunMode.Start, true).Set(RunMode.Interactive, true);
                                    done = true;
                                    break;

                                case "Y":
                                    runMode = runMode.Set(RunMode.Interactive, true);
                                    done = true;
                                    break;

                                case "W":
                                    GetUserNamePassword(out userName, out password);
                                    if (userName == null)
                                        break;
                                    Debug.Assert(password != null);

                                    Impersonator ei = impersonator;
                                    impersonator = null;
                                    if (ei != null)
                                        ei.Dispose();
                                    // Run in new security context.
                                    impersonator = new Impersonator(userName, password);
                                    break;

                                case "Z":
                                    runMode = runMode.Set(RunMode.Start, true).Clear(RunMode.Interactive, true);
                                    done = true;
                                    Console.WriteLine(ServiceResources.ConsoleConnection_RunAsync_RunningNonInteractive);
                                    Console.WriteLine(
                                        ServiceResources.ConsoleConnection_RunAsync_RunningNonInteractive2);
                                    Console.WriteLine();
                                    break;

                                default:
                                    return;
                            }
                        }
                        catch (TaskCanceledException)
                        {
                            return;
                        }
                        catch (Exception e)
                        {
                            if (!token.IsCancellationRequested)
                                Log.Add(e);
                        }
                    } while (!done);
                }
                else if (!runMode.HasFlag(RunMode.Interactive))
                    // If we don't show prompt and we're not interactive we should always start the service.
                    runMode = runMode.Set(RunMode.Start, true);

                // Create connection
                Console.Title = ServiceResources.ConsoleConnection_RunAsync_RunningTitle + service.ServiceName;
                ConsoleConnection connection = new ConsoleConnection(defaultLogFormat, defaultLoggingLevels, token);
                Guid id = service.Connect(connection);

                // Combined cancellation tokens.
                ITokenSource tSource = token.CreateLinked(connection._cancellationTokenSource.Token);
                try
                {
                    CancellationToken t = tSource.Token;

                    if (t.IsCancellationRequested) return;

                    if (runMode.HasFlag(RunMode.Start))
                    {
                        // Start the service
                        await service.StartService(ConsoleTextWriter.Default, null, t).ConfigureAwait(false);
                        if (t.IsCancellationRequested)
                            return;
                    }

                    if (!runMode.HasFlag(RunMode.Interactive))
                    {
                        // Wait to be cancelled as nothing to do.
                        await t.WaitHandle;
                        return;
                    }

                    do
                    {
                        // Flush logs
                        await Log.Flush(t).ConfigureAwait(false);

                        if (t.IsCancellationRequested) break;

                        WritePrompt(service);
                        try
                        {
                            string commandLine = await Console.In.ReadLineAsync().ConfigureAwait(false);
                            if (!string.IsNullOrWhiteSpace(commandLine))
                            {
                                bool completed = false;
                                ICancelableTokenSource commandCancellationSource = t.ToCancelable();
                                CancellationToken commandToken = commandCancellationSource.Token;

#pragma warning disable 4014
                                service.ExecuteAsync(id, commandLine, ConsoleTextWriter.Default, commandToken)
                                    .ContinueWith(
                                        task =>
                                        {
                                            Debug.Assert(task != null);

                                            completed = true;

                                            if (task.IsCompleted ||
                                                task.IsCanceled)
                                                return;

                                            if (task.IsFaulted)
                                            {
                                                Debug.Assert(task.Exception != null);
                                                _errorFormat.WriteToConsoleInstance(null, task.Exception);
                                            }
                                        },
                                        TaskContinuationOptions.ExecuteSynchronously);
#pragma warning restore 4014

                                while (!completed)
                                {
                                    if (!commandCancellationSource.IsCancellationRequested &&
                                        Console.KeyAvailable &&
                                        Console.ReadKey(true).Key == ConsoleKey.Escape)
                                    {
                                        // Cancel command
                                        Console.Write(ServiceResources.ConsoleConnection_RunAsync_Cancelling);
                                        commandCancellationSource.Cancel();
                                        break;
                                    }
                                    await Task.Delay(100, token).ConfigureAwait(false);
                                }
                            }
                        }
                        catch (TaskCanceledException)
                        {
                            throw;
                        }
                        catch (Exception e)
                        {
                            if (!t.IsCancellationRequested)
                                _errorFormat.WriteToConsoleInstance(null, e);
                        }

                        // Let any async stuff done by the command have a bit of time, also throttle commands.
                        await Task.Delay(500, t).ConfigureAwait(false);
                    } while (!t.IsCancellationRequested);
                }
                catch (TaskCanceledException)
                {
                }
                finally
                {
                    tSource.Dispose();

                    // ReSharper disable MethodSupportsCancellation
                    Log.Flush().Wait();
                    // ReSharper restore MethodSupportsCancellation
                    service.Disconnect(id);
                    Console.WriteLine(ServiceResources.ConsoleConnection_RunAsync_PressKeyToExit);
                    Console.ReadKey(true);
                }
            }
            finally
            {
                if (impersonator != null)
                    impersonator.Dispose();
            }
        }
Exemplo n.º 2
0
        public IObservable<Response> Send(
            [NotNull] Request request,
            CancellationToken token = default(CancellationToken))
        {
            OverlappingPipeClientStream stream = _stream;
            if (_state != PipeState.Connected ||
                stream == null)
                // ReSharper disable once AssignNullToNotNullAttribute
                return Observable.Empty<Response>();

            // ReSharper disable once AssignNullToNotNullAttribute
            return Observable.Create<Response>(
                async (observer, t) =>
                {
                    Debug.Assert(observer != null);

                    using (ITokenSource tokenSource = token.CreateLinked(t))
                    {
                        token = tokenSource.Token;

                        ConnectedCommand cr = new ConnectedCommand(request, observer);
                        _commandRequests.TryAdd(request.ID, cr);
                        try
                        {
                            await stream.WriteAsync(request.Serialize(), token).ConfigureAwait(false);
                            await cr.CompletionTask.WithCancellation(token).ConfigureAwait(false);
                        }
                            // ReSharper disable once EmptyGeneralCatchClause
                        catch
                        {
                        }

                        // If the command is not explicitly cancelled and is still running, and we've been cancelled
                        // then ask the server to cancel.
                        if (!cr.IsCancelled &&
                            !cr.IsCompleted &&
                            token.IsCancellationRequested)
                            try
                            {
                                using (CancellationTokenSource cts = Constants.FireAndForgetTokenSource)
                                    await CancelCommand(request.ID, cts.Token).ConfigureAwait(false);
                            }
                            catch (TaskCanceledException)
                            {
                            }

                        // Remove the command request.
                        _commandRequests.TryRemove(request.ID, out cr);
                    }
                });
        }
Exemplo n.º 3
0
        /// <summary>
        /// The timer task executes the callback asynchronously after set delays.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns></returns>
        // ReSharper disable once FunctionComplexityOverflow
        private async Task TimerTask(CancellationToken cancellationToken)
        {
            long startTicks = long.MinValue;
            long endTicks = long.MinValue;
            while (!cancellationToken.IsCancellationRequested)
                try
                {
                    CancellationTokenSource timeoutsChanged;

                    // Check we're not set to run immediately
                    if (Interlocked.Exchange(ref _runImmediate, 0) == 0)
                        do
                        {
                            // Create new cancellation token source and set _timeOutsChanged to it in a thread-safe none-locking way.
                            timeoutsChanged = new CancellationTokenSource();
                            CancellationTokenSource toc = Interlocked.Exchange(ref _timeOutsChanged, timeoutsChanged);
                            if (ReferenceEquals(toc, null))
                            {
                                toc = Interlocked.CompareExchange(ref _timeOutsChanged, null, timeoutsChanged);
                                if (!ReferenceEquals(toc, null))
                                    toc.Dispose();
                                return;
                            }

                            // If we have run immediate set at this point, we can't rely on the correct _timeOutsChanged cts being cancelled.
                            if (Interlocked.Exchange(ref _runImmediate, 0) > 0) break;

                            using (ITokenSource tokenSource = cancellationToken.CreateLinked(timeoutsChanged.Token))
                            {
                                // Check for pausing.
                                try
                                {
                                    await _pauseToken.WaitWhilePausedAsync(tokenSource.Token).ConfigureAwait(false);
                                }
                                catch (OperationCanceledException)
                                {
                                }
                                catch (Exception exception)
                                {
                                    if (!ReferenceEquals(_errorHandler, null))
                                        _errorHandler(exception);
                                }

                                if (cancellationToken.IsCancellationRequested) return;

                                // Get timeouts
                                TimeOuts timeOuts = _timeOuts;
                                if (ReferenceEquals(timeOuts, null)) return;

                                if (timeOuts.DueTimeMs < 0 ||
                                    (startTicks > timeOuts.DueTimeStamp && (timeOuts.MinimumGapMs < 0 || timeOuts.PeriodMs < 0)))
                                {
                                    // If we have infinite waits then we are effectively awaiting cancellation
                                    // ReSharper disable once PossibleNullReferenceException
                                    await tokenSource.ConfigureAwait(false);

                                    if (cancellationToken.IsCancellationRequested) return;
                                    continue;
                                }

                                // If all timeouts are zero we effectively run again immediately (after checking we didn't get a cancellation
                                // indicating the value have changed again).
                                if (timeOuts.DueTimeMs == 0 &&
                                    timeOuts.MinimumGapMs == 0 &&
                                    timeOuts.PeriodMs == 0)
                                    continue;

                                int wait;

                                if (startTicks > long.MinValue)
                                {
                                    // Calculate the wait time based on the minimum gap and the period.
                                    long now = HighPrecisionClock.Instance.NowTicks;
                                    int a = timeOuts.PeriodMs -
                                            (int)((now - startTicks) / NodaConstants.TicksPerMillisecond);
                                    int b = timeOuts.MinimumGapMs -
                                            (int)((now - endTicks) / NodaConstants.TicksPerMillisecond);
                                    int c = (int)((timeOuts.DueTimeStamp - now) / NodaConstants.TicksPerMillisecond);

                                    wait = Math.Max(a, Math.Max(b, c));
                                }
                                else
                                    // Wait the initial due time
                                    wait =
                                        (int)
                                            ((timeOuts.DueTimeStamp - HighPrecisionClock.Instance.NowTicks) /
                                             NodaConstants.TicksPerMillisecond);

                                // If we don't need to wait run again immediately (after checking values haven't changed).
                                if (wait < 1) continue;

                                try
                                {
                                    // Wait for set milliseconds
                                    // ReSharper disable PossibleNullReferenceException
                                    await Task.Delay(wait, tokenSource.Token).ConfigureAwait(false);
                                    // ReSharper restore PossibleNullReferenceException
                                }
                                catch (OperationCanceledException)
                                {
                                }
                                catch (Exception exception)
                                {
                                    if (!ReferenceEquals(_errorHandler, null))
                                        _errorHandler(exception);
                                }
                            }

                            // Recalculate wait time if 'cancelled' due to signal, and not set to run immediately; or if we're currently paused.
                        } while (
                            _pauseToken.IsPaused ||
                            (timeoutsChanged.IsCancellationRequested &&
                             !cancellationToken.IsCancellationRequested &&
                             Interlocked.Exchange(ref _runImmediate, 0) < 1));

                    if (cancellationToken.IsCancellationRequested) return;

                    try
                    {
                        Interlocked.CompareExchange(
                            ref _callbackCompletionSource,
                            new TaskCompletionSource<bool>(),
                            null);

                        startTicks = HighPrecisionClock.Instance.NowTicks;

                        // ReSharper disable once PossibleNullReferenceException
                        await _callback(cancellationToken).ConfigureAwait(false);

                        if (cancellationToken.IsCancellationRequested) return;
                    }
                    catch (OperationCanceledException)
                    {
                        // Just finish as we're cancelled

                        TaskCompletionSource<bool> callbackCompletionSource =
                            Interlocked.Exchange(ref _callbackCompletionSource, null);

                        // If the completion source is not null, then someone is awaiting last execution, so complete the task
                        if (!ReferenceEquals(callbackCompletionSource, null))
                            callbackCompletionSource.TrySetCanceled();

                        return;
                    }
                    // ReSharper disable once EmptyGeneralCatchClause
                    catch (Exception exception)
                    {
                        // Supress errors thrown by callback, unless someone is awaiting it.

                        TaskCompletionSource<bool> callbackCompletionSource =
                            Interlocked.Exchange(ref _callbackCompletionSource, null);

                        // If the completion source is not null, then someone is awaiting last execution, so complete the task
                        if (!ReferenceEquals(callbackCompletionSource, null))
                            callbackCompletionSource.TrySetException(exception);

                        if (!ReferenceEquals(_errorHandler, null))
                            _errorHandler(exception);
                    }
                    finally
                    {
                        endTicks = HighPrecisionClock.Instance.NowTicks;

                        // If run immediately was set whilst we were running, we can clear it.
                        Interlocked.Exchange(ref _runImmediate, 0);

                        TaskCompletionSource<bool> callbackCompletionSource =
                            Interlocked.Exchange(ref _callbackCompletionSource, null);

                        // If the completion source is not null, then someone is awaiting last execution, so complete the task
                        if (!ReferenceEquals(callbackCompletionSource, null))
                            callbackCompletionSource.TrySetResult(true);
                    }
                }
                catch (Exception exception)
                {
                    if (!ReferenceEquals(_errorHandler, null))
                        _errorHandler(exception);
                }
        }
Exemplo n.º 4
0
        /// <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);
        }