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.Management.Automation.Language;10using System.Management.Automation.Remoting;11using System.Management.Automation.Remoting.Internal;12using System.Management.Automation.Runspaces;13using System.Management.Automation.Tracing;14using System.Runtime.Serialization;15using System.Text;16using System.Threading;17 18using Microsoft.PowerShell.Commands;19 20using Dbg = System.Management.Automation.Diagnostics;21 22// Stops compiler from warning about unknown warnings23#pragma warning disable 1634, 169124 25namespace System.Management.Automation26{27 /// <summary>28 /// Enumeration for job status values. Indicates the status29 /// of the result object.30 /// </summary>31 public enum JobState32 {33 /// <summary>34 /// Execution of command in job not started.35 /// </summary>36 NotStarted = 0,37 38 /// <summary>39 /// Execution of command in progress.40 /// </summary>41 Running = 1,42 43 /// <summary>44 /// Execution of command completed in all45 /// computernames/runspaces.46 /// </summary>47 Completed = 2,48 49 /// <summary>50 /// An error was encountered when trying to executed51 /// command in one or more computernames/runspaces.52 /// </summary>53 Failed = 3,54 55 /// <summary>56 /// Command execution is cancelled (stopped) in one or more57 /// computernames/runspaces.58 /// </summary>59 Stopped = 4,60 61 /// <summary>62 /// Command execution is blocked (on user input host calls etc)63 /// </summary>64 Blocked = 5,65 66 /// <summary>67 /// The job has been suspended.68 /// </summary>69 Suspended = 6,70 71 /// <summary>72 /// The job is a remote job and has been disconnected from the server.73 /// </summary>74 Disconnected = 7,75 76 /// <summary>77 /// Suspend is in progress.78 /// </summary>79 Suspending = 8,80 81 /// <summary>82 /// Stop is in progress.83 /// </summary>84 Stopping = 9,85 86 /// <summary>87 /// Script execution is halted in a debugger stop.88 /// </summary>89 AtBreakpoint = 1090 }91 92 /// <summary>93 /// Defines exception which is thrown when state of the PSJob is different94 /// from the expected state.95 /// </summary>96 public class InvalidJobStateException : SystemException97 {98 /// <summary>99 /// Creates a new instance of InvalidPSJobStateException class.100 /// </summary>101 public InvalidJobStateException()102 : base103 (104 PSRemotingErrorInvariants.FormatResourceString105 (106 RemotingErrorIdStrings.InvalidJobStateGeneral107 )108 )109 {110 }111 112 /// <summary>113 /// Creates a new instance of InvalidPSJobStateException class.114 /// </summary>115 /// <param name="message">116 /// The error message that explains the reason for the exception.117 /// </param>118 public InvalidJobStateException(string message)119 : base(message)120 {121 }122 123 /// <summary>124 /// Creates a new instance of InvalidPSJobStateException class.125 /// </summary>126 /// <param name="message">127 /// The error message that explains the reason for the exception.128 /// </param>129 /// <param name="innerException">130 /// The exception that is the cause of the current exception.131 /// </param>132 public InvalidJobStateException(string message, Exception innerException)133 : base(message, innerException)134 {135 }136 137 /// <summary>138 /// Creates a new instance of InvalidJobStateException class.139 /// </summary>140 /// <param name="currentState">141 /// The Job State at the time of the error.142 /// </param>143 /// <param name="actionMessage">144 /// An additional message that gives more information about the error. Used145 /// for context after a generalized error message.146 /// </param>147 public InvalidJobStateException(JobState currentState, string actionMessage)148 : base149 (150 PSRemotingErrorInvariants.FormatResourceString151 (152 RemotingErrorIdStrings.InvalidJobStateSpecific, currentState, actionMessage153 )154 )155 {156 _currState = currentState;157 }158 159 /// <summary>160 /// Initializes a new instance of the InvalidPSJobStateException and defines value of161 /// CurrentState.162 /// </summary>163 /// <param name="currentState">Current state of powershell.</param>164 internal InvalidJobStateException(JobState currentState)165 : base166 (167 PSRemotingErrorInvariants.FormatResourceString168 (169 RemotingErrorIdStrings.InvalidJobStateGeneral170 )171 )172 {173 _currState = currentState;174 }175 176 #region ISerializable Members177 178 // No need to implement GetObjectData179 // if all fields are static or [NonSerialized]180 181 /// <summary>182 /// Initializes a new instance of the InvalidPSJobStateException183 /// class with serialized data.184 /// </summary>185 /// <param name="info">186 /// The <see cref="SerializationInfo"/> that holds the serialized object187 /// data about the exception being thrown.188 /// </param>189 /// <param name="context">190 /// The <see cref="StreamingContext"/> that contains contextual information191 /// about the source or destination.192 /// </param>193 [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] 194 protected195 InvalidJobStateException(SerializationInfo info, StreamingContext context)196 {197 throw new NotSupportedException();198 }199 200 #endregion201 202 /// <summary>203 /// Gets CurrentState of the Job.204 /// </summary>205 public JobState CurrentState206 {207 get208 {209 return _currState;210 }211 }212 213 /// <summary>214 /// State of job when exception was thrown.215 /// </summary>216 [NonSerialized]217 private readonly JobState _currState = 0;218 }219 220 /// <summary>221 /// Type which has information about JobState and Exception222 /// ,if any, associated with JobState.223 /// </summary>224 public sealed class JobStateInfo225 {226 #region constructors227 228 /// <summary>229 /// Constructor for state changes not resulting from an error.230 /// </summary>231 /// <param name="state">Execution state.</param>232 public JobStateInfo(JobState state)233 : this(state, null)234 {235 }236 237 /// <summary>238 /// Constructor for state changes with an optional error.239 /// </summary>240 /// <param name="state">The new state.</param>241 /// <param name="reason">A non-null exception if the state change was242 /// caused by an error,otherwise; null.243 /// </param>244 public JobStateInfo(JobState state, Exception reason)245 {246 State = state;247 Reason = reason;248 }249 250 /// <summary>251 /// Copy constructor to support cloning.252 /// </summary>253 /// <param name="jobStateInfo">Source information.</param>254 /// <throws>255 /// ArgumentNullException when <paramref name="jobStateInfo"/> is null.256 /// </throws>257 internal JobStateInfo(JobStateInfo jobStateInfo)258 {259 State = jobStateInfo.State;260 Reason = jobStateInfo.Reason;261 }262 263 #endregion constructors264 265 #region public_properties266 267 /// <summary>268 /// The state of the job.269 /// </summary>270 /// <remarks>271 /// This value indicates the state of the job .272 /// </remarks>273 public JobState State { get; }274 275 /// <summary>276 /// The reason for the state change, if caused by an error.277 /// </summary>278 /// <remarks>279 /// The value of this property is non-null if the state280 /// changed due to an error. Otherwise, the value of this281 /// property is null.282 /// </remarks>283 public Exception Reason { get; }284 285 #endregion public_properties286 287 /// <summary>288 /// Override for ToString()289 /// </summary>290 /// <returns></returns>291 public override string ToString()292 {293 return State.ToString();294 }295 296 /// <summary>297 /// Clones this object.298 /// </summary>299 /// <returns>Cloned object.</returns>300 internal JobStateInfo Clone()301 {302 return new JobStateInfo(this);303 }304 305 #region private_fields306 307 #endregion private_fields308 }309 310 /// <summary>311 /// Event arguments passed to JobStateEvent handlers312 /// <see cref="Job.StateChanged"/> event.313 /// </summary>314 public sealed class JobStateEventArgs : EventArgs315 {316 #region constructors317 318 /// <summary>319 /// Constructor of JobStateEventArgs.320 /// </summary>321 /// <param name="jobStateInfo">The current state of the job.</param>322 public JobStateEventArgs(JobStateInfo jobStateInfo)323 : this(jobStateInfo, null)324 {325 }326 327 /// <summary>328 /// Constructor of JobStateEventArgs.329 /// </summary>330 /// <param name="jobStateInfo">The current state of the job.</param>331 /// <param name="previousJobStateInfo">The previous state of the job.</param>332 public JobStateEventArgs(JobStateInfo jobStateInfo, JobStateInfo previousJobStateInfo)333 {334 if (jobStateInfo == null)335 {336 throw PSTraceSource.NewArgumentNullException(nameof(jobStateInfo));337 }338 339 JobStateInfo = jobStateInfo;340 PreviousJobStateInfo = previousJobStateInfo;341 }342 343 #endregion constructors344 345 #region public_properties346 347 /// <summary>348 /// Info about the current state of the job.349 /// </summary>350 public JobStateInfo JobStateInfo { get; }351 352 /// <summary>353 /// Info about the previous state of the job.354 /// </summary>355 public JobStateInfo PreviousJobStateInfo { get; }356 357 #endregion public_properties358 }359 360 /// <summary>361 /// Object that must be created by PowerShell to allow reuse of an ID for a job.362 /// Also allows setting of the Instance Id so that jobs may be recreated.363 /// </summary>364 public sealed class JobIdentifier365 {366 internal JobIdentifier(int id, Guid instanceId)367 {368 if (id <= 0)369 PSTraceSource.NewArgumentException(nameof(id), RemotingErrorIdStrings.JobSessionIdLessThanOne, id);370 Id = id;371 InstanceId = instanceId;372 }373 374 internal int Id { get; }375 376 internal Guid InstanceId { get; private set; }377 }378 379 /// <summary>380 /// Interface to expose a job debugger.381 /// </summary>382#nullable enable383 public interface IJobDebugger384 {385 /// <summary>386 /// Job Debugger.387 /// </summary>388 Debugger? Debugger389 {390 get;391 }392 393 /// <summary>394 /// True if job is running asynchronously.395 /// </summary>396 bool IsAsync397 {398 get;399 set;400 }401 }402#nullable restore403 404 /// <summary>405 /// Represents a command running in background. A job object can internally406 /// contain many child job objects.407 /// </summary>408 public abstract class Job : IDisposable409 {410 #region Constructor411 412 /// <summary>413 /// Default constructor.414 /// </summary>415 protected Job()416 {417 Id = System.Threading.Interlocked.Increment(ref s_jobIdSeed);418 }419 420 /// <summary>421 /// Creates an instance of this class.422 /// </summary>423 /// <param name="command">Command invoked by this job object.</param>424 protected Job(string command)425 : this()426 {427 Command = command;428 _name = AutoGenerateJobName();429 }430 431 /// <summary>432 /// Creates an instance of this class.433 /// </summary>434 /// <param name="command">Command invoked by this job object.</param>435 /// <param name="name">Friendly name for the job object.</param>436 protected Job(string command, string name)437 : this(command)438 {439 if (!string.IsNullOrEmpty(name))440 {441 _name = name;442 }443 }444 445 /// <summary>446 /// Creates an instance of this class.447 /// </summary>448 /// <param name="command">Command invoked by this job object.</param>449 /// <param name="name">Friendly name for the job object.</param>450 /// <param name="childJobs">Child jobs of this job object.</param>451 protected Job(string command, string name, IList<Job> childJobs)452 : this(command, name)453 {454 _childJobs = childJobs;455 }456 457 /// <summary>458 /// Creates an instance of this class.459 /// </summary>460 /// <param name="command">Command invoked by this job object.</param>461 /// <param name="name">Friendly name for the job object.</param>462 /// <param name="token">Id and InstanceId pair to be used for this job object.</param>463 /// <remarks>The JobIdentifier is a token that must be issued by PowerShell to allow464 /// reuse of the Id. This is the only way to set either Id or instance Id.</remarks>465 protected Job(string command, string name, JobIdentifier token)466 {467 if (token == null)468 throw PSTraceSource.NewArgumentNullException(nameof(token), RemotingErrorIdStrings.JobIdentifierNull);469 if (token.Id > s_jobIdSeed)470 {471 throw PSTraceSource.NewArgumentException(nameof(token), RemotingErrorIdStrings.JobIdNotYetAssigned, token.Id);472 }473 474 Command = command;475 476 Id = token.Id;477 InstanceId = token.InstanceId;478 479 if (!string.IsNullOrEmpty(name))480 {481 _name = name;482 }483 else484 {485 _name = AutoGenerateJobName();486 }487 }488 489 /// <summary>490 /// Creates an instance of this class.491 /// </summary>492 /// <param name="command">Command invoked by this job object.</param>493 /// <param name="name">Friendly name for the job object.</param>494 /// <param name="instanceId">InstanceId to be used for this job object.</param>495 /// <remarks>The InstanceId may need to be set to maintain job identity across496 /// instances of the process.</remarks>497 protected Job(string command, string name, Guid instanceId)498 : this(command, name)499 {500 InstanceId = instanceId;501 }502 503 internal static string GetCommandTextFromInvocationInfo(InvocationInfo invocationInfo)504 {505 if (invocationInfo == null)506 {507 return null;508 }509 510 IScriptExtent scriptExtent = invocationInfo.ScriptPosition;511 if ((scriptExtent != null) && (scriptExtent.StartScriptPosition != null) && !string.IsNullOrWhiteSpace(scriptExtent.StartScriptPosition.Line))512 {513 Dbg.Assert(scriptExtent.StartScriptPosition.ColumnNumber > 0, "Column numbers start at 1");514 Dbg.Assert(scriptExtent.StartScriptPosition.ColumnNumber <= scriptExtent.StartScriptPosition.Line.Length, "Column numbers are not greater than the length of a line");515 return scriptExtent.StartScriptPosition.Line.AsSpan(scriptExtent.StartScriptPosition.ColumnNumber - 1).Trim().ToString();516 }517 518 return invocationInfo.InvocationName;519 }520 521 #endregion Constructor522 523 #region Private Members524 525 private ManualResetEvent _finished = new ManualResetEvent(false);526 527 private string _name;528 private IList<Job> _childJobs;529 internal readonly object syncObject = new object(); // object used for synchronization530 // ISSUE: Should Result be public property531 private PSDataCollection<PSStreamObject> _results = new PSDataCollection<PSStreamObject>();532 private bool _resultsOwner = true;533 private PSDataCollection<ErrorRecord> _error = new PSDataCollection<ErrorRecord>();534 private bool _errorOwner = true;535 private PSDataCollection<ProgressRecord> _progress = new PSDataCollection<ProgressRecord>();536 private bool _progressOwner = true;537 private PSDataCollection<VerboseRecord> _verbose = new PSDataCollection<VerboseRecord>();538 private bool _verboseOwner = true;539 private PSDataCollection<WarningRecord> _warning = new PSDataCollection<WarningRecord>();540 private bool _warningOwner = true;541 private PSDataCollection<DebugRecord> _debug = new PSDataCollection<DebugRecord>();542 private bool _debugOwner = true;543 private PSDataCollection<InformationRecord> _information = new PSDataCollection<InformationRecord>();544 private bool _informationOwner = true;545 private PSDataCollection<PSObject> _output = new PSDataCollection<PSObject>();546 private bool _outputOwner = true;547 548 /// <summary>549 /// Static variable which is incremented to generate id.550 /// </summary>551 private static int s_jobIdSeed = 0;552 553 private string _jobTypeName = string.Empty;554 555 #endregion Private Members556 557 #region Job Properties558 559 /// <summary>560 /// Command Invoked by this Job.561 /// </summary>562 public string Command { get; }563 564 /// <summary>565 /// Status of the command execution.566 /// </summary>567 public JobStateInfo JobStateInfo { get; private set; } = new JobStateInfo(JobState.NotStarted);568 569 /// <summary>570 /// Wait Handle which is signaled when job is finished.571 /// This is set when state of the job is set to Completed,572 /// Stopped or Failed.573 /// </summary>574 public WaitHandle Finished575 {576 get577 {578 lock (this.syncObject)579 {580 if (_finished != null)581 {582 return _finished;583 }584 else585 {586 // Damage control mode:587 // Somebody is trying to get Finished handle for an already disposed Job instance.588 // Return an already triggered handle (disposed job is finished by definition).589 // The newly created handle will not be disposed in a deterministic manner590 // and in some circumstances can be mistaken for a handle leak.591 return new ManualResetEvent(true);592 }593 }594 }595 }596 597 /// <summary>598 /// Unique identifier for this job.599 /// </summary>600 public Guid InstanceId { get; } = Guid.NewGuid();601 602 /// <summary>603 /// Short identifier for this result which will be604 /// recycled and used within a process.605 /// </summary>606 public int Id { get; }607 608 /// <summary>609 /// Name for identifying this job object.610 /// </summary>611 public string Name612 {613 get614 {615 return _name;616 }617 618 set619 {620 AssertNotDisposed();621 _name = value;622 }623 }624 625 /// <summary>626 /// List of child jobs contained within this job.627 /// </summary>628 public IList<Job> ChildJobs629 {630 get631 {632 if (_childJobs == null)633 {634 lock (syncObject)635 {636 _childJobs ??= new List<Job>();637 }638 }639 640 return _childJobs;641 }642 }643 644 /// <summary>645 /// Success status of the command execution.646 /// </summary>647 public abstract string StatusMessage { get; }648 649 /// <summary>650 /// Indicates that more data is available in this651 /// result object for reading.652 /// </summary>653 public abstract bool HasMoreData { get; }654 655 /// <summary>656 /// Time job was started.657 /// </summary>658 public DateTime? PSBeginTime { get; protected set; } = null;659 660 /// <summary>661 /// Time job stopped.662 /// </summary>663 public DateTime? PSEndTime { get; protected set; } = null;664 665 /// <summary>666 /// Job type name.667 /// </summary>668 public string PSJobTypeName669 {670 get671 {672 return _jobTypeName;673 }674 675 protected internal set676 {677 _jobTypeName = value ?? this.GetType().ToString();678 }679 }680 681 #region results682 683 /// <summary>684 /// Result objects from this job. If this object is not a685 /// leaf node (with no children), then this will686 /// aggregate the results from all child jobs.687 /// </summary>688 internal PSDataCollection<PSStreamObject> Results689 {690 get691 {692 return _results;693 }694 695 set696 {697 if (value == null)698 {699 throw PSTraceSource.NewArgumentNullException("Results");700 }701 702 lock (syncObject)703 {704 AssertChangesAreAccepted();705 _resultsOwner = false;706 _results = value;707 }708 }709 }710 711 /// <summary>712 /// Indicates if a particular Job type uses the713 /// internal results collection.714 /// </summary>715 internal bool UsesResultsCollection { get; set; }716 717 /// <summary>718 /// Suppresses forwarding of job output into a cmdlet (like Receive-Job).719 /// This flag modifies functionality of <see cref="WriteObject"/> method, so that it doesnt add output-processing to <see cref="Results"/> collection.720 /// </summary>721 internal bool SuppressOutputForwarding { get; set; }722 723 internal virtual void WriteObject(object outputObject)724 {725 PSObject pso = (outputObject == null) ? null : PSObject.AsPSObject(outputObject);726 this.Output.Add(pso);727 728 if (!SuppressOutputForwarding)729 {730 this.Results.Add(new PSStreamObject(PSStreamObjectType.Output, pso));731 }732 }733 734 /// <summary>735 /// Allows propagating of terminating exceptions from remote "throw" statement736 /// (normally / by default all remote errors are transformed into non-terminating errors.737 /// </summary>738 internal bool PropagateThrows { get; set; }739 740 private void WriteError(Cmdlet cmdlet, ErrorRecord errorRecord)741 {742 if (this.PropagateThrows)743 {744 Exception e = GetExceptionFromErrorRecord(errorRecord);745 if (e != null)746 throw e;747 }748 749 errorRecord.PreserveInvocationInfoOnce = true;750 cmdlet.WriteError(errorRecord);751 }752 753 private static Exception GetExceptionFromErrorRecord(ErrorRecord errorRecord)754 {755 if (errorRecord.Exception is not RuntimeException runtimeException)756 return null;757 758 if (runtimeException is not RemoteException remoteException)759 return null;760 761 PSPropertyInfo wasThrownFromThrow =762 remoteException.SerializedRemoteException.Properties["WasThrownFromThrowStatement"];763 if (wasThrownFromThrow == null || !((bool)wasThrownFromThrow.Value))764 return null;765 766 runtimeException.WasThrownFromThrowStatement = true;767 return runtimeException;768 }769 770 internal virtual void WriteError(ErrorRecord errorRecord)771 {772 Error.Add(errorRecord);773 if (PropagateThrows)774 {775 Exception exception = GetExceptionFromErrorRecord(errorRecord);776 if (exception != null)777 {778 Results.Add(new PSStreamObject(PSStreamObjectType.Exception, exception));779 return;780 }781 }782 783 Results.Add(new PSStreamObject(PSStreamObjectType.Error, errorRecord));784 }785 786 internal void WriteError(ErrorRecord errorRecord, out Exception exceptionThrownOnCmdletThread)787 {788 this.Error.Add(errorRecord);789 this.InvokeCmdletMethodAndWaitForResults<object>(790 (Cmdlet cmdlet) =>791 {792 this.WriteError(cmdlet, errorRecord);793 return null;794 },795 out exceptionThrownOnCmdletThread);796 }797 798 internal virtual void WriteWarning(string message)799 {800 this.Warning.Add(new WarningRecord(message));801 this.Results.Add(new PSStreamObject(PSStreamObjectType.Warning, message));802 }803 804 internal virtual void WriteVerbose(string message)805 {806 this.Verbose.Add(new VerboseRecord(message));807 this.Results.Add(new PSStreamObject(PSStreamObjectType.Verbose, message));808 }809 810 internal virtual void WriteDebug(string message)811 {812 this.Debug.Add(new DebugRecord(message));813 this.Results.Add(new PSStreamObject(PSStreamObjectType.Debug, message));814 }815 816 internal virtual void WriteProgress(ProgressRecord progressRecord)817 {818 if ((progressRecord.ParentActivityId == (-1)) && (_parentActivityId != null))819 {820 progressRecord = new ProgressRecord(progressRecord) { ParentActivityId = _parentActivityId.Value };821 }822 823 Progress.Add(progressRecord);824 Results.Add(new PSStreamObject(PSStreamObjectType.Progress, progressRecord));825 }826 827 internal virtual void WriteInformation(InformationRecord informationRecord)828 {829 Information.Add(informationRecord);830 Results.Add(new PSStreamObject(PSStreamObjectType.Information, informationRecord));831 }832 833 private Lazy<int> _parentActivityId;834 835 internal void SetParentActivityIdGetter(Func<int> parentActivityIdGetter)836 {837 Dbg.Assert(parentActivityIdGetter != null, "Caller should verify parentActivityIdGetter != null");838 _parentActivityId = new Lazy<int>(parentActivityIdGetter);839 }840 841 internal bool ShouldContinue(string query, string caption)842 {843 Exception exceptionThrownOnCmdletThread;844 return this.ShouldContinue(query, caption, out exceptionThrownOnCmdletThread);845 }846 847 internal bool ShouldContinue(string query, string caption, out Exception exceptionThrownOnCmdletThread)848 {849 bool methodResult = InvokeCmdletMethodAndWaitForResults(850 cmdlet => cmdlet.ShouldContinue(query, caption),851 out exceptionThrownOnCmdletThread);852 return methodResult;853 }854 855 internal virtual void NonblockingShouldProcess(856 string verboseDescription,857 string verboseWarning,858 string caption)859 {860 InvokeCmdletMethodAndIgnoreResults(861 (Cmdlet cmdlet) =>862 {863 ShouldProcessReason throwAwayProcessReason;864 cmdlet.ShouldProcess(865 verboseDescription,866 verboseWarning,867 caption,868 out throwAwayProcessReason);869 });870 }871 872 internal virtual bool ShouldProcess(873 string verboseDescription,874 string verboseWarning,875 string caption,876 out ShouldProcessReason shouldProcessReason,877 out Exception exceptionThrownOnCmdletThread)878 {879 ShouldProcessReason closureSafeShouldProcessReason = ShouldProcessReason.None;880 881 bool methodResult = InvokeCmdletMethodAndWaitForResults(882 cmdlet => cmdlet.ShouldProcess(883 verboseDescription,884 verboseWarning,885 caption,886 out closureSafeShouldProcessReason),887 out exceptionThrownOnCmdletThread);888 889 shouldProcessReason = closureSafeShouldProcessReason;890 return methodResult;891 }892 893 private void InvokeCmdletMethodAndIgnoreResults(Action<Cmdlet> invokeCmdletMethod)894 {895 object resultsLock = new object();896 CmdletMethodInvoker<object> methodInvoker = new CmdletMethodInvoker<object>897 {898 Action = (Cmdlet cmdlet) => { invokeCmdletMethod(cmdlet); return null; },899 Finished = null,900 SyncObject = resultsLock901 };902 Results.Add(new PSStreamObject(PSStreamObjectType.BlockingError, methodInvoker));903 }904 905 private T InvokeCmdletMethodAndWaitForResults<T>(Func<Cmdlet, T> invokeCmdletMethodAndReturnResult, out Exception exceptionThrownOnCmdletThread)906 {907 Dbg.Assert(invokeCmdletMethodAndReturnResult != null, "Caller should verify invokeCmdletMethodAndReturnResult != null");908 909 T methodResult = default(T);910 Exception closureSafeExceptionThrownOnCmdletThread = null;911 object resultsLock = new object();912 using (var gotResultEvent = new ManualResetEventSlim(false))913 {914 EventHandler<JobStateEventArgs> stateChangedEventHandler =915 (object sender, JobStateEventArgs eventArgs) =>916 {917 if (IsFinishedState(eventArgs.JobStateInfo.State) || eventArgs.JobStateInfo.State == JobState.Stopping)918 {919 lock (resultsLock)920 {921 closureSafeExceptionThrownOnCmdletThread = new OperationCanceledException();922 }923 924 gotResultEvent.Set();925 }926 };927 this.StateChanged += stateChangedEventHandler;928 Interlocked.MemoryBarrier();929 try930 {931 stateChangedEventHandler(null, new JobStateEventArgs(this.JobStateInfo));932 933 if (!gotResultEvent.IsSet)934 {935 this.SetJobState(JobState.Blocked);936 937 // addition to results column happens here938 CmdletMethodInvoker<T> methodInvoker = new CmdletMethodInvoker<T>939 {940 Action = invokeCmdletMethodAndReturnResult,941 Finished = gotResultEvent,942 SyncObject = resultsLock943 };944 PSStreamObjectType objectType = PSStreamObjectType.ShouldMethod;945 946 if (typeof(T) == typeof(object))947 objectType = PSStreamObjectType.BlockingError;948 949 Results.Add(new PSStreamObject(objectType, methodInvoker));950 951 gotResultEvent.Wait();952 this.SetJobState(JobState.Running);953 954 lock (resultsLock)955 {956 if (closureSafeExceptionThrownOnCmdletThread == null) // stateChangedEventHandler didn't set the results? = ok to clobber results?957 {958 closureSafeExceptionThrownOnCmdletThread = methodInvoker.ExceptionThrownOnCmdletThread;959 methodResult = methodInvoker.MethodResult;960 }961 }962 }963 }964 finally965 {966 this.StateChanged -= stateChangedEventHandler;967 }968 }969 970 lock (resultsLock)971 {972 exceptionThrownOnCmdletThread = closureSafeExceptionThrownOnCmdletThread;973 return methodResult;974 }975 }976 977 internal virtual void ForwardAvailableResultsToCmdlet(Cmdlet cmdlet)978 {979 foreach (PSStreamObject obj in Results.ReadAll())980 {981 obj.WriteStreamObject(cmdlet);982 }983 }984 985 internal virtual void ForwardAllResultsToCmdlet(Cmdlet cmdlet)986 {987 foreach (PSStreamObject obj in this.Results)988 {989 obj.WriteStreamObject(cmdlet);990 }991 }992 993 /// <summary>994 /// This method is introduce for delaying the loading of streams995 /// for a particular job.996 /// </summary>997 protected virtual void DoLoadJobStreams()998 {999 }1000 1001 /// <summary>1002 /// Unloads job streams information. Enables jobs to1003 /// clear stream information from memory.1004 /// </summary>1005 protected virtual void DoUnloadJobStreams()1006 {1007 }1008 1009 /// <summary>1010 /// Load the required job streams.1011 /// </summary>1012 public void LoadJobStreams()1013 {1014 if (_jobStreamsLoaded)1015 {1016 return;1017 }1018 1019 lock (syncObject)1020 {1021 if (_jobStreamsLoaded)1022 {1023 return;1024 }1025 1026 _jobStreamsLoaded = true;1027 }1028 1029 try1030 {1031 DoLoadJobStreams();1032 }1033 catch (Exception e)1034 {1035 // third party call-out for platform API1036 // Therefore it is fine to eat the exception1037 // here1038 using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource())1039 {1040 tracer.TraceException(e);1041 }1042 }1043 }1044 1045 private bool _jobStreamsLoaded;1046 1047 /// <summary>1048 /// Unload the required job streams.1049 /// </summary>1050 public void UnloadJobStreams()1051 {1052 if (!_jobStreamsLoaded) return;1053 1054 lock (syncObject)1055 {1056 if (!_jobStreamsLoaded) return;1057 _jobStreamsLoaded = false;1058 }1059 1060 try1061 {1062 DoUnloadJobStreams();1063 }1064 catch (Exception e)1065 {1066 // third party call-out for platform API1067 // Therefore it is fine to eat the exception1068 // here1069 using (PowerShellTraceSource tracer = PowerShellTraceSourceFactory.GetTraceSource())1070 {1071 tracer.TraceException(e);1072 }1073 }1074 }1075 1076 /// <summary>1077 /// Gets or sets the output buffer. Output of job is written1078 /// into this buffer.1079 /// </summary>1080 /// <exception cref="ArgumentNullException">1081 /// Cannot set to a null value.1082 /// </exception>1083 /// <exception cref="ObjectDisposedException">1084 /// Object is disposed.1085 /// </exception>1086 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]1087 public PSDataCollection<PSObject> Output1088 {1089 get1090 {1091 LoadJobStreams(); // for delayed loading1092 return _output;1093 }1094 1095 set1096 {1097 if (value == null)1098 {1099 throw PSTraceSource.NewArgumentNullException("Output");1100 }1101 1102 lock (syncObject)1103 {1104 AssertChangesAreAccepted();1105 _outputOwner = false;1106 _output = value;1107 _jobStreamsLoaded = true;1108 }1109 }1110 }1111 1112 /// <summary>1113 /// Gets or sets the error buffer. Errors of job are written1114 /// into this buffer.1115 /// </summary>1116 /// <exception cref="ArgumentNullException">1117 /// Cannot set to a null value.1118 /// </exception>1119 /// <exception cref="ObjectDisposedException">1120 /// Object is disposed.1121 /// </exception>1122 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]1123 public PSDataCollection<ErrorRecord> Error1124 {1125 get1126 {1127 LoadJobStreams(); // for delayed loading1128 return _error;1129 }1130 1131 set1132 {1133 if (value == null)1134 {1135 throw PSTraceSource.NewArgumentNullException("Error");1136 }1137 1138 lock (syncObject)1139 {1140 AssertChangesAreAccepted();1141 _errorOwner = false;1142 _error = value;1143 _jobStreamsLoaded = true;1144 }1145 }1146 }1147 1148 /// <summary>1149 /// Gets or sets the progress buffer. Progress of job is written1150 /// into this buffer.1151 /// </summary>1152 /// <exception cref="ArgumentNullException">1153 /// Cannot set to a null value.1154 /// </exception>1155 /// <exception cref="ObjectDisposedException">1156 /// Object is disposed.1157 /// </exception>1158 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]1159 public PSDataCollection<ProgressRecord> Progress1160 {1161 get1162 {1163 LoadJobStreams(); // for delayed loading1164 return _progress;1165 }1166 1167 set1168 {1169 if (value == null)1170 {1171 throw PSTraceSource.NewArgumentNullException("Progress");1172 }1173 1174 lock (syncObject)1175 {1176 AssertChangesAreAccepted();1177 _progressOwner = false;1178 _progress = value;1179 _jobStreamsLoaded = true;1180 }1181 }1182 }1183 1184 /// <summary>1185 /// Gets or sets the verbose buffer. Verbose output of job is written to1186 /// this stream.1187 /// </summary>1188 /// <exception cref="ObjectDisposedException">1189 /// Object is disposed.1190 /// </exception>1191 [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]1192 public PSDataCollection<VerboseRecord> Verbose1193 {1194 get1195 {1196 LoadJobStreams(); // for delayed loading1197 return _verbose;1198 }1199 1200 set