MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Collections.ObjectModel;8using System.Diagnostics;9using System.Diagnostics.CodeAnalysis;10using System.Management.Automation;11using System.Management.Automation.Host;12using System.Management.Automation.Internal;13using System.Management.Automation.Runspaces;14using System.Management.Automation.Runspaces.Internal;15using System.Runtime.Serialization;16using System.Threading;17using System.Threading.Tasks;18 19using Microsoft.Management.Infrastructure;20using Microsoft.PowerShell.Telemetry;21 22using Dbg = System.Management.Automation.Diagnostics;23 24#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings25 26namespace System.Management.Automation27{28 #region Exceptions29 30 /// <summary>31 /// Defines exception which is thrown when state of the PowerShell is different32 /// from the expected state.33 /// </summary>34 public class InvalidPowerShellStateException : SystemException35 {36 /// <summary>37 /// Creates a new instance of InvalidPowershellStateException class.38 /// </summary>39 public InvalidPowerShellStateException()40 : base41 (StringUtil.Format(PowerShellStrings.InvalidPowerShellStateGeneral))42 {43 }44 45 /// <summary>46 /// Creates a new instance of InvalidPowershellStateException class.47 /// </summary>48 /// <param name="message">49 /// The error message that explains the reason for the exception.50 /// </param>51 public InvalidPowerShellStateException(string message)52 : base(message)53 {54 }55 56 /// <summary>57 /// Creates a new instance of InvalidPowershellStateException class.58 /// </summary>59 /// <param name="message">60 /// The error message that explains the reason for the exception.61 /// </param>62 /// <param name="innerException">63 /// The exception that is the cause of the current exception.64 /// </param>65 public InvalidPowerShellStateException(string message, Exception innerException)66 : base(message, innerException)67 {68 }69 70 /// <summary>71 /// Initializes a new instance of the InvalidPowerShellStateException and defines value of72 /// CurrentState.73 /// </summary>74 /// <param name="currentState">Current state of powershell.</param>75 internal InvalidPowerShellStateException(PSInvocationState currentState)76 : base77 (StringUtil.Format(PowerShellStrings.InvalidPowerShellStateGeneral))78 {79 _currState = currentState;80 }81 82 #region ISerializable Members83 84 // No need to implement GetObjectData85 // if all fields are static or [NonSerialized]86 87 /// <summary>88 /// Initializes a new instance of the InvalidPowerShellStateException89 /// class with serialized data.90 /// </summary>91 /// <param name="info">92 /// The <see cref="SerializationInfo"/> that holds the serialized object93 /// data about the exception being thrown.94 /// </param>95 /// <param name="context">96 /// The <see cref="StreamingContext"/> that contains contextual information97 /// about the source or destination.98 /// </param>99 [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")]100 protected101 InvalidPowerShellStateException(SerializationInfo info, StreamingContext context)102 {103 throw new NotSupportedException();104 }105 106 #endregion107 108 /// <summary>109 /// Gets CurrentState of the powershell.110 /// </summary>111 public PSInvocationState CurrentState112 {113 get114 {115 return _currState;116 }117 }118 119 /// <summary>120 /// State of powershell when exception was thrown.121 /// </summary>122 [NonSerialized]123 private readonly PSInvocationState _currState = 0;124 }125 126 #endregion127 128 #region PSInvocationState, PSInvocationStateInfo, PSInvocationStateChangedEventArgs129 130 /// <summary>131 /// Enumerated type defining the state of the PowerShell.132 /// </summary>133 public enum PSInvocationState134 {135 /// <summary>136 /// PowerShell has not been started.137 /// </summary>138 NotStarted = 0,139 /// <summary>140 /// PowerShell is executing.141 /// </summary>142 Running = 1,143 /// <summary>144 /// PowerShell is stoping execution.145 /// </summary>146 Stopping = 2,147 /// <summary>148 /// PowerShell is completed due to a stop request.149 /// </summary>150 Stopped = 3,151 /// <summary>152 /// PowerShell has completed executing a command.153 /// </summary>154 Completed = 4,155 /// <summary>156 /// PowerShell completed abnormally due to an error.157 /// </summary>158 Failed = 5,159 /// <summary>160 /// PowerShell is in disconnected state.161 /// </summary>162 Disconnected = 6163 }164 165 /// <summary>166 /// Enumerated type defining runspace modes for nested pipeline.167 /// </summary>168 public enum RunspaceMode169 {170 /// <summary>171 /// Use current runspace from the current thread of execution.172 /// </summary>173 CurrentRunspace = 0,174 175 /// <summary>176 /// Create new runspace.177 /// </summary>178 NewRunspace = 1179 }180 181 /// <summary>182 /// Type which has information about InvocationState and Exception183 /// associated with InvocationState.184 /// </summary>185 public sealed class PSInvocationStateInfo186 {187 #region Constructors188 189 /// <summary>190 /// Constructor for state changes with an optional error.191 /// </summary>192 /// <param name="state">The new state.</param>193 /// <param name="reason">A non-null exception if the state change was194 /// caused by an error,otherwise; null.195 /// </param>196 internal PSInvocationStateInfo(PSInvocationState state, Exception reason)197 {198 _executionState = state;199 _exceptionReason = reason;200 }201 202 /// <summary>203 /// Construct from PipelineStateInfo.204 /// </summary>205 /// <param name="pipelineStateInfo"></param>206 internal PSInvocationStateInfo(PipelineStateInfo pipelineStateInfo)207 {208 _executionState = (PSInvocationState)((int)pipelineStateInfo.State);209 _exceptionReason = pipelineStateInfo.Reason;210 }211 212 #endregion213 214 #region Public Properties215 216 /// <summary>217 /// The state of the PowerShell instance.218 /// </summary>219 /// <remarks>220 /// </remarks>221 public PSInvocationState State222 {223 get224 {225 return _executionState;226 }227 }228 229 /// <summary>230 /// The reason for the state change, if caused by an error.231 /// </summary>232 /// <remarks>233 /// The value of this property is non-null if the state234 /// changed due to an error. Otherwise, the value of this235 /// property is null.236 /// </remarks>237 public Exception Reason238 {239 get240 {241 return _exceptionReason;242 }243 }244 245 #endregion246 247 /// <summary>248 /// Clone the current instance.249 /// </summary>250 /// <returns>251 /// A copy of the current instance.252 /// </returns>253 internal PSInvocationStateInfo Clone()254 {255 return new PSInvocationStateInfo(256 _executionState,257 _exceptionReason258 );259 }260 261 #region Private data262 263 /// <summary>264 /// The current execution state.265 /// </summary>266 private readonly PSInvocationState _executionState;267 268 /// <summary>269 /// Non-null exception if the execution state change was due to an error.270 /// </summary>271 private readonly Exception _exceptionReason;272 273 #endregion274 }275 276 /// <summary>277 /// Event arguments passed to PowerShell state change handlers278 /// <see cref="PowerShell.InvocationStateChanged"/> event.279 /// </summary>280 public sealed class PSInvocationStateChangedEventArgs : EventArgs281 {282 #region Constructors283 284 /// <summary>285 /// Constructs PSInvocationStateChangedEventArgs from PSInvocationStateInfo.286 /// </summary>287 /// <param name="psStateInfo">288 /// state to raise the event with.289 /// </param>290 internal PSInvocationStateChangedEventArgs(PSInvocationStateInfo psStateInfo)291 {292 Dbg.Assert(psStateInfo != null, "caller should validate the parameter");293 InvocationStateInfo = psStateInfo;294 }295 296 #endregion297 298 #region Public Properties299 300 /// <summary>301 /// Information about current state of a PowerShell Instance.302 /// </summary>303 public PSInvocationStateInfo InvocationStateInfo { get; }304 305 #endregion306 }307 308 #endregion309 310 /// <summary>311 /// Settings to control command invocation.312 /// </summary>313 public sealed class PSInvocationSettings314 {315 #region Private Fields316 317 private PSHost _host;318 319 // the following are used to flow the identity to pipeline execution thread320 321 // Invokes a remote command and immediately disconnects, if transport layer322 // supports this operation.323 324 #endregion325 326 #region Constructors327 328 /// <summary>329 /// Default Constructor.330 /// </summary>331 public PSInvocationSettings()332 {333 this.ApartmentState = ApartmentState.Unknown;334 _host = null;335 RemoteStreamOptions = 0;336 AddToHistory = false;337 ErrorActionPreference = null;338 }339 340 #endregion341 342 /// <summary>343 /// ApartmentState of the thread in which the command344 /// is executed.345 /// </summary>346 public ApartmentState ApartmentState { get; set; }347 348 /// <summary>349 /// Host to use with the Runspace when the command is350 /// executed.351 /// </summary>352 public PSHost Host353 {354 get355 {356 return _host;357 }358 359 set360 {361 if (value == null)362 {363 throw PSTraceSource.NewArgumentNullException("Host");364 }365 366 _host = value;367 }368 }369 370 /// <summary>371 /// Options for the Error, Warning, Verbose and Debug streams during remote calls.372 /// </summary>373 public RemoteStreamOptions RemoteStreamOptions { get; set; }374 375 /// <summary>376 /// Boolean which tells if the command is added to the history of the377 /// Runspace the command is executing in. By default this is false.378 /// </summary>379 public bool AddToHistory { get; set; }380 381 /// <summary>382 /// Determines how errors should be handled during batch command execution.383 /// </summary>384 public ActionPreference? ErrorActionPreference { get; set; }385 386 /// <summary>387 /// Used by Powershell remoting infrastructure to flow identity from calling thread to388 /// Pipeline Execution Thread.389 /// </summary>390 /// <remarks>391 /// Scenario: In the IIS hosting model, the calling thread is impersonated with a different392 /// identity than the process identity. However Pipeline Execution Thread always inherits393 /// process's identity and this will create problems related to security. In the IIS hosting394 /// model, we should honor calling threads identity.395 /// </remarks>396 public bool FlowImpersonationPolicy { get; set; }397 398 internal System.Security.Principal.WindowsIdentity WindowsIdentityToImpersonate { get; set; }399 400 /// <summary>401 /// When true, allows an unhandled flow control exceptions to402 /// propagate to a caller invoking the PowerShell object.403 /// </summary>404 public bool ExposeFlowControlExceptions405 {406 get;407 set;408 }409 410 /// <summary>411 /// Invokes a remote command and immediately disconnects, if the transport412 /// layer supports this operation.413 /// </summary>414 internal bool InvokeAndDisconnect { get; set; }415 }416 417 /// <summary>418 /// Batch execution context.419 /// </summary>420 internal class BatchInvocationContext421 {422 private readonly AutoResetEvent _completionEvent;423 424 /// <summary>425 /// Class constructor.426 /// </summary>427 /// <param name="command"></param>428 /// <param name="output"></param>429 internal BatchInvocationContext(PSCommand command, PSDataCollection<PSObject> output)430 {431 Command = command;432 Output = output;433 _completionEvent = new AutoResetEvent(false);434 }435 436 /// <summary>437 /// Invocation output.438 /// </summary>439 internal PSDataCollection<PSObject> Output { get; }440 441 /// <summary>442 /// Command to invoke.443 /// </summary>444 internal PSCommand Command { get; }445 446 /// <summary>447 /// Waits for the completion event.448 /// </summary>449 internal void Wait()450 {451 _completionEvent.WaitOne();452 }453 454 /// <summary>455 /// Signals the completion event.456 /// </summary>457 internal void Signal()458 {459 _completionEvent.Set();460 }461 }462 463 /// <summary>464 /// These flags control whether InvocationInfo is added to items in the Error, Warning, Verbose and Debug465 /// streams during remote calls.466 /// </summary>467 [Flags]468 public enum RemoteStreamOptions469 {470 /// <summary>471 /// If this flag is set, ErrorRecord will include an instance of InvocationInfo on remote calls.472 /// </summary>473 AddInvocationInfoToErrorRecord = 0x01,474 475 /// <summary>476 /// If this flag is set, WarningRecord will include an instance of InvocationInfo on remote calls.477 /// </summary>478 AddInvocationInfoToWarningRecord = 0x02,479 480 /// <summary>481 /// If this flag is set, DebugRecord will include an instance of InvocationInfo on remote calls.482 /// </summary>483 AddInvocationInfoToDebugRecord = 0x04,484 485 /// <summary>486 /// If this flag is set, VerboseRecord will include an instance of InvocationInfo on remote calls.487 /// </summary>488 AddInvocationInfoToVerboseRecord = 0x08,489 490 /// <summary>491 /// If this flag is set, ErrorRecord, WarningRecord, DebugRecord, and VerboseRecord will include an instance of InvocationInfo on remote calls.492 /// </summary>493 AddInvocationInfo = AddInvocationInfoToErrorRecord494 | AddInvocationInfoToWarningRecord495 | AddInvocationInfoToDebugRecord496 | AddInvocationInfoToVerboseRecord497 }498 499 #region PowerShell AsyncResult500 501 /// <summary>502 /// Internal Async result type used by BeginInvoke() and BeginStop() overloads.503 /// </summary>504 internal sealed class PowerShellAsyncResult : AsyncResult505 {506 #region Private Data / Properties507 508 // used to track if this AsyncResult is created by a BeginInvoke operation or509 // a BeginStop operation.510 511 /// <summary>512 /// True if AsyncResult monitors Async BeginInvoke().513 /// false otherwise.514 /// </summary>515 internal bool IsAssociatedWithAsyncInvoke { get; }516 517 /// <summary>518 /// The output buffer for the asynchronous invoke.519 /// </summary>520 internal PSDataCollection<PSObject> Output { get; }521 522 #endregion523 524 #region Constructor525 526 /// <summary>527 /// Constructor.528 /// </summary>529 /// <param name="ownerId">530 /// Instance Id of the Powershell object creating this instance531 /// </param>532 /// <param name="callback">533 /// Callback to call when the async operation completes.534 /// </param>535 /// <param name="state">536 /// A user supplied state to call the "callback" with.537 /// </param>538 /// <param name="output">539 /// The output buffer to return from EndInvoke.540 /// </param>541 /// <param name="isCalledFromBeginInvoke">542 /// true if AsyncResult monitors BeginInvoke.543 /// false otherwise544 /// </param>545 internal PowerShellAsyncResult(Guid ownerId, AsyncCallback callback, object state, PSDataCollection<PSObject> output,546 bool isCalledFromBeginInvoke)547 : base(ownerId, callback, state)548 {549 IsAssociatedWithAsyncInvoke = isCalledFromBeginInvoke;550 Output = output;551 }552 553 #endregion554 }555 556 #endregion557 558 /// <summary>559 /// Represents a PowerShell command or script to execute against a560 /// Runspace(Pool) if provided, otherwise execute using a default561 /// Runspace. Provides access to different result buffers562 /// like output, error, debug, verbose, progress, warning, and information.563 ///564 /// Provides a simple interface to execute a powershell command:565 /// <code>566 /// Powershell.Create().AddScript("get-process").Invoke();567 /// </code>568 /// The above statement creates a local runspace using default569 /// configuration, executes the command and then closes the runspace.570 ///571 /// Using RunspacePool property, the caller can provide the runspace572 /// where the command / script is executed.573 /// </summary>574 [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces", Justification = "PowerShell is a valid type in SMAR namespace.")]575 public sealed class PowerShell : IDisposable576 {577 #region Private Fields578 579 private PSCommand _psCommand;580 // worker object which does the invoke581 private Worker _worker;582 private PowerShellAsyncResult _invokeAsyncResult;583 private PowerShellAsyncResult _stopAsyncResult;584 private PowerShellAsyncResult _batchAsyncResult;585 private PSInvocationSettings _batchInvocationSettings;586 private PSCommand _backupPSCommand;587 private object _rsConnection;588 589 private PSDataCollection<ErrorRecord> _errorBuffer;590 591 private bool _isDisposed;592 private readonly object _syncObject = new object();593 594 // client remote powershell if the powershell595 // is executed with a remote runspace pool596 597 private ConnectCommandInfo _connectCmdInfo;598 private bool _commandInvokedSynchronously = false;599 private bool _isBatching = false;600 private bool _stopBatchExecution = false;601 602 // Delegates for asynchronous invocation/termination of PowerShell commands603 private readonly Func<IAsyncResult, PSDataCollection<PSObject>> _endInvokeMethod;604 private readonly Action<IAsyncResult> _endStopMethod;605 606 #endregion607 608 #region Internal Constructors609 610 /// <summary>611 /// Constructs PowerShell.612 /// </summary>613 /// <param name="command">614 /// A PSCommand.615 /// </param>616 /// <param name="extraCommands">617 /// A list of extra commands to run618 /// </param>619 /// <param name="rsConnection">620 /// A Runspace or RunspacePool to refer while invoking the command.621 /// This can be null in which case a new runspace is created622 /// whenever Invoke* method is called.623 /// </param>624 private PowerShell(PSCommand command, Collection<PSCommand> extraCommands, object rsConnection)625 {626 Dbg.Assert(command != null, "command must not be null");627 ExtraCommands = extraCommands ?? new Collection<PSCommand>();628 RunningExtraCommands = false;629 _psCommand = command;630 _psCommand.Owner = this;631 RemoteRunspace remoteRunspace = rsConnection as RemoteRunspace;632 _rsConnection = remoteRunspace != null ? remoteRunspace.RunspacePool : rsConnection;633 InstanceId = Guid.NewGuid();634 InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.NotStarted, null);635 OutputBuffer = null;636 OutputBufferOwner = true;637 _errorBuffer = new PSDataCollection<ErrorRecord>();638 ErrorBufferOwner = true;639 InformationalBuffers = new PSInformationalBuffers(InstanceId);640 Streams = new PSDataStreams(this);641 _endInvokeMethod = EndInvoke;642 _endStopMethod = EndStop;643 ApplicationInsightsTelemetry.SendTelemetryMetric(TelemetryType.PowerShellCreate, "create");644 }645 646 /// <summary>647 /// Constructs a PowerShell instance in the disconnected start state with648 /// the provided remote command connect information and runspace(pool) objects.649 /// </summary>650 /// <param name="connectCmdInfo">Remote command connect information.</param>651 /// <param name="rsConnection">Remote Runspace or RunspacePool object.</param>652 internal PowerShell(ConnectCommandInfo connectCmdInfo, object rsConnection)653 : this(new PSCommand(), null, rsConnection)654 {655 ExtraCommands = new Collection<PSCommand>();656 RunningExtraCommands = false;657 AddCommand(connectCmdInfo.Command);658 _connectCmdInfo = connectCmdInfo;659 660 // The command ID is passed to the PSRP layer through the PowerShell instanceID.661 InstanceId = _connectCmdInfo.CommandId;662 663 InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.Disconnected, null);664 665 if (rsConnection is RemoteRunspace)666 {667 _runspace = rsConnection as Runspace;668 _runspacePool = ((RemoteRunspace)rsConnection).RunspacePool;669 }670 else if (rsConnection is RunspacePool)671 {672 _runspacePool = (RunspacePool)rsConnection;673 }674 675 Dbg.Assert(_runspacePool != null, "Invalid rsConnection parameter>");676 RemotePowerShell = new ClientRemotePowerShell(this, _runspacePool.RemoteRunspacePoolInternal);677 }678 679 /// <summary>680 /// </summary>681 /// <param name="inputstream"></param>682 /// <param name="outputstream"></param>683 /// <param name="errorstream"></param>684 /// <param name="runspacePool"></param>685 internal PowerShell(ObjectStreamBase inputstream,686 ObjectStreamBase outputstream, ObjectStreamBase errorstream, RunspacePool runspacePool)687 {688 ExtraCommands = new Collection<PSCommand>();689 RunningExtraCommands = false;690 _rsConnection = runspacePool;691 InstanceId = Guid.NewGuid();692 InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.NotStarted, null);693 InformationalBuffers = new PSInformationalBuffers(InstanceId);694 Streams = new PSDataStreams(this);695 696 PSDataCollectionStream<PSObject> outputdatastream = (PSDataCollectionStream<PSObject>)outputstream;697 OutputBuffer = outputdatastream.ObjectStore;698 699 PSDataCollectionStream<ErrorRecord> errordatastream = (PSDataCollectionStream<ErrorRecord>)errorstream;700 _errorBuffer = errordatastream.ObjectStore;701 702 if (runspacePool != null && runspacePool.RemoteRunspacePoolInternal != null)703 {704 RemotePowerShell = new ClientRemotePowerShell(this, runspacePool.RemoteRunspacePoolInternal);705 }706 707 _endInvokeMethod = EndInvoke;708 _endStopMethod = EndStop;709 }710 711 /// <summary>712 /// Creates a PowerShell object in the disconnected start state and with a ConnectCommandInfo object713 /// parameter that specifies what remote command to associate with this PowerShell when it is connected.714 /// </summary>715 /// <param name="connectCmdInfo"></param>716 /// <param name="inputstream"></param>717 /// <param name="outputstream"></param>718 /// <param name="errorstream"></param>719 /// <param name="runspacePool"></param>720 internal PowerShell(ConnectCommandInfo connectCmdInfo, ObjectStreamBase inputstream, ObjectStreamBase outputstream,721 ObjectStreamBase errorstream, RunspacePool runspacePool)722 : this(inputstream, outputstream, errorstream, runspacePool)723 {724 ExtraCommands = new Collection<PSCommand>();725 RunningExtraCommands = false;726 _psCommand = new PSCommand();727 _psCommand.Owner = this;728 _runspacePool = runspacePool;729 730 AddCommand(connectCmdInfo.Command);731 _connectCmdInfo = connectCmdInfo;732 733 // The command ID is passed to the PSRP layer through the PowerShell instanceID.734 InstanceId = _connectCmdInfo.CommandId;735 736 InvocationStateInfo = new PSInvocationStateInfo(PSInvocationState.Disconnected, null);737 738 RemotePowerShell = new ClientRemotePowerShell(this, runspacePool.RemoteRunspacePoolInternal);739 }740 741 /// <summary>742 /// Sets the command collection in this powershell.743 /// </summary>744 /// <remarks>This method will be called by RemotePipeline745 /// before it begins execution. This method is used to set746 /// the command collection of the remote pipeline as the747 /// command collection of the underlying powershell</remarks>748 internal void InitForRemotePipeline(CommandCollection command, ObjectStreamBase inputstream,749 ObjectStreamBase outputstream, ObjectStreamBase errorstream, PSInvocationSettings settings, bool redirectShellErrorOutputPipe)750 {751 Dbg.Assert(command != null, "A command collection need to be specified");752 753 _psCommand = new PSCommand(command[0]);754 _psCommand.Owner = this;755 756 for (int i = 1; i < command.Count; i++)757 {758 AddCommand(command[i]);759 }760 761 RedirectShellErrorOutputPipe = redirectShellErrorOutputPipe;762 763 // create the client remote powershell for remoting764 // communications765 RemotePowerShell ??= new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal);766 767 // If we get here, we don't call 'Invoke' or any of it's friends on 'this', instead we serialize 'this' in PowerShell.ToPSObjectForRemoting.768 // Without the following two steps, we'll be missing the 'ExtraCommands' on the serialized instance of 'this'.769 // This is the last possible chance to call set up for batching as we will indirectly call ToPSObjectForRemoting770 // in the call to ClientRemotePowerShell.Initialize (which happens just below.)771 DetermineIsBatching();772 773 if (_isBatching)774 {775 SetupAsyncBatchExecution();776 }777 778 RemotePowerShell.Initialize(inputstream, outputstream,779 errorstream, InformationalBuffers, settings);780 }781 782 /// <summary>783 /// Initialize PowerShell object for connection to remote command.784 /// </summary>785 /// <param name="inputstream">Input stream.</param>786 /// <param name="outputstream">Output stream.</param>787 /// <param name="errorstream">Error stream.</param>788 /// <param name="settings">Settings information.</param>789 /// <param name="redirectShellErrorOutputPipe">Redirect error output.</param>790 internal void InitForRemotePipelineConnect(ObjectStreamBase inputstream, ObjectStreamBase outputstream,791 ObjectStreamBase errorstream, PSInvocationSettings settings, bool redirectShellErrorOutputPipe)792 {793 // The remotePowerShell and DSHandler cannot be initialized with a disconnected runspace.794 // Make sure the associated runspace is valid and connected.795 CheckRunspacePoolAndConnect();796 797 if (InvocationStateInfo.State != PSInvocationState.Disconnected)798 {799 throw new InvalidPowerShellStateException(InvocationStateInfo.State);800 }801 802 RedirectShellErrorOutputPipe = redirectShellErrorOutputPipe;803 804 RemotePowerShell ??= new ClientRemotePowerShell(this, ((RunspacePool)_rsConnection).RemoteRunspacePoolInternal);805 806 if (!RemotePowerShell.Initialized)807 {808 RemotePowerShell.Initialize(inputstream, outputstream, errorstream, InformationalBuffers, settings);809 }810 }811 812 #endregion813 814 #region Construction Factory815 816 /// <summary>817 /// Constructs an empty PowerShell instance; a script or command must be added before invoking this instance.818 /// </summary>819 /// <returns>820 /// An instance of PowerShell.821 /// </returns>822 public static PowerShell Create()823 {824 return new PowerShell(new PSCommand(), null, null);825 }826 827 /// <summary>828 /// Constructs an empty PowerShell instance; a script or command must be added before invoking this instance.829 /// </summary>830 /// <param name="runspace">Runspace mode.</param>831 /// <returns>An instance of PowerShell.</returns>832 public static PowerShell Create(RunspaceMode runspace)833 {834 PowerShell result = null;835 836 switch (runspace)837 {838 case RunspaceMode.CurrentRunspace:839 if (Runspace.DefaultRunspace == null)840 {841 throw new InvalidOperationException(PowerShellStrings.NoDefaultRunspaceForPSCreate);842 }843 844 result = new PowerShell(new PSCommand(), null, Runspace.DefaultRunspace);845 result.IsChild = true;846 result.IsNested = true;847 result.IsRunspaceOwner = false;848 result._runspace = Runspace.DefaultRunspace;849 break;850 case RunspaceMode.NewRunspace:851 result = new PowerShell(new PSCommand(), null, null);852 break;853 }854 855 return result;856 }857 858 /// <summary>859 /// Constructs an empty PowerShell instance; a script or command must be added before invoking this instance.860 /// </summary>861 /// <param name="initialSessionState">InitialSessionState with which to create the runspace.</param>862 /// <returns>An instance of PowerShell.</returns>863 public static PowerShell Create(InitialSessionState initialSessionState)864 {865 PowerShell result = Create();866 867 result.Runspace = RunspaceFactory.CreateRunspace(initialSessionState);868 result.Runspace.Open();869 870 return result;871 }872 873 /// <summary>874 /// Constructs an empty PowerShell instance and associates it with the provided875 /// Runspace; a script or command must be added before invoking this instance.876 /// </summary>877 /// <param name="runspace">Runspace in which to invoke commands.</param>878 /// <returns>An instance of PowerShell.</returns>879 /// <remarks>880 /// The required Runspace argument is accepted no matter what state it is in.881 /// Leaving Runspace state management to the caller allows them to open their882 /// runspace in whatever manner is most appropriate for their application883 /// (in another thread while this instance of the PowerShell class is being884 /// instantiated, for example).885 /// </remarks>886 public static PowerShell Create(Runspace runspace)887 {888 if (runspace == null)889 {890 throw new PSArgumentNullException(nameof(runspace));891 }892 893 PowerShell result = Create();894 result.Runspace = runspace;895 896 return result;897 }898 899 /// <summary>900 /// Creates a nested powershell within the current instance.901 /// Nested PowerShell is used to do simple operations like checking state902 /// of a variable while another command is using the runspace.903 ///904 /// Nested PowerShell should be invoked from the same thread as the parent905 /// PowerShell invocation thread. So effectively the parent Powershell906 /// invocation thread is blocked until nested invoke() operation is907 /// complete.908 ///909 /// Implement PSHost.EnterNestedPrompt to perform invoke() operation on the910 /// nested powershell.911 /// </summary>912 /// <exception cref="InvalidOperationException">913 /// 1. State of powershell instance is not valid to create a nested powershell instance.914 /// Nested PowerShell should be created only for a running powershell instance.915 /// </exception>916 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "ps", Justification = "ps represents PowerShell and is used at many places.")]917 public PowerShell CreateNestedPowerShell()918 {919 if ((_worker != null) && (_worker.CurrentlyRunningPipeline != null))920 {921 PowerShell result = new PowerShell(new PSCommand(),922 null, _worker.CurrentlyRunningPipeline.Runspace);923 result.IsNested = true;924 return result;925 }926 927 throw PSTraceSource.NewInvalidOperationException(PowerShellStrings.InvalidStateCreateNested);928 }929 930 /// <summary>931 /// Method needed when deserializing PowerShell object coming from a RemoteDataObject.932 /// </summary>933 /// <param name="isNested">Indicates if PowerShell object is nested.</param>934 /// <param name="psCommand">Commands that the PowerShell pipeline is built of.</param>935 /// <param name="extraCommands">Extra commands to run.</param>936 private static PowerShell Create(bool isNested, PSCommand psCommand, Collection<PSCommand> extraCommands)937 {938 PowerShell powerShell = new PowerShell(psCommand, extraCommands, null);939 powerShell.IsNested = isNested;940 return powerShell;941 }942 943 #endregion944 945 #region Command / Parameter Construction946 947 /// <summary>948 /// Add a cmdlet to construct a command pipeline.949 /// For example, to construct a command string "Get-Process | Sort-Object",950 /// <code>951 /// PowerShell shell = PowerShell.Create()952 /// .AddCommand("Get-Process")953 /// .AddCommand("Sort-Object");954 /// </code>955 /// </summary>956 /// <param name="cmdlet">957 /// A string representing cmdlet.958 /// </param>959 /// <returns>960 /// A PowerShell instance with <paramref name="cmdlet"/> added.961 /// </returns>962 /// <remarks>963 /// This method is not thread safe.964 /// </remarks>965 /// <exception cref="ArgumentNullException">966 /// cmdlet is null.967 /// </exception>968 /// <exception cref="InvalidPowerShellStateException">969 /// Powershell instance cannot be changed in its970 /// current state.971 /// </exception>972 /// <exception cref="ObjectDisposedException">973 /// Object is disposed.974 /// </exception>975 public PowerShell AddCommand(string cmdlet)976 {977 lock (_syncObject)978 {979 AssertChangesAreAccepted();980 981 _psCommand.AddCommand(cmdlet);982 983 return this;984 }985 }986 987 /// <summary>988 /// Add a cmdlet to construct a command pipeline.989 /// For example, to construct a command string "Get-Process | Sort-Object",990 /// <code>991 /// PowerShell shell = PowerShell.Create()992 /// .AddCommand("Get-Process", true)993 /// .AddCommand("Sort-Object", true);994 /// </code>995 /// </summary>996 /// <param name="cmdlet">997 /// A string representing cmdlet.998 /// </param>999 /// <param name="useLocalScope">1000 /// if true local scope is used to run the script command.1001 /// </param>1002 /// <returns>1003 /// A PowerShell instance with <paramref name="cmdlet"/> added.1004 /// </returns>1005 /// <remarks>1006 /// This method is not thread safe.1007 /// </remarks>1008 /// <exception cref="ArgumentNullException">1009 /// cmdlet is null.1010 /// </exception>1011 /// <exception cref="InvalidPowerShellStateException">1012 /// Powershell instance cannot be changed in its1013 /// current state.1014 /// </exception>1015 /// <exception cref="ObjectDisposedException">1016 /// Object is disposed.1017 /// </exception>1018 public PowerShell AddCommand(string cmdlet, bool useLocalScope)1019 {1020 lock (_syncObject)1021 {1022 AssertChangesAreAccepted();1023 1024 _psCommand.AddCommand(cmdlet, useLocalScope);1025 1026 return this;1027 }1028 }1029 1030 /// <summary>1031 /// Add a piece of script to construct a command pipeline.1032 /// For example, to construct a command string "Get-Process | ForEach-Object { $_.Name }"1033 /// <code>1034 /// PowerShell shell = PowerShell.Create()1035 /// .AddScript("Get-Process | ForEach-Object { $_.Name }");1036 /// </code>1037 /// </summary>1038 /// <param name="script">1039 /// A string representing a script.1040 /// </param>1041 /// <returns>1042 /// A PowerShell instance with <paramref name="command"/> added.1043 /// </returns>1044 /// <remarks>1045 /// This method is not thread-safe.1046 /// </remarks>1047 /// <exception cref="ArgumentNullException">1048 /// command is null.1049 /// </exception>1050 /// <exception cref="InvalidPowerShellStateException">1051 /// Powershell instance cannot be changed in its1052 /// current state.1053 /// </exception>1054 /// <exception cref="ObjectDisposedException">1055 /// Object is disposed.1056 /// </exception>1057 public PowerShell AddScript(string script)1058 {1059 lock (_syncObject)1060 {1061 AssertChangesAreAccepted();1062 1063 _psCommand.AddScript(script);1064 1065 return this;1066 }1067 }1068 1069 /// <summary>1070 /// Add a piece of script to construct a command pipeline.1071 /// For example, to construct a command string "Get-Process | ForEach-Object { $_.Name }"1072 /// <code>1073 /// PowerShell shell = PowerShell.Create()1074 /// .AddScript("Get-Process | ForEach-Object { $_.Name }", true);1075 /// </code>1076 /// </summary>1077 /// <param name="script">1078 /// A string representing a script.1079 /// </param>1080 /// <param name="useLocalScope">1081 /// if true local scope is used to run the script command.1082 /// </param>1083 /// <returns>1084 /// A PowerShell instance with <paramref name="command"/> added.1085 /// </returns>1086 /// <remarks>1087 /// This method is not thread-safe.1088 /// </remarks>1089 /// <exception cref="ArgumentNullException">1090 /// command is null.1091 /// </exception>1092 /// <exception cref="InvalidPowerShellStateException">1093 /// Powershell instance cannot be changed in its1094 /// current state.1095 /// </exception>1096 /// <exception cref="ObjectDisposedException">1097 /// Object is disposed.1098 /// </exception>1099 public PowerShell AddScript(string script, bool useLocalScope)1100 {1101 lock (_syncObject)1102 {1103 AssertChangesAreAccepted();1104 1105 _psCommand.AddScript(script, useLocalScope);1106 1107 return this;1108 }1109 }1110 1111 /// <summary>1112 /// Add a <see cref="Command"/> element to the current command1113 /// pipeline.1114 /// </summary>1115 /// <param name="command">1116 /// Command to add.1117 /// </param>1118 /// <returns>1119 /// A PSCommand instance with <paramref name="command"/> added.1120 /// </returns>1121 /// <remarks>1122 /// This method is not thread-safe.1123 /// </remarks>1124 /// <exception cref="ArgumentNullException">1125 /// command is null.1126 /// </exception>1127 /// <exception cref="InvalidPowerShellStateException">1128 /// Powershell instance cannot be changed in its1129 /// current state.1130 /// </exception>1131 /// <exception cref="ObjectDisposedException">1132 /// Object is disposed.1133 /// </exception>1134 internal PowerShell AddCommand(Command command)1135 {1136 lock (_syncObject)1137 {1138 AssertChangesAreAccepted();1139 1140 _psCommand.AddCommand(command);1141 1142 return this;1143 }1144 }1145 1146 /// <summary>1147 /// CommandInfo object for the command to add.1148 /// </summary>1149 /// <param name="commandInfo">The CommandInfo object for the command to add.</param>1150 /// <returns>1151 /// A PSCommand instance with the command added.1152 /// </returns>1153 /// <remarks>1154 /// This method is not thread-safe.1155 /// </remarks>1156 /// <exception cref="ArgumentNullException">1157 /// command is null.1158 /// </exception>1159 /// <exception cref="InvalidPowerShellStateException">1160 /// Powershell instance cannot be changed in its1161 /// current state.1162 /// </exception>1163 /// <exception cref="ObjectDisposedException">1164 /// Object is disposed.1165 /// </exception>1166 public PowerShell AddCommand(CommandInfo commandInfo)1167 {1168 if (commandInfo == null)1169 {1170 throw PSTraceSource.NewArgumentNullException(nameof(commandInfo));1171 }1172 1173 Command cmd = new Command(commandInfo);1174 _psCommand.AddCommand(cmd);1175 return this;1176 }1177 1178 /// <summary>1179 /// Add a parameter to the last added command.1180 /// For example, to construct a command string "Get-Process | Select-Object -Property Name"1181 /// <code>1182 /// PowerShell shell = PowerShell.Create()1183 /// .AddCommand("Get-Process")1184 /// .AddCommand("Select-Object").AddParameter("Property", "Name");1185 /// </code>1186 /// </summary>1187 /// <param name="parameterName">1188 /// Name of the parameter.1189 /// </param>1190 /// <param name="value">1191 /// Value for the parameter.1192 /// </param>1193 /// <returns>1194 /// A PowerShell instance with <paramref name="parameterName"/> added1195 /// to the parameter list of the last command.1196 /// </returns>1197 /// <remarks>1198 /// This method is not thread safe.1199 /// </remarks>1200 /// <exception cref="ArgumentException">