/// <summary>
        /// Executes a PSCommand against the session's runspace and returns
        /// a collection of results of the expected type.
        /// </summary>
        /// <typeparam name="TResult">The expected result type.</typeparam>
        /// <param name="psCommand">The PSCommand to be executed.</param>
        /// <param name="sendOutputToHost">
        /// If true, causes any output written during command execution to be written to the host.
        /// </param>
        /// <param name="sendErrorToHost">
        /// If true, causes any errors encountered during command execution to be written to the host.
        /// </param>
        /// <returns>
        /// An awaitable Task which will provide results once the command
        /// execution completes.
        /// </returns>
        public async Task <IEnumerable <TResult> > ExecuteCommand <TResult>(
            PSCommand psCommand,
            bool sendOutputToHost = false,
            bool sendErrorToHost  = true)
        {
            RunspaceHandle        runspaceHandle  = null;
            IEnumerable <TResult> executionResult = null;

            // If the debugger is active and the caller isn't on the pipeline
            // thread, send the command over to that thread to be executed.
            if (Thread.CurrentThread.ManagedThreadId != this.pipelineThreadId &&
                this.pipelineExecutionTask != null)
            {
                Logger.Write(LogLevel.Verbose, "Passing command execution to pipeline thread.");

                PipelineExecutionRequest <TResult> executionRequest =
                    new PipelineExecutionRequest <TResult>(
                        this, psCommand, sendOutputToHost);

                // Send the pipeline execution request to the pipeline thread
                this.pipelineResultTask = new TaskCompletionSource <IPipelineExecutionRequest>();
                this.pipelineExecutionTask.SetResult(executionRequest);

                await this.pipelineResultTask.Task;
                return(executionRequest.Results);
            }
            else
            {
                try
                {
                    // Instruct PowerShell to send output and errors to the host
                    if (sendOutputToHost)
                    {
                        psCommand.Commands[0].MergeMyResults(
                            PipelineResultTypes.Error,
                            PipelineResultTypes.Output);

                        psCommand.Commands.Add(
                            this.GetOutputCommand(
                                endOfStatement: false));
                    }

                    if (this.currentRunspace.RunspaceAvailability == RunspaceAvailability.AvailableForNestedCommand ||
                        this.debuggerStoppedTask != null)
                    {
                        Logger.Write(
                            LogLevel.Verbose,
                            string.Format(
                                "Attempting to execute nested pipeline command(s):\r\n\r\n{0}",
                                GetStringForPSCommand(psCommand)));

                        executionResult =
                            this.ExecuteCommandInDebugger <TResult>(
                                psCommand,
                                sendOutputToHost);
                    }
                    else
                    {
                        Logger.Write(
                            LogLevel.Verbose,
                            string.Format(
                                "Attempting to execute command(s):\r\n\r\n{0}",
                                GetStringForPSCommand(psCommand)));

                        // Set the runspace
                        runspaceHandle = await this.GetRunspaceHandle();

                        if (runspaceHandle.Runspace.RunspaceAvailability != RunspaceAvailability.AvailableForNestedCommand)
                        {
                            this.powerShell.Runspace = runspaceHandle.Runspace;
                        }

                        // Invoke the pipeline on a background thread
                        // TODO: Use built-in async invocation!
                        executionResult =
                            await Task.Factory.StartNew <IEnumerable <TResult> >(
                                () =>
                        {
                            this.powerShell.Commands    = psCommand;
                            Collection <TResult> result = this.powerShell.Invoke <TResult>();
                            return(result);
                        },
                                CancellationToken.None, // Might need a cancellation token
                                TaskCreationOptions.None,
                                TaskScheduler.Default
                                );

                        if (this.powerShell.HadErrors)
                        {
                            string errorMessage = "Execution completed with errors:\r\n\r\n";

                            foreach (var error in this.powerShell.Streams.Error)
                            {
                                errorMessage += error.ToString() + "\r\n";
                            }

                            Logger.Write(LogLevel.Error, errorMessage);
                        }
                        else
                        {
                            Logger.Write(
                                LogLevel.Verbose,
                                "Execution completed successfully.");
                        }

                        return(executionResult);
                    }
                }
                catch (RuntimeException e)
                {
                    Logger.Write(
                        LogLevel.Error,
                        "Runtime exception occurred while executing command:\r\n\r\n" + e.ToString());

                    if (sendErrorToHost)
                    {
                        // Write the error to the host
                        this.WriteExceptionToHost(e);
                    }
                }
                finally
                {
                    // Get the new prompt before releasing the runspace handle
                    if (sendOutputToHost)
                    {
                        // Write the prompt
                        if (runspaceHandle != null)
                        {
                            this.WritePromptWithRunspace(runspaceHandle.Runspace);
                        }
                        else
                        {
                            this.WritePromptWithNestedPipeline();
                        }
                    }

                    // Dispose of the execution context
                    if (runspaceHandle != null)
                    {
                        runspaceHandle.Dispose();
                    }
                }
            }

            return(executionResult);
        }
Exemple #2
0
        private async Task <IEnumerable <T> > ExecuteCommand <T>(PSCommand command, bool redirectOutput = false)
        {
            var result = default(IEnumerable <T>);

            if (Thread.CurrentThread.ManagedThreadId != _pipelineThreadId && _pipelineExecutionTask != null)
            {
                // Queue the execution since one task is already running.
                var executionRequest = new PipelineExecutionRequest <T>(this, command, redirectOutput);

                _pipelineResultTask = new TaskCompletionSource <IPipelineExecutionRequest>();

                if (_pipelineExecutionTask.TrySetResult(executionRequest))
                {
                    await _pipelineResultTask.Task;
                    return(executionRequest.Results);
                }

                return(null);
            }

            var scriptOutput = new PSDataCollection <PSObject>();

            if (redirectOutput)
            {
                scriptOutput.DataAdded += (sender, args) =>
                {
                    // Stream script output to console.
                    Execute.OnUIThread(() =>
                    {
                        var output = IoC.Get <IOutput>();

                        foreach (var item in scriptOutput.ReadAll())
                        {
                            output.AppendLine(item.ToString());
                        }
                    });
                };
            }

            if (_runspace.RunspaceAvailability == RunspaceAvailability.AvailableForNestedCommand ||
                _debuggerExecutionTask != null)
            {
                result = ExecuteCommandInDebugger <T>(command, redirectOutput);
            }
            else
            {
                result = await Task.Factory.StartNew <IEnumerable <T> >(() =>
                {
                    _powerShell.Commands = command;
                    var executionResult  = _powerShell.Invoke <T>(scriptOutput);
                    return(executionResult);
                },
                                                                        CancellationToken.None,
                                                                        TaskCreationOptions.None,
                                                                        TaskScheduler.Default
                                                                        );
            }

            _powerShell?.Commands.Clear();

            if (_powerShell == null || _powerShell.HadErrors)
            {
                return(null);
            }

            return(result);
        }
        /// <summary>
        /// Executes a PSCommand against the session's runspace and returns
        /// a collection of results of the expected type.
        /// </summary>
        /// <typeparam name="TResult">The expected result type.</typeparam>
        /// <param name="psCommand">The PSCommand to be executed.</param>
        /// <param name="sendOutputToHost">
        /// If true, causes any output written during command execution to be written to the host.
        /// </param>
        /// <returns>
        /// An awaitable Task which will provide results once the command
        /// execution completes.
        /// </returns>
        public async Task <IEnumerable <TResult> > ExecuteCommand <TResult>(
            PSCommand psCommand,
            bool sendOutputToHost = false)
        {
            // If the debugger is active and the caller isn't on the pipeline
            // thread, send the command over to that thread to be executed.
            if (Thread.CurrentThread.ManagedThreadId != this.pipelineThreadId &&
                this.pipelineExecutionTask != null)
            {
                Logger.Write(LogLevel.Verbose, "Passing command execution to pipeline thread.");

                PipelineExecutionRequest <TResult> executionRequest =
                    new PipelineExecutionRequest <TResult>(
                        this, psCommand, sendOutputToHost);

                // Send the pipeline execution request to the pipeline thread
                this.pipelineResultTask = new TaskCompletionSource <IPipelineExecutionRequest>();
                this.pipelineExecutionTask.SetResult(executionRequest);

                await this.pipelineResultTask.Task;
                return(executionRequest.Results);
            }
            else
            {
                try
                {
                    // Instruct PowerShell to send output and errors to the host
                    if (sendOutputToHost)
                    {
                        psCommand.Commands[0].MergeMyResults(
                            PipelineResultTypes.Error,
                            PipelineResultTypes.Output);

                        psCommand.Commands.Add(
                            this.GetOutputCommand(
                                endOfStatement: false));
                    }

                    if (this.currentRunspace.RunspaceAvailability == RunspaceAvailability.AvailableForNestedCommand ||
                        this.debuggerStoppedTask != null)
                    {
                        Logger.Write(
                            LogLevel.Verbose,
                            string.Format(
                                "Attempting to execute nested pipeline command(s):\r\n\r\n{0}",
                                GetStringForPSCommand(psCommand)));

                        using (Pipeline pipeline = this.currentRunspace.CreateNestedPipeline())
                        {
                            foreach (var command in psCommand.Commands)
                            {
                                pipeline.Commands.Add(command);
                            }

                            IEnumerable <TResult> result =
                                pipeline
                                .Invoke()
                                .Select(pso => pso.BaseObject)
                                .Cast <TResult>();

                            return(result);
                        }
                    }
                    else
                    {
                        Logger.Write(
                            LogLevel.Verbose,
                            string.Format(
                                "Attempting to execute command(s):\r\n\r\n{0}",
                                GetStringForPSCommand(psCommand)));

                        // Set the runspace
                        var runspaceHandle = await this.GetRunspaceHandle();

                        if (runspaceHandle.Runspace.RunspaceAvailability != RunspaceAvailability.AvailableForNestedCommand)
                        {
                            this.powerShell.Runspace = runspaceHandle.Runspace;
                        }

                        // Invoke the pipeline on a background thread
                        // TODO: Use built-in async invocation!
                        var taskResult =
                            await Task.Factory.StartNew <IEnumerable <TResult> >(
                                () =>
                        {
                            this.powerShell.Commands    = psCommand;
                            Collection <TResult> result = this.powerShell.Invoke <TResult>();
                            return(result);
                        },
                                CancellationToken.None,     // Might need a cancellation token
                                TaskCreationOptions.None,
                                TaskScheduler.Default
                                );

                        runspaceHandle.Dispose();

                        if (this.powerShell.HadErrors)
                        {
                            // TODO: Find a good way to extract errors!
                            Logger.Write(
                                LogLevel.Error,
                                "Execution completed with errors.");
                        }
                        else
                        {
                            Logger.Write(
                                LogLevel.Verbose,
                                "Execution completed successfully.");
                        }

                        bool hadErrors = this.powerShell.HadErrors;
                        return(taskResult);
                    }
                }
                catch (RuntimeException e)
                {
                    // TODO: Return an error
                    Logger.Write(
                        LogLevel.Error,
                        "Exception occurred while attempting to execute command:\r\n\r\n" + e.ToString());
                }
            }

            // TODO: Better result
            return(null);
        }