/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs
537 строк
17 KB
xtqqczze
Fix `SA1028`: Code should not contain trailing whitespace. Part 1. (#26203)
16 окт 2025, 10:58
Не верифицирован
16 окт 2025, 10:58
a10ff10
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Runspaces; using System.Threading; namespace Microsoft.PowerShell.Commands { /// <summary> /// A cmdlet that traces the specified categories and flags for the duration of the /// specified expression. /// </summary> [Cmdlet(VerbsDiagnostic.Trace, "Command", DefaultParameterSetName = "expressionSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097136")] public class TraceCommandCommand : TraceListenerCommandBase, IDisposable { #region Parameters /// <summary> /// This parameter specifies the current pipeline object. /// </summary> [Parameter(ValueFromPipeline = true)] public PSObject InputObject { get; set; } = AutomationNull.Value; /// <summary> /// The TraceSource parameter determines which TraceSource categories the /// operation will take place on. /// </summary> [Parameter(Position = 0, Mandatory = true)] public string[] Name { get { return base.NameInternal; } set { base.NameInternal = value; } } /// <summary> /// The flags to be set on the TraceSource. /// </summary> /// <value></value> [Parameter(Position = 2)] public PSTraceSourceOptions Option { get { return base.OptionsInternal; } set { base.OptionsInternal = value; } } /// <summary> /// The parameter for the expression that should be traced. /// </summary> /// <value></value> [Parameter(Position = 1, Mandatory = true, ParameterSetName = "expressionSet")] public ScriptBlock Expression { get; set; } /// <summary> /// The parameter for the expression that should be traced. /// </summary> /// <value></value> [Parameter(Position = 1, Mandatory = true, ParameterSetName = "commandSet")] public string Command { get; set; } /// <summary> /// When set, this parameter is the arguments to pass to the command specified by /// the -Command parameter. /// </summary> [Parameter(ParameterSetName = "commandSet", ValueFromRemainingArguments = true)] [Alias("Args")] public object[] ArgumentList { get; set; } /// <summary> /// The parameter which determines the options for output from the trace listeners. /// </summary> [Parameter] public TraceOptions ListenerOption { get { return base.ListenerOptionsInternal; } set { base.ListenerOptionsInternal = value; } } /// <summary> /// Adds the file trace listener using the specified file. /// </summary> /// <value></value> [Parameter] [Alias("PSPath", "Path")] public string FilePath { get { return base.FileListener; } set { base.FileListener = value; } } /// <summary> /// Force parameter to control read-only files. /// </summary> [Parameter] public SwitchParameter Force { get { return base.ForceWrite; } set { base.ForceWrite = value; } } /// <summary> /// If this parameter is specified the Debugger trace listener will be added. /// </summary> /// <value></value> [Parameter] public SwitchParameter Debugger { get { return base.DebuggerListener; } set { base.DebuggerListener = value; } } /// <summary> /// If this parameter is specified the Msh Host trace listener will be added. /// </summary> /// <value></value> [Parameter] public SwitchParameter PSHost { get { return base.PSHostListener; } set { base.PSHostListener = value; } } #endregion Parameters #region Cmdlet code private Collection<PSTraceSource> _matchingSources; /// <summary> /// Gets the PSTraceSource instances that match the names specified. /// </summary> protected override void BeginProcessing() { Collection<PSTraceSource> preconfiguredSources = null; _matchingSources = ConfigureTraceSource(base.NameInternal, false, out preconfiguredSources); TurnOnTracing(_matchingSources, false); TurnOnTracing(preconfiguredSources, true); // Now that tracing has been configured, move all the sources into a // single collection foreach (PSTraceSource preconfiguredSource in preconfiguredSources) { _matchingSources.Add(preconfiguredSource); } if (ParameterSetName == "commandSet") { // Create the CommandProcessor and add it to a pipeline CommandProcessorBase commandProcessor = this.Context.CommandDiscovery.LookupCommandProcessor(Command, CommandOrigin.Runspace, false); // Add the parameters that were specified ParameterBinderController.AddArgumentsToCommandProcessor(commandProcessor, ArgumentList); _pipeline = new PipelineProcessor(); _pipeline.Add(commandProcessor); // Hook up the success and error pipelines to this cmdlet's WriteObject and // WriteError methods _pipeline.ExternalErrorOutput = new TracePipelineWriter(this, true, _matchingSources); _pipeline.ExternalSuccessOutput = new TracePipelineWriter(this, false, _matchingSources); } ResetTracing(_matchingSources); } /// <summary> /// Executes the expression. /// Note, this was taken from apply-expression. /// </summary> protected override void ProcessRecord() { TurnOnTracing(_matchingSources, false); object result = null; switch (ParameterSetName) { case "expressionSet": result = RunExpression(); break; case "commandSet": result = StepCommand(); break; } ResetTracing(_matchingSources); if (result == null) { return; } if (!LanguagePrimitives.IsNull(result)) { WriteObject(result, true); } } /// <summary> /// Finishes running the command if specified and then sets the /// tracing options and listeners back to their original values. /// </summary> protected override void EndProcessing() { if (_pipeline != null) { TurnOnTracing(_matchingSources, false); Array results = _pipeline.SynchronousExecuteEnumerate(AutomationNull.Value); ResetTracing(_matchingSources); WriteObject(results, true); } this.Dispose(); } /// <summary> /// Ensures that the sub-pipeline we created gets stopped as well. /// </summary> protected override void StopProcessing() => _pipeline?.Stop(); #endregion Cmdlet code private object RunExpression() { return Expression.DoInvokeReturnAsIs( useLocalScope: false, errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, dollarUnder: InputObject, input: new object[] { InputObject }, scriptThis: AutomationNull.Value, args: Array.Empty<object>()); } private object StepCommand() { if (InputObject != AutomationNull.Value) { _pipeline.Step(InputObject); } return null; } private PipelineProcessor _pipeline; #region IDisposable /// <summary> /// Resets the TraceSource flags back to their original value and restores /// the original TraceListeners. /// </summary> public void Dispose() { if (!_disposed) { _disposed = true; // Reset the flags for the trace switch back to the original value ResetTracing(_matchingSources); ClearStoredState(); _matchingSources = null; if (_pipeline != null) { _pipeline.Dispose(); _pipeline = null; } // If there are any file streams, close those as well. if (this.FileStreams != null) { foreach (FileStream fileStream in this.FileStreams) { fileStream.Flush(); fileStream.Dispose(); } } GC.SuppressFinalize(this); } } private bool _disposed; #endregion IDisposable } /// <summary> /// This class acts a pipe redirector for the sub-pipeline created by the Trace-Command /// cmdlet. It gets attached to the sub-pipelines success or error pipeline and redirects /// all objects written to these pipelines to trace-command pipeline. /// </summary> internal sealed class TracePipelineWriter : PipelineWriter { internal TracePipelineWriter( TraceListenerCommandBase cmdlet, bool writeError, Collection<PSTraceSource> matchingSources) { ArgumentNullException.ThrowIfNull(cmdlet); ArgumentNullException.ThrowIfNull(matchingSources); _cmdlet = cmdlet; _writeError = writeError; _matchingSources = matchingSources; } /// <summary> /// Get the wait handle signaled when buffer space is available in the underlying stream. /// </summary> public override WaitHandle WaitHandle { get { return null; } } /// <summary> /// Check if the stream is open for further writes. /// </summary> /// <value>true if the underlying stream is open, otherwise; false.</value> /// <remarks> /// Attempting to write to the underlying stream if IsOpen is false throws /// an <see cref="ObjectDisposedException"/>. /// </remarks> public override bool IsOpen { get { return _isOpen; } } /// <summary> /// Returns the number of objects in the underlying stream. /// </summary> public override int Count { get { return 0; } } /// <summary> /// Get the capacity of the stream. /// </summary> /// <value> /// The capacity of the stream. /// </value> /// <remarks> /// The capacity is the number of objects that stream may contain at one time. Once this /// limit is reached, attempts to write into the stream block until buffer space /// becomes available. /// </remarks> public override int MaxCapacity { get { return int.MaxValue; } } /// <summary> /// Close the stream. /// </summary> /// <remarks> /// Causes subsequent calls to IsOpen to return false and calls to /// a write operation to throw an ObjectDisposedException. /// All calls to Close() after the first call are silently ignored. /// </remarks> /// <exception cref="ObjectDisposedException"> /// The stream is already disposed. /// </exception> public override void Close() { if (_isOpen) { Flush(); _isOpen = false; } } /// <summary> /// Flush the data from the stream. Closed streams may be flushed, /// but disposed streams may not. /// </summary> /// <exception cref="ObjectDisposedException"> /// The underlying stream is disposed. /// </exception> public override void Flush() { } /// <summary> /// Write a single object into the underlying stream. /// </summary> /// <param name="obj">The object to add to the stream.</param> /// <returns> /// One, if the write was successful, otherwise; /// zero if the stream was closed before the object could be written, /// or if the object was AutomationNull.Value. /// </returns> /// <exception cref="ObjectDisposedException"> /// The underlying stream is closed. /// </exception> public override int Write(object obj) { _cmdlet.ResetTracing(_matchingSources); if (_writeError) { ErrorRecord errorRecord = ConvertToErrorRecord(obj); if (errorRecord != null) { _cmdlet.WriteError(errorRecord); } } else { _cmdlet.WriteObject(obj); } _cmdlet.TurnOnTracing(_matchingSources, false); return 1; } /// <summary> /// Write objects to the underlying stream. /// </summary> /// <param name="obj">Object or enumeration to read from.</param> /// <param name="enumerateCollection"> /// If enumerateCollection is true, and <paramref name="obj"/> /// is an enumeration according to LanguagePrimitives.GetEnumerable, /// the objects in the enumeration will be unrolled and /// written separately. Otherwise, <paramref name="obj"/> /// will be written as a single object. /// </param> /// <returns>The number of objects written.</returns> /// <exception cref="ObjectDisposedException"> /// The underlying stream is closed. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="obj"/> contains AutomationNull.Value /// </exception> public override int Write(object obj, bool enumerateCollection) { _cmdlet.ResetTracing(_matchingSources); int numWritten = 0; if (_writeError) { if (enumerateCollection) { foreach (object o in LanguagePrimitives.GetEnumerable(obj)) { ErrorRecord errorRecord = ConvertToErrorRecord(o); if (errorRecord != null) { numWritten++; _cmdlet.WriteError(errorRecord); } } } else { ErrorRecord errorRecord = ConvertToErrorRecord(obj); if (errorRecord != null) { numWritten++; _cmdlet.WriteError(errorRecord); } } } else { numWritten++; _cmdlet.WriteObject(obj, enumerateCollection); } _cmdlet.TurnOnTracing(_matchingSources, false); return numWritten; } private static ErrorRecord ConvertToErrorRecord(object obj) { ErrorRecord result = null; if (obj is PSObject mshobj) { object baseObject = mshobj.BaseObject; if (baseObject is not PSCustomObject) { obj = baseObject; } } if (obj is ErrorRecord errorRecordResult) { result = errorRecordResult; } return result; } private readonly TraceListenerCommandBase _cmdlet; private readonly bool _writeError; private bool _isOpen = true; private readonly Collection<PSTraceSource> _matchingSources = new(); } }