MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.ObjectModel;6using System.Management.Automation.Internal;7using System.Runtime.Serialization;8using System.Threading;9 10using Dbg = System.Management.Automation.Diagnostics;11 12namespace System.Management.Automation.Runspaces13{14 #region Exceptions15 /// <summary>16 /// Defines exception which is thrown when state of the pipeline is different17 /// from expected state.18 /// </summary>19 public class InvalidPipelineStateException : SystemException20 {21 /// <summary>22 /// Initializes a new instance of the InvalidPipelineStateException class.23 /// </summary>24 public InvalidPipelineStateException()25 : base(StringUtil.Format(RunspaceStrings.InvalidPipelineStateStateGeneral))26 {27 }28 29 /// <summary>30 /// Initializes a new instance of the InvalidPipelineStateException class31 /// with a specified error message.32 /// </summary>33 /// <param name="message">34 /// The error message that explains the reason for the exception.35 /// </param>36 public InvalidPipelineStateException(string message)37 : base(message)38 {39 }40 41 /// <summary>42 /// Initializes a new instance of the InvalidPipelineStateException class43 /// with a specified error message and a reference to the inner exception that is the cause of this exception.44 /// </summary>45 /// <param name="message">46 /// The error message that explains the reason for the exception.47 /// </param>48 /// <param name="innerException">49 /// The exception that is the cause of the current exception.50 /// </param>51 public InvalidPipelineStateException(string message, Exception innerException)52 : base(message, innerException)53 {54 }55 56 /// <summary>57 /// Initializes a new instance of the InvalidPipelineStateException and defines value of58 /// CurrentState and ExpectedState.59 /// </summary>60 /// <param name="message">The error message that explains the reason for the exception.61 /// </param>62 /// <param name="currentState">Current state of pipeline.</param>63 /// <param name="expectedState">Expected state of pipeline.</param>64 internal InvalidPipelineStateException(string message, PipelineState currentState, PipelineState expectedState)65 : base(message)66 {67 _expectedState = expectedState;68 _currentState = currentState;69 }70 71 #region ISerializable Members72 73 // 2005/04/20-JonN No need to implement GetObjectData74 // if all fields are static or [NonSerialized]75 76 /// <summary>77 /// Initializes a new instance of the <see cref="InvalidPipelineStateException"/>78 /// class with serialized data.79 /// </summary>80 /// <param name="info">81 /// The <see cref="SerializationInfo"/> that holds the serialized object82 /// data about the exception being thrown.83 /// </param>84 /// <param name="context">85 /// The <see cref="StreamingContext"/> that contains contextual information86 /// about the source or destination.87 /// </param>88 [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")]89 private InvalidPipelineStateException(SerializationInfo info, StreamingContext context)90 {91 throw new NotSupportedException();92 }93 94 #endregion95 96 /// <summary>97 /// Gets CurrentState of the pipeline.98 /// </summary>99 public PipelineState CurrentState100 {101 get { return _currentState; }102 }103 104 /// <summary>105 /// Gets ExpectedState of the pipeline.106 /// </summary>107 public PipelineState ExpectedState108 {109 get { return _expectedState; }110 }111 112 /// <summary>113 /// State of pipeline when exception was thrown.114 /// </summary>115 [NonSerialized]116 private readonly PipelineState _currentState = 0;117 118 /// <summary>119 /// States of the pipeline expected in method which throws this exception.120 /// </summary>121 [NonSerialized]122 private readonly PipelineState _expectedState = 0;123 }124 125 #endregion Exceptions126 127 #region PipelineState128 129 /// <summary>130 /// Enumerated type defining the state of the Pipeline.131 /// </summary>132 public enum PipelineState133 {134 /// <summary>135 /// The pipeline has not been started.136 /// </summary>137 NotStarted = 0,138 /// <summary>139 /// The pipeline is executing.140 /// </summary>141 Running = 1,142 /// <summary>143 /// The pipeline is stoping execution.144 /// </summary>145 Stopping = 2,146 /// <summary>147 /// The pipeline is completed due to a stop request.148 /// </summary>149 Stopped = 3,150 /// <summary>151 /// The pipeline has completed.152 /// </summary>153 Completed = 4,154 /// <summary>155 /// The pipeline completed abnormally due to an error.156 /// </summary>157 Failed = 5,158 /// <summary>159 /// The pipeline is disconnected from remote running command.160 /// </summary>161 Disconnected = 6162 }163 164 /// <summary>165 /// Type which has information about PipelineState and Exception166 /// associated with PipelineState.167 /// </summary>168 public sealed class PipelineStateInfo169 {170 #region constructors171 172 /// <summary>173 /// Constructor for state changes not resulting from an error.174 /// </summary>175 /// <param name="state">Execution state.</param>176 internal PipelineStateInfo(PipelineState state)177 : this(state, null)178 {179 }180 181 /// <summary>182 /// Constructor for state changes with an optional error.183 /// </summary>184 /// <param name="state">The new state.</param>185 /// <param name="reason">A non-null exception if the state change was186 /// caused by an error,otherwise; null.187 /// </param>188 internal PipelineStateInfo(PipelineState state, Exception reason)189 {190 State = state;191 Reason = reason;192 }193 194 /// <summary>195 /// Copy constructor to support cloning.196 /// </summary>197 /// <param name="pipelineStateInfo">Source information.</param>198 /// <throws>199 /// ArgumentNullException when <paramref name="pipelineStateInfo"/> is null.200 /// </throws>201 internal PipelineStateInfo(PipelineStateInfo pipelineStateInfo)202 {203 Dbg.Assert(pipelineStateInfo != null, "caller should validate the parameter");204 205 State = pipelineStateInfo.State;206 Reason = pipelineStateInfo.Reason;207 }208 209 #endregion constructors210 211 #region public_properties212 213 /// <summary>214 /// The state of the runspace.215 /// </summary>216 /// <remarks>217 /// This value indicates the state of the pipeline after the change.218 /// </remarks>219 public PipelineState State { get; }220 221 /// <summary>222 /// The reason for the state change, if caused by an error.223 /// </summary>224 /// <remarks>225 /// The value of this property is non-null if the state226 /// changed due to an error. Otherwise, the value of this227 /// property is null.228 /// </remarks>229 public Exception Reason { get; }230 231 #endregion public_properties232 233 /// <summary>234 /// Clones this object.235 /// </summary>236 /// <returns>Cloned object.</returns>237 internal PipelineStateInfo Clone()238 {239 return new PipelineStateInfo(this);240 }241 }242 243 /// <summary>244 /// Event arguments passed to PipelineStateEvent handlers245 /// <see cref="Pipeline.StateChanged"/> event.246 /// </summary>247 public sealed class PipelineStateEventArgs : EventArgs248 {249 #region constructors250 251 /// <summary>252 /// Constructor PipelineStateEventArgs from PipelineStateInfo.253 /// </summary>254 /// <param name="pipelineStateInfo">The current state of the255 /// pipeline.</param>256 /// <throws>257 /// ArgumentNullException when <paramref name="pipelineStateInfo"/> is null.258 /// </throws>259 internal PipelineStateEventArgs(PipelineStateInfo pipelineStateInfo)260 {261 Dbg.Assert(pipelineStateInfo != null, "caller should validate the parameter");262 PipelineStateInfo = pipelineStateInfo;263 }264 265 #endregion constructors266 267 #region public_properties268 269 /// <summary>270 /// Info about current state of pipeline.271 /// </summary>272 public PipelineStateInfo PipelineStateInfo { get; }273 274 #endregion public_properties275 }276 #endregion ExecutionState277 278 /// <summary>279 /// Defines a class which can be used to invoke a pipeline of commands.280 /// </summary>281 public abstract class Pipeline : IDisposable282 {283 #region constructor284 285 /// <summary>286 /// Explicit default constructor.287 /// </summary>288 internal Pipeline(Runspace runspace)289 : this(runspace, new CommandCollection())290 {291 }292 293 /// <summary>294 /// Constructor to initialize both Runspace and Command to invoke.295 /// Caller should make sure that "command" is not null.296 /// </summary>297 /// <param name="runspace">298 /// Runspace to use for the command invocation.299 /// </param>300 /// <param name="command">301 /// command to Invoke.302 /// Caller should make sure that "command" is not null.303 /// </param>304 internal Pipeline(Runspace runspace, CommandCollection command)305 {306 if (runspace == null)307 {308 PSTraceSource.NewArgumentNullException(nameof(runspace));309 }310 // This constructor is used only internally.311 // Caller should make sure the input is valid312 Dbg.Assert(command != null, "Command cannot be null");313 InstanceId = runspace.GeneratePipelineId();314 Commands = command;315 316 // Reset the AMSI session so that it is re-initialized317 // when the next script block is parsed.318 AmsiUtils.CloseSession();319 }320 321 #endregion constructor322 323 #region properties324 325 /// <summary>326 /// Gets the runspace this pipeline is created on.327 /// </summary>328 public abstract Runspace Runspace { get; }329 330 /// <summary>331 /// Gets the property which indicates if this pipeline is nested.332 /// </summary>333 public abstract bool IsNested { get; }334 335 /// <summary>336 /// Gets the property which indicates if this pipeline is a child pipeline.337 ///338 /// IsChild flag makes it possible for the pipeline to differentiate between339 /// a true v1 nested pipeline and the cmdlets calling cmdlets case. See bug340 /// 211462.341 /// </summary>342 internal virtual bool IsChild343 {344 get { return false; }345 346 set { }347 }348 349 /// <summary>350 /// Gets input writer for this pipeline.351 /// </summary>352 /// <remarks>353 /// When the caller calls Input.Write(), the caller writes to the354 /// input of the pipeline. Thus, <paramref name="Input"/>355 /// is a PipelineWriter or "thing which can be written to".356 /// Note:Input must be closed after Pipeline.InvokeAsync for InvokeAsync to357 /// finish.358 /// </remarks>359 public abstract PipelineWriter Input { get; }360 361 /// <summary>362 /// Gets the output reader for this pipeline.363 /// </summary>364 /// <remarks>365 /// When the caller calls Output.Read(), the caller reads from the366 /// output of the pipeline. Thus, <paramref name="Output"/>367 /// is a PipelineReader or "thing which can be read from".368 /// </remarks>369 public abstract PipelineReader<PSObject> Output { get; }370 371 /// <summary>372 /// Gets the error output reader for this pipeline.373 /// </summary>374 /// <remarks>375 /// When the caller calls Error.Read(), the caller reads from the376 /// output of the pipeline. Thus, <paramref name="Error"/>377 /// is a PipelineReader or "thing which can be read from".378 ///379 /// This is the non-terminating error stream from the command.380 /// In this release, the objects read from this PipelineReader381 /// are PSObjects wrapping ErrorRecords.382 /// </remarks>383 public abstract PipelineReader<object> Error { get; }384 385 /// <summary>386 /// Gets Info about current state of the pipeline.387 /// </summary>388 /// <remarks>389 /// This value indicates the state of the pipeline after the change.390 /// </remarks>391 public abstract PipelineStateInfo PipelineStateInfo { get; }392 393 /// <summary>394 /// True if pipeline execution encountered and error.395 /// It will always be true if _reason is non-null396 /// since an exception occurred. For other error types,397 /// It has to be set manually.398 /// </summary>399 public virtual bool HadErrors400 {401 get { return _hadErrors; }402 }403 404 private bool _hadErrors;405 406 internal void SetHadErrors(bool status)407 {408 _hadErrors = _hadErrors || status;409 }410 411 /// <summary>412 /// Gets the unique identifier for this pipeline. This identifier is unique with in413 /// the scope of Runspace.414 /// </summary>415 public long InstanceId { get; }416 417 /// <summary>418 /// Gets the collection of commands for this pipeline.419 /// </summary>420 public CommandCollection Commands { get; private set; }421 422 /// <summary>423 /// If this property is true, SessionState is updated for this424 /// pipeline state.425 /// </summary>426 public bool SetPipelineSessionState { get; set; } = true;427 428 /// <summary>429 /// Settings for the pipeline invocation thread.430 /// </summary>431 internal PSInvocationSettings InvocationSettings { get; set; }432 433 /// <summary>434 /// If this flag is true, the commands in this Pipeline will redirect the global error output pipe435 /// (ExecutionContext.ShellFunctionErrorOutputPipe) to the command's error output pipe.436 ///437 /// When the global error output pipe is not set, $ErrorActionPreference is not checked and all438 /// errors are treated as terminating errors.439 ///440 /// On V1, the global error output pipe is redirected to the command's error output pipe only when441 /// it has already been redirected. The command-line host achieves this redirection by merging the442 /// error output into the output pipe so it checks $ErrorActionPreference all right. However, when443 /// the Pipeline class is used programmatically the global error output pipe is not set and the first444 /// error terminates the pipeline.445 ///446 /// This flag is used to force the redirection. By default it is false to maintain compatibility with447 /// V1, but the V2 hosting interface (PowerShell class) sets this flag to true to ensure the global448 /// error output pipe is always set and $ErrorActionPreference is checked when invoking the Pipeline.449 /// </summary>450 internal bool RedirectShellErrorOutputPipe { get; set; } = false;451 452 #endregion properties453 454 #region events455 456 /// <summary>457 /// Event raised when Pipeline's state changes.458 /// </summary>459 public abstract event EventHandler<PipelineStateEventArgs> StateChanged;460 461 #endregion events462 463 #region methods464 465 /// <summary>466 /// Invoke the pipeline, synchronously, returning the results as an array of467 /// objects.468 /// </summary>469 /// <remarks>If using synchronous invoke, do not close470 /// input objectWriter. Synchronous invoke will always close the input471 /// objectWriter.472 /// </remarks>473 /// <exception cref="InvalidOperationException">474 /// No command is added to pipeline475 /// </exception>476 /// <exception cref="InvalidPipelineStateException">477 /// PipelineState is not NotStarted.478 /// </exception>479 /// <exception cref="InvalidOperationException">480 /// 1) A pipeline is already executing. Pipeline cannot execute481 /// concurrently.482 /// 2) Attempt is made to invoke a nested pipeline directly. Nested483 /// pipeline must be invoked from a running pipeline.484 /// </exception>485 /// <exception cref="InvalidRunspaceStateException">486 /// RunspaceState is not Open487 /// </exception>488 /// <exception cref="ObjectDisposedException">489 /// Pipeline already disposed490 /// </exception>491 /// <exception cref="ScriptCallDepthException">492 /// The script recursed too deeply into script functions.493 /// There is a fixed limit on the depth of recursion.494 /// </exception>495 /// <exception cref="System.Security.SecurityException">496 /// A CLR security violation occurred. Typically, this happens497 /// because the current CLR permissions do not allow adequate498 /// reflection access to a cmdlet assembly.499 /// </exception>500 /// <exception cref="RuntimeException">501 /// Pipeline.Invoke can throw a variety of exceptions derived502 /// from RuntimeException. The most likely of these exceptions503 /// are listed below.504 /// </exception>505 /// <exception cref="ParameterBindingException">506 /// One of more parameters or parameter values specified for507 /// a cmdlet are not valid, or mandatory parameters for a cmdlet508 /// were not specified.509 /// </exception>510 /// <exception cref="CmdletInvocationException">511 /// A cmdlet generated a terminating error.512 /// </exception>513 /// <exception cref="CmdletProviderInvocationException">514 /// A provider generated a terminating error.515 /// </exception>516 /// <exception cref="ActionPreferenceStopException">517 /// The ActionPreference.Stop or ActionPreference.Inquire policy518 /// triggered a terminating error.519 /// </exception>520 /// <exception cref="PipelineStoppedException">521 /// The pipeline was terminated asynchronously.522 /// </exception>523 /// <exception cref="MetadataException">524 /// If there is an error generating the metadata for dynamic parameters.525 /// </exception>526 public Collection<PSObject> Invoke()527 {528 return Invoke(null);529 }530 531 /// <summary>532 /// Invoke the pipeline, synchronously, returning the results as an array of objects.533 /// </summary>534 /// <param name="input">an array of input objects to pass to the pipeline.535 /// Array may be empty but may not be null</param>536 /// <returns>An array of zero or more result objects.</returns>537 /// <remarks>If using synchronous exectute, do not close538 /// input objectWriter. Synchronous invoke will always close the input539 /// objectWriter.540 /// </remarks>541 /// <exception cref="InvalidOperationException">542 /// No command is added to pipeline543 /// </exception>544 /// <exception cref="InvalidPipelineStateException">545 /// PipelineState is not NotStarted.546 /// </exception>547 /// <exception cref="InvalidOperationException">548 /// 1) A pipeline is already executing. Pipeline cannot execute549 /// concurrently.550 /// 2) Attempt is made to invoke a nested pipeline directly. Nested551 /// pipeline must be invoked from a running pipeline.552 /// </exception>553 /// <exception cref="InvalidRunspaceStateException">554 /// RunspaceState is not Open555 /// </exception>556 /// <exception cref="ObjectDisposedException">557 /// Pipeline already disposed558 /// </exception>559 /// <exception cref="ScriptCallDepthException">560 /// The script recursed too deeply into script functions.561 /// There is a fixed limit on the depth of recursion.562 /// </exception>563 /// <exception cref="System.Security.SecurityException">564 /// A CLR security violation occurred. Typically, this happens565 /// because the current CLR permissions do not allow adequate566 /// reflection access to a cmdlet assembly.567 /// </exception>568 /// <exception cref="RuntimeException">569 /// Pipeline.Invoke can throw a variety of exceptions derived570 /// from RuntimeException. The most likely of these exceptions571 /// are listed below.572 /// </exception>573 /// <exception cref="ParameterBindingException">574 /// One of more parameters or parameter values specified for575 /// a cmdlet are not valid, or mandatory parameters for a cmdlet576 /// were not specified.577 /// </exception>578 /// <exception cref="CmdletInvocationException">579 /// A cmdlet generated a terminating error.580 /// </exception>581 /// <exception cref="CmdletProviderInvocationException">582 /// A provider generated a terminating error.583 /// </exception>584 /// <exception cref="ActionPreferenceStopException">585 /// The ActionPreference.Stop or ActionPreference.Inquire policy586 /// triggered a terminating error.587 /// </exception>588 /// <exception cref="PipelineStoppedException">589 /// The pipeline was terminated asynchronously.590 /// </exception>591 /// <exception cref="MetadataException">592 /// If there is an error generating the metadata for dynamic parameters.593 /// </exception>594 public abstract Collection<PSObject> Invoke(IEnumerable input);595 596 /// <summary>597 /// Invoke the pipeline asynchronously.598 /// </summary>599 /// <remarks>600 /// 1) Results are returned through the <see cref="Pipeline.Output"/> reader.601 /// 2) When pipeline is invoked using InvokeAsync, invocation doesn't602 /// finish until Input to pipeline is closed. Caller of InvokeAsync must close603 /// the input pipe after all input has been written to input pipe. Input pipe604 /// is closed by calling Pipeline.Input.Close();605 ///606 /// If you want this pipeline to execute as a standalone command607 /// (that is, using command-line parameters only),608 /// be sure to call Pipeline.Input.Close() before calling609 /// InvokeAsync(). Otherwise, the command will be executed610 /// as though it had external input. If you observe that the611 /// command isn't doing anything, this may be the reason.612 /// </remarks>613 /// <exception cref="InvalidOperationException">614 /// No command is added to pipeline615 /// </exception>616 /// <exception cref="InvalidPipelineStateException">617 /// PipelineState is not NotStarted.618 /// </exception>619 /// <exception cref="InvalidOperationException">620 /// 1) A pipeline is already executing. Pipeline cannot execute621 /// concurrently.622 /// 2) InvokeAsync is called on nested pipeline. Nested pipeline623 /// cannot be executed Asynchronously.624 /// </exception>625 /// <exception cref="InvalidRunspaceStateException">626 /// RunspaceState is not Open627 /// </exception>628 /// <exception cref="ObjectDisposedException">629 /// Pipeline already disposed630 /// </exception>631 public abstract void InvokeAsync();632 633 /// <summary>634 /// Synchronous call to stop the running pipeline.635 /// </summary>636 public abstract void Stop();637 638 /// <summary>639 /// Asynchronous call to stop the running pipeline.640 /// </summary>641 public abstract void StopAsync();642 643 /// <summary>644 /// Creates a new <see cref="Pipeline"/> that is a copy of the current instance.645 /// </summary>646 /// <returns>A new <see cref="Pipeline"/> that is a copy of this instance.</returns>647 public abstract Pipeline Copy();648 649 /// <summary>650 /// Connects synchronously to a running command on a remote server.651 /// The pipeline object must be in the disconnected state.652 /// </summary>653 /// <returns>A collection of result objects.</returns>654 public abstract Collection<PSObject> Connect();655 656 /// <summary>657 /// Connects asynchronously to a running command on a remote server.658 /// </summary>659 public abstract void ConnectAsync();660 661 /// <summary>662 /// Sets the command collection.663 /// </summary>664 /// <param name="commands">Command collection to set.</param>665 /// <remarks>called by ClientRemotePipeline</remarks>666 internal void SetCommandCollection(CommandCollection commands)667 {668 Commands = commands;669 }670 671 /// <summary>672 /// Sets the history string to the one that is specified.673 /// </summary>674 /// <param name="historyString">History string to set.</param>675 internal abstract void SetHistoryString(string historyString);676 677 /// <summary>678 /// Invokes a remote command and immediately disconnects if679 /// transport layer supports it.680 /// </summary>681 internal abstract void InvokeAsyncAndDisconnect();682 683 #endregion methods684 685 #region Remote data drain/block methods686 687 /// <summary>688 /// Blocks data arriving from remote session.689 /// </summary>690 internal virtual void SuspendIncomingData()691 {692 throw new PSNotImplementedException();693 }694 695 /// <summary>696 /// Resumes data arrive from remote session.697 /// </summary>698 internal virtual void ResumeIncomingData()699 {700 throw new PSNotImplementedException();701 }702 703 /// <summary>704 /// Blocking call that waits until the current remote data705 /// queue is empty.706 /// </summary>707 internal virtual void DrainIncomingData()708 {709 throw new PSNotImplementedException();710 }711 712 #endregion713 714 #region IDisposable Members715 716 /// <summary>717 /// Disposes the pipeline. If pipeline is running, dispose first718 /// stops the pipeline.719 /// </summary>720 public721 void722 Dispose()723 {724 Dispose(!IsChild);725 GC.SuppressFinalize(this);726 }727 728 /// <summary>729 /// Protected dispose which can be overridden by derived classes.730 /// </summary>731 /// <param name="disposing"></param>732 protected virtual733 void734 Dispose(bool disposing)735 {736 }737 738 #endregion IDisposable Members739 }740}741 