MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Diagnostics.CodeAnalysis;7using System.Management.Automation.Host;8using System.Management.Automation.Internal;9using System.Runtime.Serialization;10using System.Threading;11 12using Dbg = System.Management.Automation.Diagnostics;13 14namespace System.Management.Automation.Runspaces15{16 #region Exceptions17 18 /// <summary>19 /// Exception thrown when state of the runspace is different from20 /// expected state of runspace.21 /// </summary>22 public class InvalidRunspaceStateException : SystemException23 {24 /// <summary>25 /// Initializes a new instance of InvalidRunspaceStateException.26 /// </summary>27 public InvalidRunspaceStateException()28 : base29 (30 StringUtil.Format(RunspaceStrings.InvalidRunspaceStateGeneral)31 )32 {33 }34 35 /// <summary>36 /// Initializes a new instance of InvalidRunspaceStateException with a specified error message.37 /// </summary>38 /// <param name="message">39 /// The message that describes the error.40 /// </param>41 public InvalidRunspaceStateException(string message)42 : base(message)43 {44 }45 46 /// <summary>47 /// Initializes a new instance of the InvalidRunspaceStateException class48 /// with a specified error message and a reference to the inner exception49 /// that is the cause of this exception.50 /// </summary>51 /// <param name="message">52 /// The message that describes the error.53 /// </param>54 /// <param name="innerException">55 /// The exception that is the cause of the current exception.56 /// </param>57 public InvalidRunspaceStateException(string message, Exception innerException)58 : base(message, innerException)59 {60 }61 62 /// <summary>63 /// Initializes a new instance of the InvalidRunspaceStateException64 /// with a specified error message and current and expected state.65 /// </summary>66 /// <param name="message">The message that describes the error.</param>67 /// <param name="currentState">Current state of runspace.</param>68 /// <param name="expectedState">Expected states of runspace.</param>69 internal InvalidRunspaceStateException70 (71 string message,72 RunspaceState currentState,73 RunspaceState expectedState74 )75 : base(message)76 {77 _expectedState = expectedState;78 _currentState = currentState;79 }80 81 #region ISerializable Members82 83 // 2005/04/20-JonN No need to implement GetObjectData84 // if all fields are static or [NonSerialized]85 86 /// <summary>87 /// Initializes a new instance of the <see cref="InvalidRunspaceStateException"/>88 /// class with serialized data.89 /// </summary>90 /// <param name="info">91 /// The <see cref="SerializationInfo"/> that holds the serialized object92 /// data about the exception being thrown.93 /// </param>94 /// <param name="context">95 /// The <see cref="StreamingContext"/> that contains contextual information96 /// about the source or destination.97 /// </param>98 [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")]99 protected InvalidRunspaceStateException(SerializationInfo info, StreamingContext context)100 {101 throw new NotSupportedException();102 }103 104 #endregion105 106 /// <summary>107 /// Access CurrentState of the runspace.108 /// </summary>109 /// <remarks>This is the state of the runspace when exception was thrown.110 /// </remarks>111 public RunspaceState CurrentState112 {113 get114 {115 return _currentState;116 }117 118 internal set119 {120 _currentState = value;121 }122 }123 124 /// <summary>125 /// Expected state of runspace by the operation which has thrown this exception.126 /// </summary>127 public RunspaceState ExpectedState128 {129 get130 {131 return _expectedState;132 }133 134 internal set135 {136 _expectedState = value;137 }138 }139 140 /// <summary>141 /// State of the runspace when exception was thrown.142 /// </summary>143 [NonSerialized]144 private RunspaceState _currentState = 0;145 146 /// <summary>147 /// States of the runspace expected in method which throws this exception.148 /// </summary>149 [NonSerialized]150 private RunspaceState _expectedState = 0;151 }152 153 #endregion Exceptions154 155 #region Runspace state156 157 /// <summary>158 /// Defines various states of runspace.159 /// </summary>160 public enum RunspaceState161 {162 /// <summary>163 /// Beginning state upon creation.164 /// </summary>165 BeforeOpen = 0,166 /// <summary>167 /// A runspace is being established.168 /// </summary>169 Opening = 1,170 /// <summary>171 /// The runspace is established and valid.172 /// </summary>173 Opened = 2,174 /// <summary>175 /// The runspace is closed or has not been established.176 /// </summary>177 Closed = 3,178 /// <summary>179 /// The runspace is being closed.180 /// </summary>181 Closing = 4,182 /// <summary>183 /// The runspace has been disconnected abnormally.184 /// </summary>185 Broken = 5,186 /// <summary>187 /// The runspace is being disconnected.188 /// </summary>189 Disconnecting = 6,190 /// <summary>191 /// The runspace is disconnected.192 /// </summary>193 Disconnected = 7,194 /// <summary>195 /// The runspace is Connecting.196 /// </summary>197 Connecting = 8198 }199 200 /// <summary>201 /// These options control whether a new thread is created when a command is executed within a runspace.202 /// </summary>203 public enum PSThreadOptions204 {205 /// <summary>206 /// Use the default options: UseNewThread for local Runspace, ReuseThread for local RunspacePool, server settings for remote Runspace and RunspacePool.207 /// </summary>208 Default = 0,209 210 /// <summary>211 /// Creates a new thread for each invocation.212 /// </summary>213 UseNewThread = 1,214 215 /// <summary>216 /// Creates a new thread for the first invocation and then re-uses217 /// that thread in subsequent invocations.218 /// </summary>219 ReuseThread = 2,220 221 /// <summary>222 /// Doesn't create a new thread; the execution occurs on the thread223 /// that calls Invoke. This option is not valid for asynchronous calls.224 /// </summary>225 UseCurrentThread = 3226 }227 228 /// <summary>229 /// Defines type which has information about RunspaceState and230 /// Exception associated with RunspaceState.231 /// </summary>232 public sealed class RunspaceStateInfo233 {234 #region constructors235 236 /// <summary>237 /// Constructor for state changes not resulting from an error.238 /// </summary>239 /// <param name="state">The state of the runspace.</param>240 internal RunspaceStateInfo(RunspaceState state)241 : this(state, null)242 {243 }244 245 /// <summary>246 /// Constructor for state changes with an optional error.247 /// </summary>248 /// <param name="state">The state of runspace.</param>249 /// <param name="reason">A non-null exception if the state change was250 /// caused by an error, otherwise; null.251 /// </param>252 internal RunspaceStateInfo(RunspaceState state, Exception reason)253 : base()254 {255 State = state;256 Reason = reason;257 }258 259 /// <summary>260 /// Copy constructor to support cloning.261 /// </summary>262 /// <param name="runspaceStateInfo">The source263 /// RunspaceStateInfo264 /// </param>265 internal RunspaceStateInfo(RunspaceStateInfo runspaceStateInfo)266 {267 State = runspaceStateInfo.State;268 Reason = runspaceStateInfo.Reason;269 }270 #endregion constructors271 272 #region public_properties273 274 /// <summary>275 /// The state of the runspace.276 /// </summary>277 public RunspaceState State { get; }278 279 /// <summary>280 /// The reason for the state change, if caused by an error.281 /// </summary>282 /// <remarks>283 /// The value of this property is non-null if the state284 /// changed due to an error. Otherwise, the value of this285 /// property is null.286 /// </remarks>287 public Exception Reason { get; }288 289 #endregion public_properties290 291 /// <summary>292 /// Override for ToString()293 /// </summary>294 /// <returns></returns>295 public override string ToString()296 {297 return State.ToString();298 }299 300 /// <summary>301 /// Clones current object.302 /// </summary>303 /// <returns>Cloned object.</returns>304 internal RunspaceStateInfo Clone()305 {306 return new RunspaceStateInfo(this);307 }308 309 #region private_fields310 311 #endregion private_fields312 }313 314 /// <summary>315 /// Defines Event arguments passed to RunspaceStateEvent handler316 /// <see cref="Runspace.StateChanged"/> event.317 /// </summary>318 public sealed class RunspaceStateEventArgs : EventArgs319 {320 #region constructors321 322 /// <summary>323 /// Constructs RunspaceStateEventArgs using RunspaceStateInfo.324 /// </summary>325 /// <param name="runspaceStateInfo">The information about326 /// current state of the runspace.</param>327 /// <exception cref="ArgumentNullException">RunspaceStateInfo is null328 /// </exception>329 internal RunspaceStateEventArgs(RunspaceStateInfo runspaceStateInfo)330 {331 if (runspaceStateInfo == null)332 {333 throw PSTraceSource.NewArgumentNullException(nameof(runspaceStateInfo));334 }335 336 RunspaceStateInfo = runspaceStateInfo;337 }338 339 #endregion constructors340 341 #region public_properties342 343 /// <summary>344 /// Information about state of the runspace.345 /// </summary>346 /// <remarks>347 /// This value indicates the state of the runspace after the348 /// change.349 /// </remarks>350 public RunspaceStateInfo RunspaceStateInfo { get; }351 352 #endregion public_properties353 }354 355 /// <summary>356 /// Enum to indicate whether a Runspace is busy or available.357 /// </summary>358 public enum RunspaceAvailability359 {360 /// <summary>361 /// The Runspace is not been in the Opened state.362 /// </summary>363 None = 0,364 365 /// <summary>366 /// The Runspace is available to execute commands.367 /// </summary>368 Available,369 370 /// <summary>371 /// The Runspace is available to execute nested commands.372 /// </summary>373 AvailableForNestedCommand,374 375 /// <summary>376 /// The Runspace is busy executing a command.377 /// </summary>378 Busy,379 380 /// <summary>381 /// Applies only to remote runspace case. The remote runspace382 /// is currently in a Debugger Stop mode and requires a debugger383 /// SetDebuggerAction() call to continue.384 /// </summary>385 RemoteDebug386 }387 388 /// <summary>389 /// Defines the event arguments passed to the AvailabilityChanged <see cref="Runspace.AvailabilityChanged"/> event.390 /// </summary>391 public sealed class RunspaceAvailabilityEventArgs : EventArgs392 {393 internal RunspaceAvailabilityEventArgs(RunspaceAvailability runspaceAvailability)394 {395 RunspaceAvailability = runspaceAvailability;396 }397 398 /// <summary>399 /// Whether the Runspace is available to execute commands.400 /// </summary>401 public RunspaceAvailability RunspaceAvailability { get; }402 }403 404 #endregion Runspace state405 406 #region Runspace capabilities407 408 /// <summary>409 /// Defines runspace capabilities.410 /// </summary>411 public enum RunspaceCapability412 {413 /// <summary>414 /// Legacy capabilities for WinRM only, from Win7 timeframe.415 /// </summary>416 Default = 0x0,417 418 /// <summary>419 /// Runspace and remoting layer supports disconnect/connect feature.420 /// </summary>421 SupportsDisconnect = 0x1,422 423 /// <summary>424 /// Runspace is based on a named pipe transport.425 /// </summary>426 NamedPipeTransport = 0x2,427 428 /// <summary>429 /// Runspace is based on a VM socket transport.430 /// </summary>431 VMSocketTransport = 0x4,432 433 /// <summary>434 /// Runspace is based on SSH transport.435 /// </summary>436 SSHTransport = 0x8,437 438 /// <summary>439 /// Runspace is based on open custom connection/transport support.440 /// </summary>441 CustomTransport = 0x100442 }443 444 #endregion445 446 /// <summary>447 /// Public interface to PowerShell Runtime. Provides APIs for creating pipelines,448 /// access session state etc.449 /// </summary>450 public abstract class Runspace : IDisposable451 {452 #region Private Data453 454 private static int s_globalId;455 private readonly Stack<PowerShell> _runningPowerShells;456 private PowerShell _baseRunningPowerShell;457 private readonly object _syncObject;458 459 #endregion460 461 #region constructor462 463 /// <summary>464 /// Explicit default constructor.465 /// </summary>466 internal Runspace()467 {468 // Create the default Runspace Id and friendly name.469 Id = System.Threading.Interlocked.Increment(ref s_globalId);470 Name = "Runspace" + Id.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);471 _runningPowerShells = new Stack<PowerShell>();472 _syncObject = new object();473 474 // Keep track of this runspace until it is disposed.475 lock (s_syncObject)476 {477 s_runspaceDictionary.Add(Id, new WeakReference<Runspace>(this));478 }479 }480 481 /// <summary>482 /// Static Constructor.483 /// </summary>484 static Runspace()485 {486 s_syncObject = new object();487 s_runspaceDictionary = new SortedDictionary<int, WeakReference<Runspace>>();488 s_globalId = 0;489 }490 491 #endregion constructor492 493 #region properties494 495 /// <summary>496 /// Used to store Runspace reference on per thread basis. Used by497 /// various PowerShell engine features to get access to TypeTable498 /// etc.499 /// </summary>500 [ThreadStatic]501 private static Runspace t_threadSpecificDefaultRunspace = null;502 /// <summary>503 /// Gets and sets the default Runspace used to evaluate scripts.504 /// </summary>505 /// <remarks>The Runspace used to set this property should not be shared between different threads.</remarks>506 public static Runspace DefaultRunspace507 {508 get509 {510 return t_threadSpecificDefaultRunspace;511 }512 513 set514 {515 if (value == null || !value.RunspaceIsRemote)516 {517 t_threadSpecificDefaultRunspace = value;518 }519 else520 {521 throw new InvalidOperationException(RunspaceStrings.RunspaceNotLocal);522 }523 }524 }525 526 /// <summary>527 /// A PrimaryRunspace is a runspace that persists for the entire lifetime of the PowerShell session. It is only528 /// closed or disposed when the session is ending. So when the PrimaryRunspace is closing it will trigger on-exit529 /// cleanup that includes closing any other local runspaces left open, and will allow the process to exit.530 /// </summary>531 internal static Runspace PrimaryRunspace532 {533 get534 {535 return s_primaryRunspace;536 }537 538 set539 {540 var result = Interlocked.CompareExchange<Runspace>(ref s_primaryRunspace, value, null);541 if (result != null)542 {543 throw new PSInvalidOperationException(RunspaceStrings.PrimaryRunspaceAlreadySet);544 }545 }546 }547 548 private static Runspace s_primaryRunspace;549 550 /// <summary>551 /// Returns true if Runspace.DefaultRunspace can be used to552 /// create an instance of the PowerShell class with553 /// 'UseCurrentRunspace = true'.554 /// </summary>555 public static bool CanUseDefaultRunspace556 {557 // can use default runspace in a thread safe manner only if558 // 1. we have a default runspace559 // 2. we recognize the type of current runspace and current pipeline560 // 3. the pipeline executes on the same thread as this method561 562 // we don't return "true" for563 // 1. we have a default runspace564 // 2. no currently executing pipeline565 // to avoid a race condition where a pipeline is started566 // after this property getter did all the checks567 568 get569 {570 RunspaceBase runspace = Runspace.DefaultRunspace as RunspaceBase;571 if (runspace != null)572 {573 Pipeline currentPipeline = runspace.GetCurrentlyRunningPipeline();574 LocalPipeline localPipeline = currentPipeline as LocalPipeline;575 if ((localPipeline != null) && (localPipeline.NestedPipelineExecutionThread != null))576 {577 return578 (localPipeline.NestedPipelineExecutionThread.ManagedThreadId579 == Environment.CurrentManagedThreadId);580 }581 }582 583 return false;584 }585 }586 587 internal const ApartmentState DefaultApartmentState = ApartmentState.Unknown;588 589 /// <summary>590 /// ApartmentState of the thread used to execute commands within this Runspace.591 /// </summary>592 /// <remarks>593 /// Any updates to the value of this property must be done before the Runspace is opened594 /// </remarks>595 /// <exception cref="InvalidRunspaceStateException">596 /// An attempt to change this property was made after opening the Runspace597 /// </exception>598 public ApartmentState ApartmentState599 {600 get601 {602 return this.apartmentState;603 }604 605 set606 {607 if (this.RunspaceStateInfo.State != RunspaceState.BeforeOpen)608 {609 throw new InvalidRunspaceStateException(StringUtil.Format(RunspaceStrings.ChangePropertyAfterOpen));610 }611 612 this.apartmentState = value;613 }614 }615 616 private ApartmentState apartmentState = Runspace.DefaultApartmentState;617 618 /// <summary>619 /// This property determines whether a new thread is create for each invocation.620 /// </summary>621 /// <remarks>622 /// Any updates to the value of this property must be done before the Runspace is opened623 /// </remarks>624 /// <exception cref="InvalidRunspaceStateException">625 /// An attempt to change this property was made after opening the Runspace626 /// </exception>627 /// <exception cref="InvalidOperationException">628 /// The thread options cannot be changed to the requested value629 /// </exception>630 public abstract PSThreadOptions ThreadOptions631 {632 get;633 set;634 }635 636 /// <summary>637 /// Return version of this runspace.638 /// </summary>639 public abstract Version Version640 {641 get;642 }643 644 /// <summary>645 /// Return whether the Runspace is Remote646 /// We can determine this by whether the runspace is an implementation of LocalRunspace647 /// or infer it from whether the ConnectionInfo property is null648 /// If it happens to be an instance of a LocalRunspace, but has a non-null ConnectionInfo649 /// we declare it to be remote.650 /// </summary>651 public bool RunspaceIsRemote652 {653 get654 {655 return this is not LocalRunspace && ConnectionInfo != null;656 }657 }658 659 /// <summary>660 /// Retrieve information about current state of the runspace.661 /// </summary>662 public abstract RunspaceStateInfo RunspaceStateInfo663 {664 get;665 }666 667 /// <summary>668 /// Gets the current availability of the Runspace.669 /// </summary>670 public abstract RunspaceAvailability RunspaceAvailability671 {672 get;673 protected set;674 }675 676 /// <summary>677 /// InitialSessionState information for this runspace.678 /// </summary>679 public abstract InitialSessionState InitialSessionState680 {681 get;682 }683 684 /// <summary>685 /// Get unique id for this instance of runspace. It is primarily used686 /// for logging purposes.687 /// </summary>688 public Guid InstanceId689 {690 get;691 692 // This id is also used to identify proxy and remote runspace objects.693 // We need to set this when reconstructing a remote runspace to connect694 // to an existing remote runspace.695 internal set;696 } = Guid.NewGuid();697 698 /// <summary>699 /// Gets execution context.700 /// </summary>701 /// <exception cref="InvalidRunspaceStateException">Runspace is not opened.702 /// </exception>703 internal ExecutionContext ExecutionContext704 {705 get706 {707 return GetExecutionContext;708 }709 }710 711 /// <summary>712 /// Skip user profile on engine initialization.713 /// </summary>714 internal bool SkipUserProfile { get; set; } = false;715 716 /// <summary>717 /// Connection information for remote Runspaces, null for local Runspaces.718 /// </summary>719 public abstract RunspaceConnectionInfo ConnectionInfo { get; }720 721 /// <summary>722 /// ConnectionInfo originally supplied by the user.723 /// </summary>724 public abstract RunspaceConnectionInfo OriginalConnectionInfo { get; }725 726 /// <summary>727 /// Manager for JobSourceAdapters registered in this runspace.728 /// </summary>729 public abstract JobManager JobManager { get; }730 731 /// <summary>732 /// DisconnectedOn property applies to remote runspaces that have733 /// been disconnected.734 /// </summary>735 public DateTime? DisconnectedOn736 {737 get;738 internal set;739 }740 741 /// <summary>742 /// ExpiresOn property applies to remote runspaces that have been743 /// disconnected.744 /// </summary>745 public DateTime? ExpiresOn746 {747 get;748 internal set;749 }750 751 /// <summary>752 /// Gets and sets a friendly name for the Runspace.753 /// </summary>754 public string Name755 {756 get;757 set;758 }759 760 /// <summary>761 /// Gets the Runspace Id.762 /// </summary>763 public int Id { get; }764 765 /// <summary>766 /// Gets and sets a boolean indicating whether the runspace has a767 /// debugger attached with <c>Debug-Runspace</c>.768 /// </summary>769 public bool IsRemoteDebuggerAttached { get; internal set; }770 771 /// <summary>772 /// Returns protocol version that the remote server uses for PS remoting.773 /// </summary>774 internal Version GetRemoteProtocolVersion()775 {776 Version remoteProtocolVersionDeclaredByServer;777 bool isServerDeclarationValid = PSPrimitiveDictionary.TryPathGet(778 this.GetApplicationPrivateData(),779 out remoteProtocolVersionDeclaredByServer,780 PSVersionInfo.PSVersionTableName,781 PSVersionInfo.PSRemotingProtocolVersionName);782 783 if (isServerDeclarationValid)784 {785 return remoteProtocolVersionDeclaredByServer;786 }787 else788 {789 return RemotingConstants.ProtocolVersion;790 }791 }792 793 /// <summary>794 /// Engine activity id (for ETW tracing)795 /// </summary>796 internal Guid EngineActivityId { get; set; } = Guid.Empty;797 798 /// <summary>799 /// Returns a read only runspace dictionary.800 /// </summary>801 internal static ReadOnlyDictionary<int, WeakReference<Runspace>> RunspaceDictionary802 {803 get804 {805 lock (s_syncObject)806 {807 return new ReadOnlyDictionary<int, WeakReference<Runspace>>(new Dictionary<int, WeakReference<Runspace>>(s_runspaceDictionary));808 }809 }810 }811 812 private static readonly SortedDictionary<int, WeakReference<Runspace>> s_runspaceDictionary;813 private static readonly object s_syncObject;814 815 /// <summary>816 /// Returns a read only list of runspaces.817 /// </summary>818 internal static IReadOnlyList<Runspace> RunspaceList819 {820 get821 {822 List<Runspace> runspaceList = new List<Runspace>();823 824 lock (s_syncObject)825 {826 foreach (var item in s_runspaceDictionary.Values)827 {828 Runspace runspace;829 if (item.TryGetTarget(out runspace))830 {831 runspaceList.Add(runspace);832 }833 }834 }835 836 return new ReadOnlyCollection<Runspace>(runspaceList);837 }838 }839 840 #endregion properties841 842 #region events843 844 /// <summary>845 /// Event raised when RunspaceState changes.846 /// </summary>847 public abstract event EventHandler<RunspaceStateEventArgs> StateChanged;848 849 /// <summary>850 /// Event raised when the availability of the Runspace changes.851 /// </summary>852 public abstract event EventHandler<RunspaceAvailabilityEventArgs> AvailabilityChanged;853 854 /// <summary>855 /// Returns true if there are any subscribers to the AvailabilityChanged event.856 /// </summary>857 internal abstract bool HasAvailabilityChangedSubscribers858 {859 get;860 }861 862 /// <summary>863 /// Raises the AvailabilityChanged event.864 /// </summary>865 protected abstract void OnAvailabilityChanged(RunspaceAvailabilityEventArgs e);866 867 /// <summary>868 /// Used to raise the AvailabilityChanged event when the state of the currently executing pipeline changes.869 /// </summary>870 /// <remarks>871 /// The possible pipeline states are872 /// NotStarted873 /// Running874 /// Disconnected875 /// Stopping876 /// Stopped877 /// Completed878 /// Failed879 /// </remarks>880 internal void UpdateRunspaceAvailability(PipelineState pipelineState, bool raiseEvent, Guid? cmdInstanceId = null)881 {882 RunspaceAvailability oldAvailability = this.RunspaceAvailability;883 884 switch (oldAvailability)885 {886 // Because of disconnect/connect support runspace availability can now transition887 // in and out of "None" state.888 case RunspaceAvailability.None:889 switch (pipelineState)890 {891 case PipelineState.Running:892 this.RunspaceAvailability = RunspaceAvailability.Busy;893 break;894 895 // Otherwise no change.896 }897 898 break;899 900 case RunspaceAvailability.Available:901 switch (pipelineState)902 {903 case PipelineState.Running:904 this.RunspaceAvailability = RunspaceAvailability.Busy;905 break;906 907 case PipelineState.Disconnected:908 this.RunspaceAvailability = Runspaces.RunspaceAvailability.None;909 break;910 }911 912 break;913 914 case RunspaceAvailability.AvailableForNestedCommand:915 switch (pipelineState)916 {917 case PipelineState.Running:918 this.RunspaceAvailability = RunspaceAvailability.Busy;919 break;920 921 case PipelineState.Completed: // a nested pipeline caused the host to exit nested prompt922 this.RunspaceAvailability = (this.InNestedPrompt || (_runningPowerShells.Count > 1)) ?923 RunspaceAvailability.AvailableForNestedCommand : RunspaceAvailability.Available;924 break;925 926 default:927 break; // no change in the availability928 }929 930 break;931 932 case RunspaceAvailability.Busy:933 case RunspaceAvailability.RemoteDebug:934 switch (pipelineState)935 {936 case PipelineState.Disconnected:937 if (oldAvailability == Runspaces.RunspaceAvailability.RemoteDebug)938 {939 this.RunspaceAvailability = RunspaceAvailability.RemoteDebug;940 }941 else942 {943 this.RunspaceAvailability = RunspaceAvailability.None;944 }945 946 break;947 948 case PipelineState.Stopping:949 break; // no change in the availability950 951 case PipelineState.Completed:952 case PipelineState.Stopped:953 case PipelineState.Failed:954 if (this.InNestedPrompt955 || (this is not RemoteRunspace && this.Debugger.InBreakpoint))956 {957 this.RunspaceAvailability = RunspaceAvailability.AvailableForNestedCommand;958 }959 else960 {961 RemoteRunspace remoteRunspace = this as RemoteRunspace;962 RemoteDebugger remoteDebugger = (remoteRunspace != null) ? remoteRunspace.Debugger as RemoteDebugger : null;963 Internal.ConnectCommandInfo remoteCommand = remoteRunspace?.RemoteCommand;964 if (((pipelineState == PipelineState.Completed) || (pipelineState == PipelineState.Failed) ||965 ((pipelineState == PipelineState.Stopped) && (this.RunspaceStateInfo.State == RunspaceState.Opened)))966 && (remoteCommand != null) && (cmdInstanceId != null) && (remoteCommand.CommandId == cmdInstanceId))967 {968 // Completed, Failed, and Stopped with Runspace.Opened states are command finish states and we know969 // that the command is finished on the server.970 // Setting ConnectCommands to null indicates that the runspace is free to run other971 // commands.972 remoteRunspace.RunspacePool.RemoteRunspacePoolInternal.ConnectCommands = null;973 remoteCommand = null;974 975 if ((remoteDebugger != null) && (pipelineState == PipelineState.Stopped))976 {977 // Notify remote debugger of a stop in case the stop occurred while command was in debug stop.978 remoteDebugger.OnCommandStopped();979 }980 }981 982 Pipeline currentPipeline = this.GetCurrentlyRunningPipeline();983 RemotePipeline remotePipeline = currentPipeline as RemotePipeline;984 Guid? pipeLineCmdInstance = (remotePipeline != null && remotePipeline.PowerShell != null) ? remotePipeline.PowerShell.InstanceId : (Guid?)null;985 if (currentPipeline == null)986 {987 // A runspace is available:988 // - if there is no currently running pipeline989 // and for remote runspaces:990 // - if there is no remote command associated with it.991 // - if the remote runspace pool is marked as available for connection.992 if (remoteCommand == null)993 {994 if (remoteRunspace != null)995 {996 if ((remoteDebugger != null) && (pipelineState == PipelineState.Stopped))997 {998 // Notify remote debugger of a stop in case the stop occurred while command was in debug stop.999 remoteDebugger.OnCommandStopped();1000 }1001 1002 this.RunspaceAvailability =1003 remoteRunspace.RunspacePool.RemoteRunspacePoolInternal.AvailableForConnection ?1004 RunspaceAvailability.Available : Runspaces.RunspaceAvailability.Busy;1005 }1006 else1007 {1008 this.RunspaceAvailability = RunspaceAvailability.Available;1009 }1010 }1011 }1012 else if ((cmdInstanceId != null) && (pipeLineCmdInstance != null) && (cmdInstanceId == pipeLineCmdInstance))1013 {1014 if ((remoteDebugger != null) && (pipelineState == PipelineState.Stopped))1015 {1016 // Notify remote debugger of a stop in case the stop occurred while command was in debug stop.1017 remoteDebugger.OnCommandStopped();1018 }1019 1020 this.RunspaceAvailability = RunspaceAvailability.Available;1021 }1022 else // a nested pipeline completed, but the parent pipeline is still running1023 {1024 if (oldAvailability == Runspaces.RunspaceAvailability.RemoteDebug)1025 {1026 this.RunspaceAvailability = Runspaces.RunspaceAvailability.RemoteDebug;1027 }1028 else if ((currentPipeline.PipelineStateInfo.State == PipelineState.Running) || (_runningPowerShells.Count > 1))1029 {1030 // Either the current pipeline is running or there are other nested commands to run in the Runspace.1031 this.RunspaceAvailability = RunspaceAvailability.Busy;1032 }1033 else1034 {1035 this.RunspaceAvailability = RunspaceAvailability.Available;1036 }1037 }1038 }1039 1040 break;1041 1042 case PipelineState.Running: // this can happen if a nested pipeline is created without entering a nested prompt1043 break; // no change in the availability1044 1045 default:1046 break; // no change in the availability1047 }1048 1049 break;1050 1051 default:1052 Diagnostics.Assert(false, "Invalid RunspaceAvailability");1053 break;1054 }1055 1056 if (raiseEvent && this.RunspaceAvailability != oldAvailability)1057 {1058 OnAvailabilityChanged(new RunspaceAvailabilityEventArgs(this.RunspaceAvailability));1059 }1060 }1061 1062 /// <summary>1063 /// Used to update the runspace availability when the state of the currently executing PowerShell instance changes.1064 /// </summary>1065 /// <remarks>1066 /// The possible invocation states are1067 /// NotStarted1068 /// Running1069 /// Stopping1070 /// Stopped1071 /// Completed1072 /// Failed1073 /// </remarks>1074 internal void UpdateRunspaceAvailability(PSInvocationState invocationState, bool raiseEvent, Guid cmdInstanceId)1075 {1076 switch (invocationState)1077 {1078 case PSInvocationState.NotStarted:1079 UpdateRunspaceAvailability(PipelineState.NotStarted, raiseEvent, cmdInstanceId);1080 break;1081 1082 case PSInvocationState.Running:1083 UpdateRunspaceAvailability(PipelineState.Running, raiseEvent, cmdInstanceId);1084 break;1085 1086 case PSInvocationState.Completed:1087 UpdateRunspaceAvailability(PipelineState.Completed, raiseEvent, cmdInstanceId);1088 break;1089 1090 case PSInvocationState.Failed:1091 UpdateRunspaceAvailability(PipelineState.Failed, raiseEvent, cmdInstanceId);1092 break;1093 1094 case PSInvocationState.Stopping:1095 UpdateRunspaceAvailability(PipelineState.Stopping, raiseEvent, cmdInstanceId);1096 break;1097 1098 case PSInvocationState.Stopped:1099 UpdateRunspaceAvailability(PipelineState.Stopped, raiseEvent, cmdInstanceId);1100 break;1101 1102 case PSInvocationState.Disconnected:1103 UpdateRunspaceAvailability(PipelineState.Disconnected, raiseEvent, cmdInstanceId);1104 break;1105 1106 default:1107 Diagnostics.Assert(false, "Invalid PSInvocationState");1108 break;1109 }1110 }1111 1112 /// <summary>1113 /// Used to update the runspace availability event when the state of the runspace changes.1114 /// </summary>1115 /// <remarks>1116 /// The possible runspace states are:1117 /// BeforeOpen1118 /// Opening1119 /// Opened1120 /// Closed1121 /// Closing1122 /// Broken1123 /// </remarks>1124 protected void UpdateRunspaceAvailability(RunspaceState runspaceState, bool raiseEvent)1125 {1126 RunspaceAvailability oldAvailability = this.RunspaceAvailability;1127 RemoteRunspace remoteRunspace = this as RemoteRunspace;1128 Internal.ConnectCommandInfo remoteCommand = null;1129 bool remoteDebug = false;1130 1131 if (remoteRunspace != null)1132 {1133 remoteCommand = remoteRunspace.RemoteCommand;1134 RemoteDebugger remoteDebugger = remoteRunspace.Debugger as RemoteDebugger;1135 remoteDebug = (remoteDebugger != null) && remoteDebugger.IsRemoteDebug;1136 }1137 1138 switch (oldAvailability)1139 {1140 case RunspaceAvailability.None:1141 switch (runspaceState)1142 {1143 case RunspaceState.Opened:1144 if (remoteDebug)1145 {1146 this.RunspaceAvailability = Runspaces.RunspaceAvailability.RemoteDebug;1147 }1148 else1149 {1150 this.RunspaceAvailability = (remoteCommand == null && GetCurrentlyRunningPipeline() == null) ?1151 RunspaceAvailability.Available : RunspaceAvailability.Busy;1152 }1153 1154 break;1155 1156 default:1157 break; // no change in the availability1158 }1159 1160 break;1161 1162 case RunspaceAvailability.Available:1163 case RunspaceAvailability.AvailableForNestedCommand:1164 case RunspaceAvailability.RemoteDebug:1165 case RunspaceAvailability.Busy:1166 switch (runspaceState)1167 {1168 case RunspaceState.Closing:1169 case RunspaceState.Closed:1170 case RunspaceState.Broken:1171 case RunspaceState.Disconnected:1172 this.RunspaceAvailability = RunspaceAvailability.None;1173 break;1174 1175 default:1176 break; // no change in the availability1177 }1178 1179 break;1180 1181 default:1182 Diagnostics.Assert(false, "Invalid RunspaceAvailability");1183 break;1184 }1185 1186 if (raiseEvent && this.RunspaceAvailability != oldAvailability)1187 {1188 OnAvailabilityChanged(new RunspaceAvailabilityEventArgs(this.RunspaceAvailability));1189 }1190 }1191 1192 /// <summary>1193 /// Used to update the runspace availability from Enter/ExitNestedPrompt and the debugger.1194 /// </summary>1195 internal void UpdateRunspaceAvailability(RunspaceAvailability availability, bool raiseEvent)1196 {1197 RunspaceAvailability oldAvailability = this.RunspaceAvailability;1198 1199 this.RunspaceAvailability = availability;1200 