/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/System.Management.Automation/engine/hostifaces/Pipeline.cs
740 строк
27 KB
xtqqczze
Fix `SA1028`: Code should not contain trailing whitespace. Part 1. (#26203)
16 окт 2025, 10:58
Не верифицирован
16 окт 2025, 10:58
a10ff10
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections; using System.Collections.ObjectModel; using System.Management.Automation.Internal; using System.Runtime.Serialization; using System.Threading; using Dbg = System.Management.Automation.Diagnostics; namespace System.Management.Automation.Runspaces { #region Exceptions /// <summary> /// Defines exception which is thrown when state of the pipeline is different /// from expected state. /// </summary> public class InvalidPipelineStateException : SystemException { /// <summary> /// Initializes a new instance of the InvalidPipelineStateException class. /// </summary> public InvalidPipelineStateException() : base(StringUtil.Format(RunspaceStrings.InvalidPipelineStateStateGeneral)) { } /// <summary> /// Initializes a new instance of the InvalidPipelineStateException class /// with a specified error message. /// </summary> /// <param name="message"> /// The error message that explains the reason for the exception. /// </param> public InvalidPipelineStateException(string message) : base(message) { } /// <summary> /// Initializes a new instance of the InvalidPipelineStateException class /// with a specified error message and a reference to the inner exception that is the cause of this exception. /// </summary> /// <param name="message"> /// The error message that explains the reason for the exception. /// </param> /// <param name="innerException"> /// The exception that is the cause of the current exception. /// </param> public InvalidPipelineStateException(string message, Exception innerException) : base(message, innerException) { } /// <summary> /// Initializes a new instance of the InvalidPipelineStateException and defines value of /// CurrentState and ExpectedState. /// </summary> /// <param name="message">The error message that explains the reason for the exception. /// </param> /// <param name="currentState">Current state of pipeline.</param> /// <param name="expectedState">Expected state of pipeline.</param> internal InvalidPipelineStateException(string message, PipelineState currentState, PipelineState expectedState) : base(message) { _expectedState = expectedState; _currentState = currentState; } #region ISerializable Members // 2005/04/20-JonN No need to implement GetObjectData // if all fields are static or [NonSerialized] /// <summary> /// Initializes a new instance of the <see cref="InvalidPipelineStateException"/> /// class with serialized data. /// </summary> /// <param name="info"> /// The <see cref="SerializationInfo"/> that holds the serialized object /// data about the exception being thrown. /// </param> /// <param name="context"> /// The <see cref="StreamingContext"/> that contains contextual information /// about the source or destination. /// </param> [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] private InvalidPipelineStateException(SerializationInfo info, StreamingContext context) { throw new NotSupportedException(); } #endregion /// <summary> /// Gets CurrentState of the pipeline. /// </summary> public PipelineState CurrentState { get { return _currentState; } } /// <summary> /// Gets ExpectedState of the pipeline. /// </summary> public PipelineState ExpectedState { get { return _expectedState; } } /// <summary> /// State of pipeline when exception was thrown. /// </summary> [NonSerialized] private readonly PipelineState _currentState = 0; /// <summary> /// States of the pipeline expected in method which throws this exception. /// </summary> [NonSerialized] private readonly PipelineState _expectedState = 0; } #endregion Exceptions #region PipelineState /// <summary> /// Enumerated type defining the state of the Pipeline. /// </summary> public enum PipelineState { /// <summary> /// The pipeline has not been started. /// </summary> NotStarted = 0, /// <summary> /// The pipeline is executing. /// </summary> Running = 1, /// <summary> /// The pipeline is stoping execution. /// </summary> Stopping = 2, /// <summary> /// The pipeline is completed due to a stop request. /// </summary> Stopped = 3, /// <summary> /// The pipeline has completed. /// </summary> Completed = 4, /// <summary> /// The pipeline completed abnormally due to an error. /// </summary> Failed = 5, /// <summary> /// The pipeline is disconnected from remote running command. /// </summary> Disconnected = 6 } /// <summary> /// Type which has information about PipelineState and Exception /// associated with PipelineState. /// </summary> public sealed class PipelineStateInfo { #region constructors /// <summary> /// Constructor for state changes not resulting from an error. /// </summary> /// <param name="state">Execution state.</param> internal PipelineStateInfo(PipelineState state) : this(state, null) { } /// <summary> /// Constructor for state changes with an optional error. /// </summary> /// <param name="state">The new state.</param> /// <param name="reason">A non-null exception if the state change was /// caused by an error,otherwise; null. /// </param> internal PipelineStateInfo(PipelineState state, Exception reason) { State = state; Reason = reason; } /// <summary> /// Copy constructor to support cloning. /// </summary> /// <param name="pipelineStateInfo">Source information.</param> /// <throws> /// ArgumentNullException when <paramref name="pipelineStateInfo"/> is null. /// </throws> internal PipelineStateInfo(PipelineStateInfo pipelineStateInfo) { Dbg.Assert(pipelineStateInfo != null, "caller should validate the parameter"); State = pipelineStateInfo.State; Reason = pipelineStateInfo.Reason; } #endregion constructors #region public_properties /// <summary> /// The state of the runspace. /// </summary> /// <remarks> /// This value indicates the state of the pipeline after the change. /// </remarks> public PipelineState State { get; } /// <summary> /// The reason for the state change, if caused by an error. /// </summary> /// <remarks> /// The value of this property is non-null if the state /// changed due to an error. Otherwise, the value of this /// property is null. /// </remarks> public Exception Reason { get; } #endregion public_properties /// <summary> /// Clones this object. /// </summary> /// <returns>Cloned object.</returns> internal PipelineStateInfo Clone() { return new PipelineStateInfo(this); } } /// <summary> /// Event arguments passed to PipelineStateEvent handlers /// <see cref="Pipeline.StateChanged"/> event. /// </summary> public sealed class PipelineStateEventArgs : EventArgs { #region constructors /// <summary> /// Constructor PipelineStateEventArgs from PipelineStateInfo. /// </summary> /// <param name="pipelineStateInfo">The current state of the /// pipeline.</param> /// <throws> /// ArgumentNullException when <paramref name="pipelineStateInfo"/> is null. /// </throws> internal PipelineStateEventArgs(PipelineStateInfo pipelineStateInfo) { Dbg.Assert(pipelineStateInfo != null, "caller should validate the parameter"); PipelineStateInfo = pipelineStateInfo; } #endregion constructors #region public_properties /// <summary> /// Info about current state of pipeline. /// </summary> public PipelineStateInfo PipelineStateInfo { get; } #endregion public_properties } #endregion ExecutionState /// <summary> /// Defines a class which can be used to invoke a pipeline of commands. /// </summary> public abstract class Pipeline : IDisposable { #region constructor /// <summary> /// Explicit default constructor. /// </summary> internal Pipeline(Runspace runspace) : this(runspace, new CommandCollection()) { } /// <summary> /// Constructor to initialize both Runspace and Command to invoke. /// Caller should make sure that "command" is not null. /// </summary> /// <param name="runspace"> /// Runspace to use for the command invocation. /// </param> /// <param name="command"> /// command to Invoke. /// Caller should make sure that "command" is not null. /// </param> internal Pipeline(Runspace runspace, CommandCollection command) { if (runspace == null) { PSTraceSource.NewArgumentNullException(nameof(runspace)); } // This constructor is used only internally. // Caller should make sure the input is valid Dbg.Assert(command != null, "Command cannot be null"); InstanceId = runspace.GeneratePipelineId(); Commands = command; // Reset the AMSI session so that it is re-initialized // when the next script block is parsed. AmsiUtils.CloseSession(); } #endregion constructor #region properties /// <summary> /// Gets the runspace this pipeline is created on. /// </summary> public abstract Runspace Runspace { get; } /// <summary> /// Gets the property which indicates if this pipeline is nested. /// </summary> public abstract bool IsNested { get; } /// <summary> /// Gets the property which indicates if this pipeline is a child pipeline. /// /// IsChild flag makes it possible for the pipeline to differentiate between /// a true v1 nested pipeline and the cmdlets calling cmdlets case. See bug /// 211462. /// </summary> internal virtual bool IsChild { get { return false; } set { } } /// <summary> /// Gets input writer for this pipeline. /// </summary> /// <remarks> /// When the caller calls Input.Write(), the caller writes to the /// input of the pipeline. Thus, <paramref name="Input"/> /// is a PipelineWriter or "thing which can be written to". /// Note:Input must be closed after Pipeline.InvokeAsync for InvokeAsync to /// finish. /// </remarks> public abstract PipelineWriter Input { get; } /// <summary> /// Gets the output reader for this pipeline. /// </summary> /// <remarks> /// When the caller calls Output.Read(), the caller reads from the /// output of the pipeline. Thus, <paramref name="Output"/> /// is a PipelineReader or "thing which can be read from". /// </remarks> public abstract PipelineReader<PSObject> Output { get; } /// <summary> /// Gets the error output reader for this pipeline. /// </summary> /// <remarks> /// When the caller calls Error.Read(), the caller reads from the /// output of the pipeline. Thus, <paramref name="Error"/> /// is a PipelineReader or "thing which can be read from". /// /// This is the non-terminating error stream from the command. /// In this release, the objects read from this PipelineReader /// are PSObjects wrapping ErrorRecords. /// </remarks> public abstract PipelineReader<object> Error { get; } /// <summary> /// Gets Info about current state of the pipeline. /// </summary> /// <remarks> /// This value indicates the state of the pipeline after the change. /// </remarks> public abstract PipelineStateInfo PipelineStateInfo { get; } /// <summary> /// True if pipeline execution encountered and error. /// It will always be true if _reason is non-null /// since an exception occurred. For other error types, /// It has to be set manually. /// </summary> public virtual bool HadErrors { get { return _hadErrors; } } private bool _hadErrors; internal void SetHadErrors(bool status) { _hadErrors = _hadErrors || status; } /// <summary> /// Gets the unique identifier for this pipeline. This identifier is unique with in /// the scope of Runspace. /// </summary> public long InstanceId { get; } /// <summary> /// Gets the collection of commands for this pipeline. /// </summary> public CommandCollection Commands { get; private set; } /// <summary> /// If this property is true, SessionState is updated for this /// pipeline state. /// </summary> public bool SetPipelineSessionState { get; set; } = true; /// <summary> /// Settings for the pipeline invocation thread. /// </summary> internal PSInvocationSettings InvocationSettings { get; set; } /// <summary> /// If this flag is true, the commands in this Pipeline will redirect the global error output pipe /// (ExecutionContext.ShellFunctionErrorOutputPipe) to the command's error output pipe. /// /// When the global error output pipe is not set, $ErrorActionPreference is not checked and all /// errors are treated as terminating errors. /// /// On V1, the global error output pipe is redirected to the command's error output pipe only when /// it has already been redirected. The command-line host achieves this redirection by merging the /// error output into the output pipe so it checks $ErrorActionPreference all right. However, when /// the Pipeline class is used programmatically the global error output pipe is not set and the first /// error terminates the pipeline. /// /// This flag is used to force the redirection. By default it is false to maintain compatibility with /// V1, but the V2 hosting interface (PowerShell class) sets this flag to true to ensure the global /// error output pipe is always set and $ErrorActionPreference is checked when invoking the Pipeline. /// </summary> internal bool RedirectShellErrorOutputPipe { get; set; } = false; #endregion properties #region events /// <summary> /// Event raised when Pipeline's state changes. /// </summary> public abstract event EventHandler<PipelineStateEventArgs> StateChanged; #endregion events #region methods /// <summary> /// Invoke the pipeline, synchronously, returning the results as an array of /// objects. /// </summary> /// <remarks>If using synchronous invoke, do not close /// input objectWriter. Synchronous invoke will always close the input /// objectWriter. /// </remarks> /// <exception cref="InvalidOperationException"> /// No command is added to pipeline /// </exception> /// <exception cref="InvalidPipelineStateException"> /// PipelineState is not NotStarted. /// </exception> /// <exception cref="InvalidOperationException"> /// 1) A pipeline is already executing. Pipeline cannot execute /// concurrently. /// 2) Attempt is made to invoke a nested pipeline directly. Nested /// pipeline must be invoked from a running pipeline. /// </exception> /// <exception cref="InvalidRunspaceStateException"> /// RunspaceState is not Open /// </exception> /// <exception cref="ObjectDisposedException"> /// Pipeline already disposed /// </exception> /// <exception cref="ScriptCallDepthException"> /// The script recursed too deeply into script functions. /// There is a fixed limit on the depth of recursion. /// </exception> /// <exception cref="System.Security.SecurityException"> /// A CLR security violation occurred. Typically, this happens /// because the current CLR permissions do not allow adequate /// reflection access to a cmdlet assembly. /// </exception> /// <exception cref="RuntimeException"> /// Pipeline.Invoke can throw a variety of exceptions derived /// from RuntimeException. The most likely of these exceptions /// are listed below. /// </exception> /// <exception cref="ParameterBindingException"> /// One of more parameters or parameter values specified for /// a cmdlet are not valid, or mandatory parameters for a cmdlet /// were not specified. /// </exception> /// <exception cref="CmdletInvocationException"> /// A cmdlet generated a terminating error. /// </exception> /// <exception cref="CmdletProviderInvocationException"> /// A provider generated a terminating error. /// </exception> /// <exception cref="ActionPreferenceStopException"> /// The ActionPreference.Stop or ActionPreference.Inquire policy /// triggered a terminating error. /// </exception> /// <exception cref="PipelineStoppedException"> /// The pipeline was terminated asynchronously. /// </exception> /// <exception cref="MetadataException"> /// If there is an error generating the metadata for dynamic parameters. /// </exception> public Collection<PSObject> Invoke() { return Invoke(null); } /// <summary> /// Invoke the pipeline, synchronously, returning the results as an array of objects. /// </summary> /// <param name="input">an array of input objects to pass to the pipeline. /// Array may be empty but may not be null</param> /// <returns>An array of zero or more result objects.</returns> /// <remarks>If using synchronous exectute, do not close /// input objectWriter. Synchronous invoke will always close the input /// objectWriter. /// </remarks> /// <exception cref="InvalidOperationException"> /// No command is added to pipeline /// </exception> /// <exception cref="InvalidPipelineStateException"> /// PipelineState is not NotStarted. /// </exception> /// <exception cref="InvalidOperationException"> /// 1) A pipeline is already executing. Pipeline cannot execute /// concurrently. /// 2) Attempt is made to invoke a nested pipeline directly. Nested /// pipeline must be invoked from a running pipeline. /// </exception> /// <exception cref="InvalidRunspaceStateException"> /// RunspaceState is not Open /// </exception> /// <exception cref="ObjectDisposedException"> /// Pipeline already disposed /// </exception> /// <exception cref="ScriptCallDepthException"> /// The script recursed too deeply into script functions. /// There is a fixed limit on the depth of recursion. /// </exception> /// <exception cref="System.Security.SecurityException"> /// A CLR security violation occurred. Typically, this happens /// because the current CLR permissions do not allow adequate /// reflection access to a cmdlet assembly. /// </exception> /// <exception cref="RuntimeException"> /// Pipeline.Invoke can throw a variety of exceptions derived /// from RuntimeException. The most likely of these exceptions /// are listed below. /// </exception> /// <exception cref="ParameterBindingException"> /// One of more parameters or parameter values specified for /// a cmdlet are not valid, or mandatory parameters for a cmdlet /// were not specified. /// </exception> /// <exception cref="CmdletInvocationException"> /// A cmdlet generated a terminating error. /// </exception> /// <exception cref="CmdletProviderInvocationException"> /// A provider generated a terminating error. /// </exception> /// <exception cref="ActionPreferenceStopException"> /// The ActionPreference.Stop or ActionPreference.Inquire policy /// triggered a terminating error. /// </exception> /// <exception cref="PipelineStoppedException"> /// The pipeline was terminated asynchronously. /// </exception> /// <exception cref="MetadataException"> /// If there is an error generating the metadata for dynamic parameters. /// </exception> public abstract Collection<PSObject> Invoke(IEnumerable input); /// <summary> /// Invoke the pipeline asynchronously. /// </summary> /// <remarks> /// 1) Results are returned through the <see cref="Pipeline.Output"/> reader. /// 2) When pipeline is invoked using InvokeAsync, invocation doesn't /// finish until Input to pipeline is closed. Caller of InvokeAsync must close /// the input pipe after all input has been written to input pipe. Input pipe /// is closed by calling Pipeline.Input.Close(); /// /// If you want this pipeline to execute as a standalone command /// (that is, using command-line parameters only), /// be sure to call Pipeline.Input.Close() before calling /// InvokeAsync(). Otherwise, the command will be executed /// as though it had external input. If you observe that the /// command isn't doing anything, this may be the reason. /// </remarks> /// <exception cref="InvalidOperationException"> /// No command is added to pipeline /// </exception> /// <exception cref="InvalidPipelineStateException"> /// PipelineState is not NotStarted. /// </exception> /// <exception cref="InvalidOperationException"> /// 1) A pipeline is already executing. Pipeline cannot execute /// concurrently. /// 2) InvokeAsync is called on nested pipeline. Nested pipeline /// cannot be executed Asynchronously. /// </exception> /// <exception cref="InvalidRunspaceStateException"> /// RunspaceState is not Open /// </exception> /// <exception cref="ObjectDisposedException"> /// Pipeline already disposed /// </exception> public abstract void InvokeAsync(); /// <summary> /// Synchronous call to stop the running pipeline. /// </summary> public abstract void Stop(); /// <summary> /// Asynchronous call to stop the running pipeline. /// </summary> public abstract void StopAsync(); /// <summary> /// Creates a new <see cref="Pipeline"/> that is a copy of the current instance. /// </summary> /// <returns>A new <see cref="Pipeline"/> that is a copy of this instance.</returns> public abstract Pipeline Copy(); /// <summary> /// Connects synchronously to a running command on a remote server. /// The pipeline object must be in the disconnected state. /// </summary> /// <returns>A collection of result objects.</returns> public abstract Collection<PSObject> Connect(); /// <summary> /// Connects asynchronously to a running command on a remote server. /// </summary> public abstract void ConnectAsync(); /// <summary> /// Sets the command collection. /// </summary> /// <param name="commands">Command collection to set.</param> /// <remarks>called by ClientRemotePipeline</remarks> internal void SetCommandCollection(CommandCollection commands) { Commands = commands; } /// <summary> /// Sets the history string to the one that is specified. /// </summary> /// <param name="historyString">History string to set.</param> internal abstract void SetHistoryString(string historyString); /// <summary> /// Invokes a remote command and immediately disconnects if /// transport layer supports it. /// </summary> internal abstract void InvokeAsyncAndDisconnect(); #endregion methods #region Remote data drain/block methods /// <summary> /// Blocks data arriving from remote session. /// </summary> internal virtual void SuspendIncomingData() { throw new PSNotImplementedException(); } /// <summary> /// Resumes data arrive from remote session. /// </summary> internal virtual void ResumeIncomingData() { throw new PSNotImplementedException(); } /// <summary> /// Blocking call that waits until the current remote data /// queue is empty. /// </summary> internal virtual void DrainIncomingData() { throw new PSNotImplementedException(); } #endregion #region IDisposable Members /// <summary> /// Disposes the pipeline. If pipeline is running, dispose first /// stops the pipeline. /// </summary> public void Dispose() { Dispose(!IsChild); GC.SuppressFinalize(this); } /// <summary> /// Protected dispose which can be overridden by derived classes. /// </summary> /// <param name="disposing"></param> protected virtual void Dispose(bool disposing) { } #endregion IDisposable Members } }