MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#pragma warning disable 1634, 16915 6using System.Collections;7using System.Collections.Generic;8using System.Collections.ObjectModel;9using System.Management.Automation.Host;10using System.Management.Automation.Internal;11using System.Management.Automation.Internal.Host;12using System.Management.Automation.Remoting;13using System.Management.Automation.Runspaces;14using System.Threading;15 16using Dbg = System.Management.Automation.Diagnostics;17 18namespace System.Management.Automation19{20 /// <summary>21 /// Monad internal implementation of the ICommandRuntime2 interface22 /// used for execution in the monad engine environment.23 ///24 /// There will be one instance of this class for each cmdlet added to25 /// a pipeline. When the cmdlet calls its WriteObject API, that API will call26 /// the WriteObject implementation in this class which, in turn, calls27 /// the downstream cmdlet.28 /// </summary>29 internal class MshCommandRuntime : ICommandRuntime230 {31 #region private_members32 33 /// <summary>34 /// Gets/Set the execution context value for this runtime object.35 /// </summary>36 internal ExecutionContext Context { get; set; }37 38 private SessionState _state = null;39 internal InternalHost CBhost;40 41 /// <summary>42 /// The host object for this object.43 /// </summary>44 public PSHost Host { get; }45 46 // Output pipes.47 private Pipe _inputPipe;48 private Pipe _outputPipe;49 private Pipe _errorOutputPipe;50 51 /// <summary>52 /// IsClosed indicates to the Cmdlet whether its upstream partner53 /// could still write more data to its incoming queue.54 /// Note that there may still be data in the incoming queue.55 /// </summary>56 internal bool IsClosed { get; set; }57 58 /// <summary>59 /// True if we're not closed and the input pipe is non-null...60 /// </summary>61 internal bool IsPipelineInputExpected62 {63 get64 {65 // No objects in the input pipe66 // The pipe is closed. So there can't be any more object67 if (IsClosed && (_inputPipe == null || _inputPipe.Empty))68 {69 return false;70 }71 72 return true;73 }74 }75 76 /// <summary>77 /// This allows all success output to be set to a variable. Similar to the way -errorvariable sets78 /// all errors to a variable name. Semantically this is equivalent to : cmd |set-var varname -passthru79 /// but it should be MUCH faster as there is no binding that takes place.80 /// </summary>81 /// <exception cref="System.ArgumentNullException">82 /// may not be set to null83 /// </exception>84 /// <remarks>85 /// This is a common parameter via class CommonParameters.86 /// </remarks>87 internal string OutVariable { get; set; }88 89 internal IList OutVarList { get { return _outVarList; } set { _outVarList = value; } }90 91 private IList _outVarList = null;92 93 internal PipelineProcessor PipelineProcessor { get; set; }94 95 private readonly CommandInfo _commandInfo;96 private readonly InternalCommand _thisCommand;97 98 #endregion private_members99 100 internal MshCommandRuntime(ExecutionContext context, CommandInfo commandInfo, InternalCommand thisCommand)101 {102 Context = context;103 Host = context.EngineHostInterface;104 this.CBhost = (InternalHost)context.EngineHostInterface;105 _commandInfo = commandInfo;106 _thisCommand = thisCommand;107 LogPipelineExecutionDetail = InitShouldLogPipelineExecutionDetail();108 }109 110 /// <summary>111 /// For diagnostic purposes.112 /// </summary>113 /// <returns></returns>114 public override string ToString()115 {116 if (_commandInfo != null)117 return _commandInfo.ToString();118 return "<NullCommandInfo>"; // does not require localization119 }120 121 private InvocationInfo _myInvocation;122 /// <summary>123 /// Return the invocation data object for this command.124 /// </summary>125 /// <value>The invocation object for this command.</value>126 internal InvocationInfo MyInvocation127 {128 get { return _myInvocation ??= _thisCommand.MyInvocation; }129 }130 131 /// <summary>132 /// Internal helper. Indicates whether stop has been requested on this command.133 /// </summary>134 internal bool IsStopping135 {136 get { return (this.PipelineProcessor != null && this.PipelineProcessor.Stopping); }137 }138 139 #region Write140 141 // Trust: WriteObject needs to respect EmitTrustCategory142 143 /// <summary>144 /// Writes the object to the output pipe.145 /// </summary>146 /// <param name="sendToPipeline">147 /// The object that needs to be written. This will be written as148 /// a single object, even if it is an enumeration.149 /// </param>150 /// <exception cref="System.Management.Automation.PipelineStoppedException">151 /// The pipeline has already been terminated, or was terminated152 /// during the execution of this method.153 /// The Cmdlet should generally just allow PipelineStoppedException154 /// to percolate up to the caller of ProcessRecord etc.155 /// </exception>156 /// <exception cref="System.InvalidOperationException">157 /// Not permitted at this time or from this thread.158 /// WriteObject may only be called during a call to this Cmdlet's159 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,160 /// and only from that thread.161 /// </exception>162 /// <seealso cref="System.Management.Automation.ICommandRuntime.WriteObject(object,bool)"/>163 /// <seealso cref="System.Management.Automation.ICommandRuntime.WriteError(ErrorRecord)"/>164 public void WriteObject(object sendToPipeline)165 {166 // This check will be repeated in _WriteObjectSkipAllowCheck,167 // but we want PipelineStoppedException to take precedence168 // over InvalidOperationException if the pipeline has been169 // closed.170 ThrowIfStopping();171 172#if CORECLR173 // SecurityContext is not supported in CoreCLR174 DoWriteObject(sendToPipeline);175#else176 if (UseSecurityContextRun)177 {178 if (PipelineProcessor == null || PipelineProcessor.SecurityContext == null)179 throw PSTraceSource.NewInvalidOperationException(PipelineStrings.WriteNotPermitted);180 ContextCallback delegateCallback =181 new ContextCallback(DoWriteObject);182 183 SecurityContext.Run(184 PipelineProcessor.SecurityContext.CreateCopy(),185 delegateCallback,186 sendToPipeline);187 }188 else189 {190 DoWriteObject(sendToPipeline);191 }192#endif193 }194 195 /// <exception cref="System.Management.Automation.PipelineStoppedException">196 /// The pipeline has already been terminated, or was terminated197 /// during the execution of this method.198 /// The Cmdlet should generally just allow PipelineStoppedException199 /// to percolate up to the caller of ProcessRecord etc.200 /// </exception>201 /// <exception cref="System.InvalidOperationException">202 /// Not permitted at this time or from this thread203 /// </exception>204 private void DoWriteObject(object sendToPipeline)205 {206 ThrowIfWriteNotPermitted(true);207 _WriteObjectSkipAllowCheck(sendToPipeline);208 }209 210 /// <summary>211 /// Writes one or more objects to the output pipe.212 /// If the object is a collection and the enumerateCollection flag213 /// is true, the objects in the collection214 /// will be written individually.215 /// </summary>216 /// <param name="sendToPipeline">217 /// The object that needs to be written to the pipeline.218 /// </param>219 /// <param name="enumerateCollection">220 /// true if the collection should be enumerated221 /// </param>222 /// <exception cref="System.Management.Automation.PipelineStoppedException">223 /// The pipeline has already been terminated, or was terminated224 /// during the execution of this method.225 /// The Cmdlet should generally just allow PipelineStoppedException226 /// to percolate up to the caller of ProcessRecord etc.227 /// </exception>228 /// <exception cref="System.InvalidOperationException">229 /// Not permitted at this time or from this thread.230 /// WriteObject may only be called during a call to this Cmdlet's231 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,232 /// and only from that thread.233 /// </exception>234 /// <seealso cref="System.Management.Automation.ICommandRuntime.WriteObject(object)"/>235 /// <seealso cref="System.Management.Automation.ICommandRuntime.WriteError(ErrorRecord)"/>236 public void WriteObject(object sendToPipeline, bool enumerateCollection)237 {238 if (!enumerateCollection)239 {240 WriteObject(sendToPipeline);241 return;242 }243 244 // This check will be repeated in _WriteObjectsSkipAllowCheck,245 // but we want PipelineStoppedException to take precedence246 // over InvalidOperationException if the pipeline has been247 // closed.248 ThrowIfStopping();249 250#if CORECLR251 // SecurityContext is not supported in CoreCLR252 DoWriteEnumeratedObject(sendToPipeline);253#else254 if (UseSecurityContextRun)255 {256 if (PipelineProcessor == null || PipelineProcessor.SecurityContext == null)257 throw PSTraceSource.NewInvalidOperationException(PipelineStrings.WriteNotPermitted);258 ContextCallback delegateCallback =259 new ContextCallback(DoWriteObjects);260 SecurityContext.Run(261 PipelineProcessor.SecurityContext.CreateCopy(),262 delegateCallback,263 sendToPipeline);264 }265 else266 {267 DoWriteObjects(sendToPipeline);268 }269#endif270 }271 272 /// <summary>273 /// Writes an object enumerated from a collection to the output pipe.274 /// </summary>275 /// <param name="sendToPipeline">276 /// The enumerated object that needs to be written to the pipeline.277 /// </param>278 /// <exception cref="System.Management.Automation.PipelineStoppedException">279 /// The pipeline has already been terminated, or was terminated280 /// during the execution of this method.281 /// The Cmdlet should generally just allow PipelineStoppedException282 /// to percolate up to the caller of ProcessRecord etc.283 /// </exception>284 /// <exception cref="System.InvalidOperationException">285 /// Not permitted at this time or from this thread.286 /// </exception>287 private void DoWriteEnumeratedObject(object sendToPipeline)288 {289 // NOTICE-2004/06/08-JonN 959638290 ThrowIfWriteNotPermitted(true);291 _EnumerateAndWriteObjectSkipAllowCheck(sendToPipeline);292 }293 // Trust: public void WriteObject(object sendToPipeline, DataTrustCategory trustCategory); // enumerateCollection defaults to false294 // Trust: public void WriteObject(object sendToPipeline, bool enumerateCollection, DataTrustCategory trustCategory);295 296 // Variables needed to generate a unique SourceId for297 // WriteProgress(ProgressRecord).298 private static Int64 s_lastUsedSourceId /* = 0 */;299 private Int64 _sourceId /* = 0 */;300 301 /// <summary>302 /// Display progress information.303 /// </summary>304 /// <param name="progressRecord">Progress information.</param>305 /// <exception cref="System.Management.Automation.PipelineStoppedException">306 /// The pipeline has already been terminated, or was terminated307 /// during the execution of this method.308 /// The Cmdlet should generally just allow PipelineStoppedException309 /// to percolate up to the caller of ProcessRecord etc.310 /// </exception>311 /// <exception cref="System.InvalidOperationException">312 /// Not permitted at this time or from this thread.313 /// WriteProgress may only be called during a call to this Cmdlet's314 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,315 /// and only from that thread.316 /// </exception>317 /// <remarks>318 /// Use WriteProgress to display progress information about319 /// the activity of your Cmdlet, when the operation of your Cmdlet320 /// could potentially take a long time.321 ///322 /// By default, progress output will323 /// be displayed, although this can be configured with the324 /// ProgressPreference shell variable.325 /// </remarks>326 /// <seealso cref="System.Management.Automation.Cmdlet.WriteDebug(string)"/>327 /// <seealso cref="System.Management.Automation.Cmdlet.WriteWarning(string)"/>328 /// <seealso cref="System.Management.Automation.Cmdlet.WriteVerbose(string)"/>329 public void WriteProgress(ProgressRecord progressRecord)330 {331 this.WriteProgress(progressRecord, false);332 }333 334 internal void WriteProgress(ProgressRecord progressRecord, bool overrideInquire)335 {336 // NTRAID#Windows Out Of Band Releases-918023-2005/08/22-JonN337 ThrowIfStopping();338 339 //340 // WriteError/WriteObject have a check that prevents them to be called from outside341 // Begin/Process/End. This is done because the Pipeline needs to be ready before these342 // functions can be called.343 //344 // WriteDebug/Warning/Verbose/Process used to do the same check, even though it is not345 // strictly needed. If we ever implement pipelines for these objects we may need to346 // enforce the check again.347 //348 // See bug 583774 in the Windows 7 database for more details.349 //350 ThrowIfWriteNotPermitted(false);351 352 // Bug909439: We need a unique sourceId to send to353 // WriteProgress. The following logic ensures that354 // there is a unique id for each Cmdlet instance.355 356 if (_sourceId == 0)357 {358 _sourceId = Interlocked.Increment(ref s_lastUsedSourceId);359 }360 361 this.WriteProgress(_sourceId, progressRecord, overrideInquire);362 }363 364 /// <summary>365 /// Displays progress output if enabled.366 /// </summary>367 /// <param name="sourceId">368 /// Identifies which command is reporting progress369 /// </param>370 /// <param name="progressRecord">371 /// Progress status to be displayed372 /// </param>373 /// <exception cref="System.Management.Automation.PipelineStoppedException">374 /// The pipeline has already been terminated, or was terminated375 /// during the execution of this method.376 /// The Cmdlet should generally just allow PipelineStoppedException377 /// to percolate up to the caller of ProcessRecord etc.378 /// </exception>379 /// <remarks>380 /// If the pipeline is terminated due to ActionPreference.Stop381 /// or ActionPreference.Inquire, this method will throw382 /// <see cref="System.Management.Automation.PipelineStoppedException"/>,383 /// but the command failure will ultimately be384 /// <see cref="System.Management.Automation.ActionPreferenceStopException"/>,385 /// </remarks>386 public void WriteProgress(387 Int64 sourceId,388 ProgressRecord progressRecord)389 {390 WriteProgress(sourceId, progressRecord, false);391 }392 393 internal bool IsWriteProgressEnabled()394 => WriteHelper_ShouldWrite(ProgressPreference, lastProgressContinueStatus);395 396 internal void WriteProgress(397 Int64 sourceId,398 ProgressRecord progressRecord,399 bool overrideInquire)400 {401 if (progressRecord == null)402 {403 throw PSTraceSource.NewArgumentNullException(nameof(progressRecord));404 }405 406 if (Host == null || Host.UI == null)407 {408 Diagnostics.Assert(false, "No host in CommandBase.WriteProgress()");409 throw PSTraceSource.NewInvalidOperationException();410 }411 412 InternalHostUserInterface ui = Host.UI as InternalHostUserInterface;413 414 ActionPreference preference = ProgressPreference;415 if (overrideInquire && preference == ActionPreference.Inquire)416 {417 preference = ActionPreference.Continue;418 }419 420 if (WriteHelper_ShouldWrite(421 preference, lastProgressContinueStatus))422 {423 // Break into the debugger if requested424 if (preference == ActionPreference.Break)425 {426 CBhost?.Runspace?.Debugger?.Break(progressRecord);427 }428 429 ui.WriteProgress(sourceId, progressRecord);430 }431 432 lastProgressContinueStatus = WriteHelper(433 null,434 null,435 preference,436 lastProgressContinueStatus,437 "ProgressPreference",438 progressRecord.Activity);439 }440 441 /// <summary>442 /// Display debug information.443 /// </summary>444 /// <param name="text">Debug output.</param>445 /// <exception cref="System.Management.Automation.PipelineStoppedException">446 /// The pipeline has already been terminated, or was terminated447 /// during the execution of this method.448 /// The Cmdlet should generally just allow PipelineStoppedException449 /// to percolate up to the caller of ProcessRecord etc.450 /// </exception>451 /// <exception cref="System.InvalidOperationException">452 /// Not permitted at this time or from this thread.453 /// WriteDebug may only be called during a call to this Cmdlet's454 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,455 /// and only from that thread.456 /// </exception>457 /// <remarks>458 /// Use WriteDebug to display debug information on the inner workings459 /// of your Cmdlet. By default, debug output will460 /// not be displayed, although this can be configured with the461 /// DebugPreference shell variable or the -Debug command-line option.462 /// </remarks>463 /// <remarks>464 /// If the pipeline is terminated due to ActionPreference.Stop465 /// or ActionPreference.Inquire, this method will throw466 /// <see cref="System.Management.Automation.PipelineStoppedException"/>,467 /// but the command failure will ultimately be468 /// <see cref="System.Management.Automation.ActionPreferenceStopException"/>,469 /// </remarks>470 /// <seealso cref="System.Management.Automation.Cmdlet.WriteVerbose(string)"/>471 /// <seealso cref="System.Management.Automation.Cmdlet.WriteWarning(string)"/>472 /// <seealso cref="System.Management.Automation.Cmdlet.WriteProgress(ProgressRecord)"/>473 public void WriteDebug(string text)474 {475 WriteDebug(new DebugRecord(text));476 }477 478 internal bool IsWriteDebugEnabled()479 => WriteHelper_ShouldWrite(DebugPreference, lastDebugContinueStatus);480 481 /// <summary>482 /// Display debug information.483 /// </summary>484 internal void WriteDebug(DebugRecord record, bool overrideInquire = false)485 {486 ActionPreference preference = DebugPreference;487 if (overrideInquire && preference == ActionPreference.Inquire)488 preference = ActionPreference.Continue;489 490 if (WriteHelper_ShouldWrite(preference, lastDebugContinueStatus))491 {492 if (record.InvocationInfo == null)493 {494 record.SetInvocationInfo(MyInvocation);495 }496 497 // Break into the debugger if requested498 if (preference == ActionPreference.Break)499 {500 CBhost?.Runspace?.Debugger?.Break(record);501 }502 503 if (DebugOutputPipe != null)504 {505 if (CBhost != null && CBhost.InternalUI != null &&506 DebugOutputPipe.NullPipe)507 {508 // If redirecting to a null pipe, still write to509 // information buffers.510 CBhost.InternalUI.WriteDebugInfoBuffers(record);511 }512 513 // Set WriteStream so that the debug output is formatted correctly.514 PSObject debugWrap = PSObject.AsPSObject(record);515 debugWrap.WriteStream = WriteStreamType.Debug;516 517 DebugOutputPipe.Add(debugWrap);518 }519 else520 {521 //522 // If no pipe, write directly to host.523 //524 if (Host == null || Host.UI == null)525 {526 Diagnostics.Assert(false, "No host in CommandBase.WriteDebug()");527 throw PSTraceSource.NewInvalidOperationException();528 }529 530 CBhost.InternalUI.TranscribeResult(StringUtil.Format(InternalHostUserInterfaceStrings.DebugFormatString, record.Message));531 CBhost.InternalUI.WriteDebugRecord(record);532 }533 }534 535 lastDebugContinueStatus = WriteHelper(536 null,537 null,538 preference,539 lastDebugContinueStatus,540 "DebugPreference",541 record.Message);542 }543 544 /// <summary>545 /// Display verbose information.546 /// </summary>547 /// <param name="text">Verbose output.</param>548 /// <exception cref="System.Management.Automation.PipelineStoppedException">549 /// The pipeline has already been terminated, or was terminated550 /// during the execution of this method.551 /// The Cmdlet should generally just allow PipelineStoppedException552 /// to percolate up to the caller of ProcessRecord etc.553 /// </exception>554 /// <exception cref="System.InvalidOperationException">555 /// Not permitted at this time or from this thread.556 /// WriteVerbose may only be called during a call to this Cmdlet's557 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,558 /// and only from that thread.559 /// </exception>560 /// <remarks>561 /// Use WriteVerbose to display more detailed information about562 /// the activity of your Cmdlet. By default, verbose output will563 /// not be displayed, although this can be configured with the564 /// VerbosePreference shell variable565 /// or the -Verbose and -Debug command-line options.566 /// </remarks>567 /// <seealso cref="System.Management.Automation.Cmdlet.WriteDebug(string)"/>568 /// <seealso cref="System.Management.Automation.Cmdlet.WriteWarning(string)"/>569 /// <seealso cref="System.Management.Automation.Cmdlet.WriteProgress(ProgressRecord)"/>570 public void WriteVerbose(string text)571 {572 WriteVerbose(new VerboseRecord(text));573 }574 575 internal bool IsWriteVerboseEnabled()576 => WriteHelper_ShouldWrite(VerbosePreference, lastVerboseContinueStatus);577 578 /// <summary>579 /// Display verbose information.580 /// </summary>581 internal void WriteVerbose(VerboseRecord record, bool overrideInquire = false)582 {583 ActionPreference preference = VerbosePreference;584 if (overrideInquire && preference == ActionPreference.Inquire)585 preference = ActionPreference.Continue;586 587 if (WriteHelper_ShouldWrite(preference, lastVerboseContinueStatus))588 {589 if (record.InvocationInfo == null)590 {591 record.SetInvocationInfo(MyInvocation);592 }593 594 // Break into the debugger if requested595 if (preference == ActionPreference.Break)596 {597 CBhost?.Runspace?.Debugger?.Break(record);598 }599 600 if (VerboseOutputPipe != null)601 {602 if (CBhost != null && CBhost.InternalUI != null &&603 VerboseOutputPipe.NullPipe)604 {605 // If redirecting to a null pipe, still write to606 // information buffers.607 CBhost.InternalUI.WriteVerboseInfoBuffers(record);608 }609 610 // Add WriteStream so that the verbose output is formatted correctly.611 PSObject verboseWrap = PSObject.AsPSObject(record);612 verboseWrap.WriteStream = WriteStreamType.Verbose;613 614 VerboseOutputPipe.Add(verboseWrap);615 }616 else617 {618 //619 // If no pipe, write directly to host.620 //621 if (Host == null || Host.UI == null)622 {623 Diagnostics.Assert(false, "No host in CommandBase.WriteVerbose()");624 throw PSTraceSource.NewInvalidOperationException();625 }626 627 CBhost.InternalUI.TranscribeResult(StringUtil.Format(InternalHostUserInterfaceStrings.VerboseFormatString, record.Message));628 CBhost.InternalUI.WriteVerboseRecord(record);629 }630 }631 632 lastVerboseContinueStatus = WriteHelper(633 null,634 null,635 preference,636 lastVerboseContinueStatus,637 "VerbosePreference",638 record.Message);639 }640 641 /// <summary>642 /// Display warning information.643 /// </summary>644 /// <param name="text">Warning output.</param>645 /// <exception cref="System.Management.Automation.PipelineStoppedException">646 /// The pipeline has already been terminated, or was terminated647 /// during the execution of this method.648 /// The Cmdlet should generally just allow PipelineStoppedException649 /// to percolate up to the caller of ProcessRecord etc.650 /// </exception>651 /// <exception cref="System.InvalidOperationException">652 /// Not permitted at this time or from this thread.653 /// WriteWarning may only be called during a call to this Cmdlet's654 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,655 /// and only from that thread.656 /// </exception>657 /// <remarks>658 /// Use WriteWarning to display warnings about659 /// the activity of your Cmdlet. By default, warning output will660 /// be displayed, although this can be configured with the661 /// WarningPreference shell variable662 /// or the -Verbose and -Debug command-line options.663 /// </remarks>664 /// <seealso cref="System.Management.Automation.Cmdlet.WriteDebug(string)"/>665 /// <seealso cref="System.Management.Automation.Cmdlet.WriteVerbose(string)"/>666 /// <seealso cref="System.Management.Automation.Cmdlet.WriteProgress(ProgressRecord)"/>667 public void WriteWarning(string text)668 {669 WriteWarning(new WarningRecord(text));670 }671 672 internal bool IsWriteWarningEnabled()673 => WriteHelper_ShouldWrite(WarningPreference, lastWarningContinueStatus);674 675 /// <summary>676 /// Display warning information.677 /// </summary>678 internal void WriteWarning(WarningRecord record, bool overrideInquire = false)679 {680 ActionPreference preference = WarningPreference;681 if (overrideInquire && preference == ActionPreference.Inquire)682 preference = ActionPreference.Continue;683 684 if (WriteHelper_ShouldWrite(preference, lastWarningContinueStatus))685 {686 if (record.InvocationInfo == null)687 {688 record.SetInvocationInfo(MyInvocation);689 }690 691 // Break into the debugger if requested692 if (preference == ActionPreference.Break)693 {694 CBhost?.Runspace?.Debugger?.Break(record);695 }696 697 if (WarningOutputPipe != null)698 {699 if (CBhost != null && CBhost.InternalUI != null &&700 WarningOutputPipe.NullPipe)701 {702 // If redirecting to a null pipe, still write to703 // information buffers.704 CBhost.InternalUI.WriteWarningInfoBuffers(record);705 }706 707 // Add WriteStream so that the warning output is formatted correctly.708 PSObject warningWrap = PSObject.AsPSObject(record);709 warningWrap.WriteStream = WriteStreamType.Warning;710 711 WarningOutputPipe.AddWithoutAppendingOutVarList(warningWrap);712 }713 else714 {715 //716 // If no pipe, write directly to host.717 //718 if (Host == null || Host.UI == null)719 {720 Diagnostics.Assert(false, "No host in CommandBase.WriteWarning()");721 throw PSTraceSource.NewInvalidOperationException();722 }723 724 CBhost.InternalUI.TranscribeResult(StringUtil.Format(InternalHostUserInterfaceStrings.WarningFormatString, record.Message));725 CBhost.InternalUI.WriteWarningRecord(record);726 }727 }728 729 AppendWarningVarList(record);730 731 lastWarningContinueStatus = WriteHelper(732 null,733 null,734 preference,735 lastWarningContinueStatus,736 "WarningPreference",737 record.Message);738 }739 740 /// <summary>741 /// Display tagged object information.742 /// </summary>743 public void WriteInformation(InformationRecord informationRecord)744 {745 WriteInformation(informationRecord, false);746 }747 748 internal bool IsWriteInformationEnabled()749 => WriteHelper_ShouldWrite(InformationPreference, lastInformationContinueStatus);750 751 /// <summary>752 /// Display tagged object information.753 /// </summary>754 internal void WriteInformation(InformationRecord record, bool overrideInquire = false)755 {756 ActionPreference preference = InformationPreference;757 if (overrideInquire && preference == ActionPreference.Inquire)758 preference = ActionPreference.Continue;759 760 // Break into the debugger if requested761 if (preference == ActionPreference.Break)762 {763 CBhost?.Runspace?.Debugger?.Break(record);764 }765 766 if (preference != ActionPreference.Ignore)767 {768 if (InformationOutputPipe != null)769 {770 if (CBhost != null && CBhost.InternalUI != null &&771 InformationOutputPipe.NullPipe)772 {773 // If redirecting to a null pipe, still write to774 // information buffers.775 CBhost.InternalUI.WriteInformationInfoBuffers(record);776 }777 778 // Add WriteStream so that the information output is formatted correctly.779 PSObject informationWrap = PSObject.AsPSObject(record);780 informationWrap.WriteStream = WriteStreamType.Information;781 782 InformationOutputPipe.Add(informationWrap);783 }784 else785 {786 //787 // If no pipe, write directly to host.788 //789 if (Host == null || Host.UI == null)790 {791 throw PSTraceSource.NewInvalidOperationException("No host in CommandBase.WriteInformation()");792 }793 794 CBhost.InternalUI.WriteInformationRecord(record);795 796 if ((record.Tags.Contains("PSHOST") && (!record.Tags.Contains("FORWARDED")))797 || (preference == ActionPreference.Continue))798 {799 HostInformationMessage hostOutput = record.MessageData as HostInformationMessage;800 if (hostOutput != null)801 {802 string message = hostOutput.Message;803 ConsoleColor? foregroundColor = null;804 ConsoleColor? backgroundColor = null;805 bool noNewLine = false;806 807 if (hostOutput.ForegroundColor.HasValue)808 {809 foregroundColor = hostOutput.ForegroundColor.Value;810 }811 812 if (hostOutput.BackgroundColor.HasValue)813 {814 backgroundColor = hostOutput.BackgroundColor.Value;815 }816 817 if (hostOutput.NoNewLine.HasValue)818 {819 noNewLine = hostOutput.NoNewLine.Value;820 }821 822 if (foregroundColor.HasValue || backgroundColor.HasValue)823 {824 // It is possible for either one or the other to be empty if run from a825 // non-interactive host, but only one was specified in Write-Host.826 // So fill them with defaults if they are empty.827 if (!foregroundColor.HasValue)828 {829 foregroundColor = ConsoleColor.Gray;830 }831 832 if (!backgroundColor.HasValue)833 {834 backgroundColor = ConsoleColor.Black;835 }836 837 if (noNewLine)838 {839 CBhost.InternalUI.Write(foregroundColor.Value, backgroundColor.Value, message);840 }841 else842 {843 CBhost.InternalUI.WriteLine(foregroundColor.Value, backgroundColor.Value, message);844 }845 }846 else847 {848 if (noNewLine)849 {850 CBhost.InternalUI.Write(message);851 }852 else853 {854 CBhost.InternalUI.WriteLine(message);855 }856 }857 }858 else859 {860 CBhost.InternalUI.WriteLine(record.ToString());861 }862 }863 }864 865 // Both informational and PSHost-targeted messages are transcribed here.866 // The only difference between these two is that PSHost-targeted messages are transcribed867 // even if InformationAction is SilentlyContinue.868 if (record.Tags.Contains("PSHOST") || (preference != ActionPreference.SilentlyContinue))869 {870 CBhost.InternalUI.TranscribeResult(record.ToString());871 }872 }873 874 AppendInformationVarList(record);875 876 lastInformationContinueStatus = WriteHelper(877 null,878 null,879 preference,880 lastInformationContinueStatus,881 "InformationPreference",882 record.ToString());883 }884 885 /// <summary>886 /// Write text into pipeline execution log.887 /// </summary>888 /// <param name="text">Text to be written to log.</param>889 /// <remarks>890 /// Use WriteCommandDetail to write important information about cmdlet execution to891 /// pipeline execution log.892 ///893 /// If LogPipelineExecutionDetail is turned on, this information will be written894 /// to PowerShell log under log category "Pipeline execution detail"895 /// </remarks>896 /// <seealso cref="System.Management.Automation.ICommandRuntime.WriteDebug(string)"/>897 /// <seealso cref="System.Management.Automation.ICommandRuntime.WriteVerbose(string)"/>898 /// <seealso cref="System.Management.Automation.ICommandRuntime.WriteProgress(ProgressRecord)"/>899 public void WriteCommandDetail(string text)900 {901 this.PipelineProcessor.LogExecutionInfo(_thisCommand.MyInvocation, text);902 }903 904 internal bool LogPipelineExecutionDetail { get; } = false;905 906 private bool InitShouldLogPipelineExecutionDetail()907 {908 CmdletInfo cmdletInfo = _commandInfo as CmdletInfo;909 910 if (cmdletInfo != null)911 {912 if (string.Equals("Add-Type", cmdletInfo.Name, StringComparison.OrdinalIgnoreCase))913 {914 return true;915 }916 917 if (cmdletInfo.Module == null && cmdletInfo.PSSnapIn != null)918 {919 return cmdletInfo.PSSnapIn.LogPipelineExecutionDetails;920 }921 922 if (cmdletInfo.PSSnapIn == null && cmdletInfo.Module != null)923 {924 return cmdletInfo.Module.LogPipelineExecutionDetails;925 }926 927 return false;928 }929 930 // Logging should be enabled for functions from modules also931 FunctionInfo functionInfo = _commandInfo as FunctionInfo;932 if (functionInfo != null && functionInfo.Module != null)933 {934 return functionInfo.Module.LogPipelineExecutionDetails;935 }936 937 return false;938 }939 940 /// <summary>941 /// This allows all success output to be set to a variable, where the variable is reset for each item returned by942 /// the cmdlet. Semantically this is equivalent to : cmd | % { $pipelineVariable = $_; (...) }943 /// </summary>944 internal string PipelineVariable { get; set; }945 946 private PSVariable _pipelineVarReference;947 private bool _shouldRemovePipelineVariable;948 949 internal void SetupOutVariable()950 {951 if (string.IsNullOrEmpty(this.OutVariable))952 {953 return;954 }955 956 EnsureVariableParameterAllowed();957 958 // Handle the creation of OutVariable in the case of Out-Default specially,959 // as it needs to handle much of its OutVariable support itself.960 if (!OutVariable.StartsWith('+') &&961 string.Equals("Out-Default", _commandInfo.Name, StringComparison.OrdinalIgnoreCase))962 {963 _state ??= new SessionState(Context.EngineSessionState);964 965 IList oldValue = null;966 oldValue = PSObject.Base(_state.PSVariable.GetValue(this.OutVariable)) as IList;967 968 _outVarList = oldValue ?? new ArrayList();969 970 if (_thisCommand is not PSScriptCmdlet)971 {972 this.OutputPipe.AddVariableList(VariableStreamKind.Output, _outVarList);973 }974 975 _state.PSVariable.Set(this.OutVariable, _outVarList);976 }977 else978 {979 SetupVariable(VariableStreamKind.Output, this.OutVariable, ref _outVarList);980 }981 }982 983 internal void SetupPipelineVariable()984 {985 // This can't use the common SetupVariable implementation, as this needs to persist for an entire986 // pipeline.987 988 if (string.IsNullOrEmpty(PipelineVariable))989 {990 return;991 }992 993 EnsureVariableParameterAllowed();994 995 _state ??= new SessionState(Context.EngineSessionState);996 997 // Create the pipeline variable998 _pipelineVarReference = new PSVariable(PipelineVariable);999 object varToUse = _state.Internal.SetVariable(1000 _pipelineVarReference,1001 force: false,1002 CommandOrigin.Internal);1003 1004 if (ReferenceEquals(_pipelineVarReference, varToUse))1005 {1006 // The returned variable is the exact same instance, which means we set a new variable.1007 // In this case, we will try removing the pipeline variable in the end.1008 _shouldRemovePipelineVariable = true;1009 }1010 else1011 {1012 // A variable with the same name already exists in the same scope and it was returned.1013 // In this case, we update the reference and don't remove the variable in the end.1014 _pipelineVarReference = (PSVariable)varToUse;1015 }1016 1017 if (_thisCommand is not PSScriptCmdlet)1018 {1019 this.OutputPipe.SetPipelineVariable(_pipelineVarReference);1020 }1021 }1022 1023 internal void RemovePipelineVariable()1024 {1025 if (_shouldRemovePipelineVariable)1026 {1027 // Remove pipeline variable when a pipeline is being torn down.1028 _state.PSVariable.Remove(PipelineVariable);1029 }1030 }1031 1032 /// <summary>1033 /// Configures the number of objects to buffer before calling the downstream Cmdlet.1034 /// </summary>1035 /// <remarks>1036 /// This is a common parameter via class CommonParameters.1037 /// </remarks>1038 internal int OutBuffer1039 {1040 get { return OutputPipe.OutBufferCount; }1041 1042 set { OutputPipe.OutBufferCount = value; }1043 }1044 1045 #endregion Write1046 1047 #region Should1048 #region ShouldProcess1049 /// <summary>1050 /// Confirm the operation with the user. Cmdlets which make changes1051 /// (e.g. delete files, stop services etc.) should call ShouldProcess1052 /// to give the user the opportunity to confirm that the operation1053 /// should actually be performed.1054 /// </summary>1055 /// <param name="target">1056 /// Name of the target resource being acted upon. This will1057 /// potentially be displayed to the user.1058 /// </param>1059 /// <exception cref="System.Management.Automation.PipelineStoppedException">1060 /// The pipeline has already been terminated, or was terminated1061 /// during the execution of this method.1062 /// The Cmdlet should generally just allow PipelineStoppedException1063 /// to percolate up to the caller of ProcessRecord etc.1064 /// </exception>1065 /// <exception cref="System.InvalidOperationException">1066 /// Not permitted at this time or from this thread.1067 /// ShouldProcess may only be called during a call to this Cmdlet's1068 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,1069 /// and only from that thread.1070 /// </exception>1071 /// <returns>1072 /// If ShouldProcess returns true, the operation should be performed.1073 /// If ShouldProcess returns false, the operation should not be1074 /// performed, and the Cmdlet should move on to the next target resource.1075 /// </returns>1076 /// <remarks>1077 /// A Cmdlet should declare1078 /// [Cmdlet( SupportsShouldProcess = true )]1079 /// if-and-only-if it calls ShouldProcess before making changes.1080 ///1081 /// ShouldProcess may only be called during a call to this Cmdlet's1082 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,1083 /// and only from that thread.1084 ///1085 /// ShouldProcess will take into account command-line settings1086 /// and preference variables in determining what it should return1087 /// and whether it should prompt the user.1088 /// </remarks>1089 /// <remarks>1090 /// If the pipeline is terminated due to ActionPreference.Stop1091 /// or ActionPreference.Inquire,1092 /// <see cref="System.Management.Automation.Cmdlet.ShouldProcess(string)"/>1093 /// will throw1094 /// <see cref="System.Management.Automation.PipelineStoppedException"/>,1095 /// but the command failure will ultimately be1096 /// <see cref="System.Management.Automation.ActionPreferenceStopException"/>,1097 /// </remarks>1098 /// <example>1099 /// <code>1100 /// namespace Microsoft.Samples.Cmdlet1101 /// {1102 /// [Cmdlet(VerbsCommon.Remove,"myobjecttype1")]1103 /// public class RemoveMyObjectType1 : PSCmdlet1104 /// {1105 /// [Parameter( Mandatory = true )]1106 /// public string Filename1107 /// {1108 /// get { return filename; }1109 /// set { filename = value; }1110 /// }1111 /// private string filename;1112 ///1113 /// public override void ProcessRecord()1114 /// {1115 /// if (ShouldProcess(filename))1116 /// {1117 /// // delete the object1118 /// }1119 /// }1120 /// }1121 /// }1122 /// </code>1123 /// </example>1124 /// <seealso cref="System.Management.Automation.Cmdlet.ShouldProcess(string,string)"/>1125 /// <seealso cref="System.Management.Automation.Cmdlet.ShouldProcess(string,string,string)"/>1126 /// <seealso cref="System.Management.Automation.Cmdlet.ShouldProcess(string,string,string,out ShouldProcessReason)"/>1127 /// <seealso cref="System.Management.Automation.Cmdlet.ShouldContinue(string,string)"/>1128 /// <seealso cref="System.Management.Automation.Cmdlet.ShouldContinue(string,string,ref bool,ref bool)"/>1129 public bool ShouldProcess(string target)1130 {1131 string verboseDescription = StringUtil.Format(CommandBaseStrings.ShouldProcessMessage,1132 MyInvocation.MyCommand.Name,1133 target);1134 ShouldProcessReason shouldProcessReason;1135 return DoShouldProcess(verboseDescription, null, null, out shouldProcessReason);1136 }1137 1138 /// <summary>1139 /// Confirm the operation with the user. Cmdlets which make changes1140 /// (e.g. delete files, stop services etc.) should call ShouldProcess1141 /// to give the user the opportunity to confirm that the operation1142 /// should actually be performed.1143 ///1144 /// This variant allows the caller to specify text for both the1145 /// target resource and the action.1146 /// </summary>1147 /// <param name="target">1148 /// Name of the target resource being acted upon. This will1149 /// potentially be displayed to the user.1150 /// </param>1151 /// <param name="action">1152 /// Name of the action which is being performed. This will1153 /// potentially be displayed to the user. (default is Cmdlet name)1154 /// </param>1155 /// <exception cref="System.Management.Automation.PipelineStoppedException">1156 /// The pipeline has already been terminated, or was terminated1157 /// during the execution of this method.1158 /// The Cmdlet should generally just allow PipelineStoppedException1159 /// to percolate up to the caller of ProcessRecord etc.1160 /// </exception>1161 /// <exception cref="System.InvalidOperationException">1162 /// Not permitted at this time or from this thread.1163 /// ShouldProcess may only be called during a call to this Cmdlet's1164 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,1165 /// and only from that thread.1166 /// </exception>1167 /// <returns>1168 /// If ShouldProcess returns true, the operation should be performed.1169 /// If ShouldProcess returns false, the operation should not be1170 /// performed, and the Cmdlet should move on to the next target resource.1171 /// </returns>1172 /// <remarks>1173 /// A Cmdlet should declare1174 /// [Cmdlet( SupportsShouldProcess = true )]1175 /// if-and-only-if it calls ShouldProcess before making changes.1176 ///1177 /// ShouldProcess may only be called during a call to this Cmdlet's1178 /// implementation of ProcessRecord, BeginProcessing or EndProcessing,1179 /// and only from that thread.1180 ///1181 /// ShouldProcess will take into account command-line settings1182 /// and preference variables in determining what it should return1183 /// and whether it should prompt the user.1184 /// </remarks>1185 /// <remarks>1186 /// If the pipeline is terminated due to ActionPreference.Stop1187 /// or ActionPreference.Inquire, this method will throw1188 /// <see cref="System.Management.Automation.PipelineStoppedException"/>,1189 /// but the command failure will ultimately be1190 /// <see cref="System.Management.Automation.ActionPreferenceStopException"/>,1191 /// </remarks>1192 /// <example>1193 /// <code>1194 /// namespace Microsoft.Samples.Cmdlet1195 /// {1196 /// [Cmdlet(VerbsCommon.Remove,"myobjecttype2")]1197 /// public class RemoveMyObjectType2 : PSCmdlet1198 /// {1199 /// [Parameter( Mandatory = true )]1200 /// public string Filename