diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs index 58d9d762ab..f72aa5b653 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs @@ -2733,7 +2733,22 @@ private string GenerateCertificateThumbprintParameter() $clientSideParameters = @{} - $parametersToLeaveRemote = 'ErrorAction', 'WarningAction', 'InformationAction' + $parametersToLeaveRemote = if (!$EnabledExperimentalFeatures.Contains('PSNewCommonParameters')) { + @( + 'ErrorAction' + 'InformationAction' + 'WarningAction' + ) + } else { + @( + 'DebugAction' + 'ErrorAction' + 'InformationAction' + 'ProgressAction' + 'VerboseAction' + 'WarningAction' + ) + } Modify-PSImplicitRemotingParameters $clientSideParameters $PSBoundParameters 'AsJob' if ($proxyForCmdlet) diff --git a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs index 3436ddb606..43a80d66d0 100644 --- a/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs +++ b/src/Microsoft.PowerShell.Security/security/CertificateProvider.cs @@ -4,28 +4,28 @@ #if !UNIX using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; using System.Management.Automation; using System.Management.Automation.Host; using System.Management.Automation.Internal; -using Runspaces = System.Management.Automation.Runspaces; -using Dbg = System.Management.Automation; -using Security = System.Management.Automation.Security; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections; -using System.Runtime.InteropServices; using System.Management.Automation.Provider; +using System.Runtime.InteropServices; +using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; -using System.Globalization; -using System.IO; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Xml; using System.Xml.XPath; -using System.Security; using DWORD = System.UInt32; +using Runspaces = System.Management.Automation.Runspaces; +using Security = System.Management.Automation.Security; +using Sma = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -1250,7 +1250,7 @@ protected override void GetItem(string path) // The filter is non null. If the certificate // satisfies the filter, output it. Otherwise, don't. X509Certificate2 cert = item as X509Certificate2; - Dbg.Diagnostics.Assert(cert != null, "item should be a certificate"); + Sma.Diagnostics.Assert(cert != null, "item should be a certificate"); if (MatchesFilter(cert, filter)) { @@ -1358,17 +1358,14 @@ private void AttemptToImportPkiModule() try { - System.Management.Automation.PowerShell ps = null; - ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace) - .AddCommand(importModuleCommand) - .AddParameter("Name", moduleName) - .AddParameter("Scope", StringLiterals.Global) - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("InformationAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false); - ps.Invoke(); + using (var ps = Sma.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + ps.AddCommand(importModuleCommand) + .AddParameter("Name", moduleName) + .AddParameter("Scope", StringLiterals.Global) + .IgnoreMessageStreamParameters() + .Invoke(); + } } catch (Exception) { @@ -2865,7 +2862,7 @@ private static object GetCachedItem(string path) if (s_pathCache.ContainsKey(path)) { item = s_pathCache[path]; - Dbg.Diagnostics.Assert(item != null, "GetCachedItem"); + Sma.Diagnostics.Assert(item != null, "GetCachedItem"); } } diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index dfd610e475..dd02cb0c11 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -164,6 +164,10 @@ internal static IEnumerable GetFormatData() "System.Management.Automation.InformationRecord", ViewsOf_System_Management_Automation_InformationRecord()); + yield return new ExtendedTypeDefinition( + "System.Management.Automation.ProgressRecord", + ViewsOf_System_Management_Automation_ProgressRecord()); + yield return new ExtendedTypeDefinition( "System.Management.Automation.CommandParameterSetInfo", ViewsOf_System_Management_Automation_CommandParameterSetInfo()); @@ -1337,6 +1341,29 @@ private static IEnumerable ViewsOf_System_Management_Autom .EndControl()); } + private static IEnumerable ViewsOf_System_Management_Automation_ProgressRecord() + { + // This generates string output that uses the following format: + // PROGRESS: [% - ][: ][ ()] + // Activity is always shown. + // Items in square brackets are conditional, so: + // - PercentComplete only shows up if it is set, and uses a format like this: " 83% -" + // - StatusDescription is only shown if it is not "Processing" + // - CurrentOperation is only shown if it is not null or empty + // The end result is a string that might look something like any of the following examples: + // PROGRESS: Initializing... + // PROGRESS: 57% - Creating user: 57 of 100 (Initializing home directory) + // PROGRESS: 100% - Task finished + yield return new FormatViewDefinition( + "ProgressRecord", + CustomControl + .Create(outOfBand: true) + .StartEntry() + .AddScriptBlockExpressionBinding($@"'{FormatStrings.ProgressFormatString}' -f ""$(if ($_.PercentComplete -ge 0) {{'{{0,3}}% - ' -f $_.PercentComplete}})$($_.Activity)$(if ($_.StatusDescription -ne '{FormatStrings.Processing}') {{"": $($_.StatusDescription)""}})$(if ($_.CurrentOperation) {{"" ($($_.CurrentOperation))""}})""") + .EndEntry() + .EndControl()); + } + private static IEnumerable ViewsOf_System_Management_Automation_CommandParameterSetInfo() { var FmtParameterAttributes = CustomControl.Create() diff --git a/src/System.Management.Automation/engine/CommandDiscovery.cs b/src/System.Management.Automation/engine/CommandDiscovery.cs index ef9cb56c63..e64fc2ffff 100644 --- a/src/System.Management.Automation/engine/CommandDiscovery.cs +++ b/src/System.Management.Automation/engine/CommandDiscovery.cs @@ -924,20 +924,17 @@ internal static Collection AutoloadSpecifiedModule(string moduleNa discoveryTracer.WriteLine("Attempting to load module: {0}", moduleName); - PowerShell ps = null; try { - ps = PowerShell.Create(RunspaceMode.CurrentRunspace) - .AddCommand(importModuleCommand) - .AddParameter("Name", moduleName) - .AddParameter("Scope", StringLiterals.Global) - .AddParameter("PassThru") - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("InformationAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false); - matchingModules = (Collection)ps.Invoke(); + using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + ps.AddCommand(importModuleCommand) + .AddParameter("Name", moduleName) + .AddParameter("Scope", StringLiterals.Global) + .AddParameter("PassThru") + .IgnoreMessageStreamParameters(); + matchingModules = ps.Invoke(); + } } catch (Exception e) { diff --git a/src/System.Management.Automation/engine/CommonCommandParameters.cs b/src/System.Management.Automation/engine/CommonCommandParameters.cs index 9cc0b2ad89..ceea950a68 100644 --- a/src/System.Management.Automation/engine/CommonCommandParameters.cs +++ b/src/System.Management.Automation/engine/CommonCommandParameters.cs @@ -102,6 +102,40 @@ public ActionPreference WarningAction set { _commandRuntime.WarningPreference = value; } } + /// + /// Gets or sets the value of the VerboseAction parameter for the cmdlet. + /// + /// + /// This parameter tells the command what to do when a verbose record + /// occurs. + /// + [Experimental("PSNewCommonParameters", ExperimentAction.Show)] + [Parameter] + [Alias("va")] + public ActionPreference VerboseAction + { + get { return _commandRuntime.VerbosePreference; } + + set { _commandRuntime.VerbosePreference = value; } + } + + /// + /// Gets or sets the value of the DebugAction parameter for the cmdlet. + /// + /// + /// This parameter tells the command what to do when a debug record + /// occurs. + /// + [Experimental("PSNewCommonParameters", ExperimentAction.Show)] + [Parameter] + [Alias("da")] + public ActionPreference DebugAction + { + get { return _commandRuntime.DebugPreference; } + + set { _commandRuntime.DebugPreference = value; } + } + /// /// Gets or sets the value of the InformationAction parameter for the cmdlet. /// @@ -123,6 +157,22 @@ public ActionPreference InformationAction set { _commandRuntime.InformationPreference = value; } } + /// + /// Gets or sets the value of the ProgressAction parameter for the cmdlet. + /// + /// + /// This parameter tells the command what to do when an progress record occurs. + /// + [Experimental("PSNewCommonParameters", ExperimentAction.Show)] + [Parameter] + [Alias("pra")] + public ActionPreference ProgressAction + { + get { return _commandRuntime.ProgressPreference; } + + set { _commandRuntime.ProgressPreference = value; } + } + /// /// Gets or sets the value of the ErrorVariable parameter for the cmdlet. /// @@ -161,6 +211,42 @@ public string WarningVariable set { _commandRuntime.WarningVariable = value; } } + /// + /// Gets or sets the value of the VerboseVariable parameter for the cmdlet. + /// + /// + /// This parameter tells the command which variable to populate with verbose messages. + /// Use +varname to append to the variable rather than clearing it. + /// + [Experimental("PSNewCommonParameters", ExperimentAction.Show)] + [Parameter] + [Alias("vv")] + [ValidateVariableName] + public string VerboseVariable + { + get { return _commandRuntime.VerboseVariable; } + + set { _commandRuntime.VerboseVariable = value; } + } + + /// + /// Gets or sets the value of the DebugVariable parameter for the cmdlet. + /// + /// + /// This parameter tells the command which variable to populate with debug messages. + /// Use +varname to append to the variable rather than clearing it. + /// + [Experimental("PSNewCommonParameters", ExperimentAction.Show)] + [Parameter] + [Alias("dv")] + [ValidateVariableName] + public string DebugVariable + { + get { return _commandRuntime.DebugVariable; } + + set { _commandRuntime.DebugVariable = value; } + } + /// /// Gets or sets the value of the InformationVariable parameter for the cmdlet. /// @@ -178,6 +264,24 @@ public string InformationVariable set { _commandRuntime.InformationVariable = value; } } + /// + /// Gets or sets the value of the ProgressVariable parameter for the cmdlet. + /// + /// + /// This parameter tells the command which variable to populate with progress messages. + /// Use +varname to append to the variable rather than clearing it. + /// + [Experimental("PSNewCommonParameters", ExperimentAction.Show)] + [Parameter] + [Alias("prv")] + [ValidateVariableName] + public string ProgressVariable + { + get { return _commandRuntime.ProgressVariable; } + + set { _commandRuntime.ProgressVariable = value; } + } + /// /// Gets or sets the OutVariable parameter for the cmdlet. /// diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 98f0b3deb5..50a328a43d 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -1154,6 +1154,20 @@ internal ActionPreference InformationActionPreferenceVariable } } + internal ActionPreference ProgressActionPreferenceVariable + { + get => GetEnumPreference( + SpecialVariables.ProgressPreferenceVarPath, + InitialSessionState.DefaultProgressPreference, + out _); + + set => EngineSessionState.SetVariable( + SpecialVariables.ProgressPreferenceVarPath, + LanguagePrimitives.ConvertTo(value, typeof(ActionPreference), CultureInfo.InvariantCulture), + true, + CommandOrigin.Internal); + } + internal object WhatIfPreferenceVariable { get diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index f6993a03a0..7bec94b82e 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -123,6 +123,9 @@ static ExperimentalFeature() new ExperimentalFeature( name: "PSNativePSPathResolution", description: "Convert PSPath to filesystem path, if possible, for native commands"), + new ExperimentalFeature( + name: "PSNewCommonParameters", + description: "New -DebugAction, -DebugVariable, -ProgressAction, -ProgressVariable, -VerboseAction, and -VerboseVariable common parameters"), }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs index dd188b3ff9..c9dde7e967 100644 --- a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs +++ b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs @@ -593,16 +593,14 @@ private static void CallGetModuleDashList(ExecutionContext context, string modul try { - PowerShell.Create(RunspaceMode.CurrentRunspace) - .AddCommand(getModuleCommand) - .AddParameter("List", true) - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("InformationAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false) - .AddParameter("Name", modulePath) - .Invoke(); + using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + ps.AddCommand(getModuleCommand) + .AddParameter("List", true) + .AddParameter("Name", modulePath) + .IgnoreMessageStreamParameters() + .Invoke(); + } } catch (Exception e) { diff --git a/src/System.Management.Automation/engine/Modules/RemoteDiscoveryHelper.cs b/src/System.Management.Automation/engine/Modules/RemoteDiscoveryHelper.cs index 015b2bb72d..01547bfdaa 100644 --- a/src/System.Management.Automation/engine/Modules/RemoteDiscoveryHelper.cs +++ b/src/System.Management.Automation/engine/Modules/RemoteDiscoveryHelper.cs @@ -476,6 +476,12 @@ private static void HandleErrorFromPipeline(Cmdlet cmdlet, ErrorRecord errorReco CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "InformationAction"); CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "Verbose"); CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "Debug"); + if (ExperimentalFeature.EnabledExperimentalFeatureNames.Contains("PSNewCommonParameters")) + { + CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "VerboseAction"); + CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "DebugAction"); + CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "ProgressAction"); + } var invocationSettings = new PSInvocationSettings { Host = cmdlet.Host }; diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index 4d07cd3d3a..b9641c5177 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -425,6 +425,8 @@ internal void WriteProgress(ProgressRecord progressRecord, bool overrideInquire) ui.WriteProgress(sourceId, progressRecord); } + AppendProgressVarList(progressRecord); + lastProgressContinueStatus = WriteHelper( null, null, @@ -525,6 +527,8 @@ internal void WriteDebug(DebugRecord record, bool overrideInquire = false) } } + AppendDebugVarList(record); + lastDebugContinueStatus = WriteHelper( null, null, @@ -619,6 +623,8 @@ internal void WriteVerbose(VerboseRecord record, bool overrideInquire = false) } } + AppendVerboseVarList(record); + lastVerboseContinueStatus = WriteHelper( null, null, @@ -2508,6 +2514,62 @@ internal void AppendWarningVarList(object obj) #endregion Warning PSVariable + #region Verbose PSVariable + private IList _verboseVarList; + + /// + /// Gets or sets a variable that will be used store verbose records. + /// Use +varname to append to the variable rather than clearing it. + /// + /// + /// This is a common parameter via class CommonParameters. + /// + internal string VerboseVariable { get; set; } + + internal void SetupVerboseVariable() + { + SetupVariable(VariableStreamKind.Verbose, this.VerboseVariable, ref _verboseVarList); + } + + /// + /// Append a verbose to VerboseVariable if specified. + /// + /// The verbose message. + internal void AppendVerboseVarList(object obj) + { + this.OutputPipe.AppendVariableList(VariableStreamKind.Verbose, obj); + } + + #endregion Verbose PSVariable + + #region Debug PSVariable + private IList _debugVarList; + + /// + /// Gets or sets a variable that will be used store debug records. + /// Use +varname to append to the variable rather than clearing it. + /// + /// + /// This is a common parameter via class CommonParameters. + /// + internal string DebugVariable { get; set; } + + internal void SetupDebugVariable() + { + SetupVariable(VariableStreamKind.Debug, this.DebugVariable, ref _debugVarList); + } + + /// + /// Append a debug to DebugVariable if specified. + /// + /// The debug message. + internal void AppendDebugVarList(object obj) + { + this.OutputPipe.AppendVariableList(VariableStreamKind.Debug, obj); + } + + #endregion Debug PSVariable + #region Information PSVariable private IList _informationVarList; @@ -2593,6 +2655,34 @@ internal void AppendInformationVarList(object obj) #endregion Information PSVariable + #region Progress PSVariable + private IList _progressVarList; + + /// + /// Gets or sets a variable that will be used store progress records. + /// Use +varname to append to the variable rather than clearing it. + /// + /// + /// This is a common parameter via class CommonParameters. + /// + internal string ProgressVariable { get; set; } + + internal void SetupProgressVariable() + { + SetupVariable(VariableStreamKind.Progress, this.ProgressVariable, ref _progressVarList); + } + + /// + /// Append a progress to ProgressVariable if specified. + /// + /// The progress message. + internal void AppendProgressVarList(object obj) + { + this.OutputPipe.AppendVariableList(VariableStreamKind.Progress, obj); + } + + #endregion Progress PSVariable + #region Write internal bool UseSecurityContextRun = true; @@ -2927,8 +3017,9 @@ internal void _WriteErrorSkipAllowCheck(ErrorRecord errorRecord, ActionPreferenc // workings of the command and when what information will get output. // See "User Feedback Mechanisms - Note.doc" for details. - private bool _isConfirmPreferenceCached = false; + private bool _readCommandConfirmPreference = false; private ConfirmImpact _confirmPreference = InitialSessionState.DefaultConfirmPreference; + /// /// Preference setting controlling behavior of ShouldProcess() /// @@ -2937,39 +3028,49 @@ internal void _WriteErrorSkipAllowCheck(ErrorRecord errorRecord, ActionPreferenc /// Verbose, Debug, Confirm, and WhatIf parameters and the /// $ConfirmPreference shell variable. /// - /// We only read $ConfirmPreference once, then cache the value. + /// We only read $ConfirmPreference once, then reuse the value. /// internal ConfirmImpact ConfirmPreference { get { - // WhatIf not relevant, it never gets this far in that case + // Setting CommonParameters.Confirm has the highest priority. + // WhatIf is not relevant. It never gets this far in that case. if (Confirm) + { return ConfirmImpact.Low; + } + if (Debug) { if (IsConfirmFlagSet) // -Debug -Confirm:$false + { return ConfirmImpact.None; + } + return ConfirmImpact.Low; } if (IsConfirmFlagSet) // -Confirm:$false + { return ConfirmImpact.None; + } - if (!_isConfirmPreferenceCached) + // If the common parameter was not used, read the inherited confirm + // preference once per command, regardless of how many times + // PSCmdlet.ShouldProcess is invoked in that command. + if (!_readCommandConfirmPreference) { - bool defaultUsed = false; - _confirmPreference = Context.GetEnumPreference(SpecialVariables.ConfirmPreferenceVarPath, _confirmPreference, out defaultUsed); - _isConfirmPreferenceCached = true; + _confirmPreference = Context.GetEnumPreference(SpecialVariables.ConfirmPreferenceVarPath, _confirmPreference, out _); + _readCommandConfirmPreference = true; } return _confirmPreference; } } - private bool _isDebugPreferenceSet = false; private ActionPreference _debugPreference = InitialSessionState.DefaultDebugPreference; - private bool _isDebugPreferenceCached = false; + private bool _readCommandDebugPreference = false; /// /// Preference setting. /// @@ -2980,21 +3081,29 @@ internal ActionPreference DebugPreference { get { - if (_isDebugPreferenceSet) + // Setting CommonParameters.DebugAction has the highest priority. + if (IsDebugActionSet) { return _debugPreference; } - if (IsDebugFlagSet) + // Setting CommonParameters.Debug has the next highest priority. + if (Debug) { - return Debug ? ActionPreference.Continue : ActionPreference.SilentlyContinue; + return ActionPreference.Continue; } - if (!_isDebugPreferenceCached) + if (IsDebugFlagSet) { - bool defaultUsed = false; + return ActionPreference.SilentlyContinue; + } - _debugPreference = Context.GetEnumPreference(SpecialVariables.DebugPreferenceVarPath, _debugPreference, out defaultUsed); + // If the common parameter was not used, read the inherited debug + // preference once per command, regardless of how many times + // PSCmdlet.WriteDebug is invoked in that command. + if (!_readCommandDebugPreference) + { + _debugPreference = Context.GetEnumPreference(SpecialVariables.DebugPreferenceVarPath, _debugPreference, out _); // If the host couldn't prompt for the debug action anyways, change it to 'Continue'. // This lets hosts still see debug output without having to implement the prompting logic. @@ -3003,7 +3112,7 @@ internal ActionPreference DebugPreference _debugPreference = ActionPreference.Continue; } - _isDebugPreferenceCached = true; + _readCommandDebugPreference = true; } return _debugPreference; @@ -3017,12 +3126,15 @@ internal ActionPreference DebugPreference } _debugPreference = value; - _isDebugPreferenceSet = true; + IsDebugActionSet = true; } } - private bool _isVerbosePreferenceCached = false; + internal bool IsDebugActionSet { get; private set; } = false; + private ActionPreference _verbosePreference = InitialSessionState.DefaultVerbosePreference; + private bool _readCommandVerbosePreference = false; + /// /// Preference setting. /// @@ -3033,45 +3145,71 @@ internal ActionPreference VerbosePreference { get { + // Setting CommonParameters.VerboseAction has the highest priority. + if (IsVerboseActionSet) + { + return _verbosePreference; + } + + // Setting CommonParameters.Verbose has the next highest priority. + if (Verbose) + { + return ActionPreference.Continue; + } + if (IsVerboseFlagSet) { - if (Verbose) - return ActionPreference.Continue; - else - return ActionPreference.SilentlyContinue; + return ActionPreference.SilentlyContinue; } + // Legacy functionality support: + // If a user invoked Write-Verbose without -Verbose and with -Debug, they would + // originally have been prompted using Inquire (in v6.x and earlier), but with + // https://github.com/PowerShell/PowerShell/pull/8195, -Debug was changed to + // apply ActionPreference.Continue behavior instead, so the legacy -Debug + // behavior on Write-Verbose is simply showing the message using Continue as well. if (Debug) { - // If the host couldn't prompt for the debug action anyways, use 'Continue'. - // This lets hosts still see debug output without having to implement the prompting logic. - if (CBhost.ExternalHost.UI == null) - { - return ActionPreference.Continue; - } - else + return ActionPreference.Continue; + } + + // If the common parameter was not used, read the inherited verbose + // preference once per command, regardless of how many times + // PSCmdlet.WriteVerbose is invoked in that command. + if (!_readCommandVerbosePreference) + { + _verbosePreference = Context.GetEnumPreference(SpecialVariables.VerbosePreferenceVarPath, _verbosePreference, out _); + + // If the host couldn't prompt for the verbose action anyways, change it to 'Continue'. + // This lets hosts still see warning output without having to implement the prompting logic. + if ((CBhost.ExternalHost.UI == null) && (_verbosePreference == ActionPreference.Inquire)) { - return ActionPreference.Inquire; + _verbosePreference = ActionPreference.Continue; } + + _readCommandVerbosePreference = true; } - if (!_isVerbosePreferenceCached) + return _verbosePreference; + } + + set + { + if (value == ActionPreference.Suspend) { - bool defaultUsed = false; - _verbosePreference = Context.GetEnumPreference( - SpecialVariables.VerbosePreferenceVarPath, - _verbosePreference, - out defaultUsed); + throw PSTraceSource.NewNotSupportedException(ErrorPackage.ActionPreferenceReservedForFutureUseError, value); } - return _verbosePreference; + _verbosePreference = value; + IsVerboseActionSet = true; } } - internal bool IsWarningActionSet { get; private set; } = false; + internal bool IsVerboseActionSet { get; private set; } - private bool _isWarningPreferenceCached = false; + private bool _readCommandWarningPreference = false; private ActionPreference _warningPreference = InitialSessionState.DefaultWarningPreference; + /// /// Preference setting. /// @@ -3084,18 +3222,41 @@ internal ActionPreference WarningPreference { // Setting CommonParameters.WarningAction has highest priority if (IsWarningActionSet) + { return _warningPreference; + } - if (Debug) - return ActionPreference.Inquire; - if (Verbose) + // Legacy functionality support: + // If a user invokes Write-Warning without -WarningAction and with -Verbose, + // show the warning message. + // If a user invoked Write-Warning without -WarningAction and with -Debug, + // they would originally have been prompted using Inquire (in v6.x and earlier), + // but with https://github.com/PowerShell/PowerShell/pull/8195, -Debug was + // changed to apply ActionPreference.Continue behavior instead, so the legacy + // -Debug behavior on Write-Warning is simply showing the warning using Continue + // as well. + if (Debug || Verbose) + { return ActionPreference.Continue; + } + // Debug:$false and Verbose:$false ignored - if (!_isWarningPreferenceCached) + // If the common parameter was not used, read the inherited warning + // preference once per command, regardless of how many times + // PSCmdlet.WriteWarning is invoked in that command. + if (!_readCommandWarningPreference) { - bool defaultUsed = false; - _warningPreference = Context.GetEnumPreference(SpecialVariables.WarningPreferenceVarPath, _warningPreference, out defaultUsed); + _warningPreference = Context.GetEnumPreference(SpecialVariables.WarningPreferenceVarPath, _warningPreference, out _); + + // If the host couldn't prompt for the warning action anyways, change it to 'Continue'. + // This lets hosts still see warning output without having to implement the prompting logic. + if ((CBhost.ExternalHost.UI == null) && (_warningPreference == ActionPreference.Inquire)) + { + _warningPreference = ActionPreference.Continue; + } + + _readCommandWarningPreference = true; } return _warningPreference; @@ -3113,6 +3274,8 @@ internal ActionPreference WarningPreference } } + internal bool IsWarningActionSet { get; private set; } + // This is used so that people can tell whether the verbose switch // was specified. This is useful in the Cmdlet-calling-Cmdlet case // where you'd like the underlying Cmdlet to have the same switches. @@ -3211,7 +3374,7 @@ internal bool Debug internal bool IsDebugFlagSet { get; private set; } = false; private bool _whatIfFlag = InitialSessionState.DefaultWhatIfPreference; - private bool _isWhatIfPreferenceCached /* = false */; + private bool _readCommandWhatIfPreference /* = false */; /// /// WhatIf indicates that the command should not /// perform any changes to persistent state outside Monad. @@ -3223,11 +3386,19 @@ internal SwitchParameter WhatIf { get { - if (!IsWhatIfFlagSet && !_isWhatIfPreferenceCached) + // Setting CommonParameters.WhatIf has highest priority + if (IsWhatIfFlagSet) { - bool defaultUsed = false; - _whatIfFlag = Context.GetBooleanPreference(SpecialVariables.WhatIfPreferenceVarPath, _whatIfFlag, out defaultUsed); - _isWhatIfPreferenceCached = true; + return _whatIfFlag; + } + + // If the common parameter was not used, read the inherited WhatIf + // preference once per command, regardless of how many times + // PSCmdlet.ShouldProcess is invoked in that command. + if (!_readCommandWhatIfPreference) + { + _whatIfFlag = Context.GetBooleanPreference(SpecialVariables.WhatIfPreferenceVarPath, _whatIfFlag, out _); + _readCommandWhatIfPreference = true; } return _whatIfFlag; @@ -3243,7 +3414,7 @@ internal SwitchParameter WhatIf internal bool IsWhatIfFlagSet { get; private set; } private ActionPreference _errorAction = InitialSessionState.DefaultErrorActionPreference; - private bool _isErrorActionPreferenceCached = false; + private bool _readCommandErrorActionPreference = false; /// /// ErrorAction tells the command what to do when an error occurs. /// @@ -3261,11 +3432,14 @@ internal ActionPreference ErrorAction if (IsErrorActionSet) return _errorAction; - if (!_isErrorActionPreferenceCached) + // If the common parameter was not used, read the inherited error + // action preference once per command, regardless of how many times + // PSCmdlet.WriteError is invoked in that command. + if (!_readCommandErrorActionPreference) { bool defaultUsed = false; _errorAction = Context.GetEnumPreference(SpecialVariables.ErrorActionPreferenceVarPath, _errorAction, out defaultUsed); - _isErrorActionPreferenceCached = true; + _readCommandErrorActionPreference = true; } return _errorAction; @@ -3285,6 +3459,9 @@ internal ActionPreference ErrorAction internal bool IsErrorActionSet { get; private set; } = false; + private ActionPreference _progressPreference = InitialSessionState.DefaultProgressPreference; + private bool _readCommandProgressPreference = false; + /// /// Preference setting for displaying ProgressRecords when WriteProgress is called. /// @@ -3293,14 +3470,19 @@ internal ActionPreference ProgressPreference { get { - if (_isProgressPreferenceSet) + if (IsProgressActionSet) + { return _progressPreference; + } - if (!_isProgressPreferenceCached) + // If the common parameter was not used, read the inherited progress + // preference once per command, regardless of how many times + // PSCmdlet.WriteProgress is invoked in that command. + if (!_readCommandProgressPreference) { bool defaultUsed = false; _progressPreference = Context.GetEnumPreference(SpecialVariables.ProgressPreferenceVarPath, _progressPreference, out defaultUsed); - _isProgressPreferenceCached = true; + _readCommandProgressPreference = true; } return _progressPreference; @@ -3314,13 +3496,14 @@ internal ActionPreference ProgressPreference } _progressPreference = value; - _isProgressPreferenceSet = true; + IsProgressActionSet = true; } } - private ActionPreference _progressPreference = InitialSessionState.DefaultProgressPreference; - private bool _isProgressPreferenceSet = false; - private bool _isProgressPreferenceCached = false; + internal bool IsProgressActionSet { get; private set; } + + private ActionPreference _informationPreference = InitialSessionState.DefaultInformationPreference; + private bool _readCommandInformationPreference; /// /// Preference setting for displaying InformationRecords when WriteInformation is called. @@ -3333,11 +3516,14 @@ internal ActionPreference InformationPreference if (IsInformationActionSet) return _informationPreference; - if (!_isInformationPreferenceCached) + // If the common parameter was not used, read the inherited + // information preference once per command, regardless of how + // many times PSCmdlet.WriteInformation is invoked in that command. + if (!_readCommandInformationPreference) { bool defaultUsed = false; _informationPreference = Context.GetEnumPreference(SpecialVariables.InformationPreferenceVarPath, _informationPreference, out defaultUsed); - _isInformationPreferenceCached = true; + _readCommandInformationPreference = true; } return _informationPreference; @@ -3355,12 +3541,8 @@ internal ActionPreference InformationPreference } } - private ActionPreference _informationPreference = InitialSessionState.DefaultInformationPreference; - internal bool IsInformationActionSet { get; private set; } = false; - private bool _isInformationPreferenceCached = false; - internal PagingParameters PagingParameters { get; set; } #endregion Preference @@ -3734,11 +3916,26 @@ internal void SetVariableListsInPipe() this.OutputPipe.AddVariableList(VariableStreamKind.Warning, _warningVarList); } + if (_verboseVarList != null) + { + this.OutputPipe.AddVariableList(VariableStreamKind.Verbose, _verboseVarList); + } + + if (_debugVarList != null) + { + this.OutputPipe.AddVariableList(VariableStreamKind.Debug, _debugVarList); + } + if (_informationVarList != null) { this.OutputPipe.AddVariableList(VariableStreamKind.Information, _informationVarList); } + if (_progressVarList != null) + { + this.OutputPipe.AddVariableList(VariableStreamKind.Progress, _progressVarList); + } + if (this.PipelineVariable != null) { this.OutputPipe.SetPipelineVariable(_pipelineVarReference); @@ -3764,11 +3961,26 @@ internal void RemoveVariableListsInPipe() this.OutputPipe.RemoveVariableList(VariableStreamKind.Warning, _warningVarList); } + if (_verboseVarList != null) + { + this.OutputPipe.RemoveVariableList(VariableStreamKind.Verbose, _verboseVarList); + } + + if (_debugVarList != null) + { + this.OutputPipe.RemoveVariableList(VariableStreamKind.Debug, _debugVarList); + } + if (_informationVarList != null) { this.OutputPipe.RemoveVariableList(VariableStreamKind.Information, _informationVarList); } + if (_progressVarList != null) + { + this.OutputPipe.RemoveVariableList(VariableStreamKind.Progress, _progressVarList); + } + if (this.PipelineVariable != null) { this.OutputPipe.RemovePipelineVariable(); diff --git a/src/System.Management.Automation/engine/PSClassSearcher.cs b/src/System.Management.Automation/engine/PSClassSearcher.cs index 613035820d..9ab7e975cb 100644 --- a/src/System.Management.Automation/engine/PSClassSearcher.cs +++ b/src/System.Management.Automation/engine/PSClassSearcher.cs @@ -266,16 +266,15 @@ private Collection GetPSModuleInfo(string modulePath) string moduleName = Path.GetFileNameWithoutExtension(modulePath); - var modules = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace) - .AddCommand(getModuleCommand) - .AddParameter("List", true) - .AddParameter("Name", moduleName) - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("InformationAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false) - .Invoke(); + Collection modules = null; + using (var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + modules = ps.AddCommand(getModuleCommand) + .AddParameter("List", true) + .AddParameter("Name", moduleName) + .IgnoreMessageStreamParameters() + .Invoke(); + } lock (_lockObject) { diff --git a/src/System.Management.Automation/engine/Pipe.cs b/src/System.Management.Automation/engine/Pipe.cs index ac9350487a..cc0d6e1e22 100644 --- a/src/System.Management.Automation/engine/Pipe.cs +++ b/src/System.Management.Automation/engine/Pipe.cs @@ -8,14 +8,17 @@ namespace System.Management.Automation.Internal { /// - /// Corresponds to -OutputVariable, -ErrorVariable, -WarningVariable, and -InformationVariable. + /// Corresponds to -OutputVariable, -ErrorVariable, -WarningVariable, -VerboseVariable, -DebugVariable, -InformationVariable, and -ProgressVariable. /// internal enum VariableStreamKind { Output, Error, Warning, - Information + Verbose, + Debug, + Information, + Progress, }; /// @@ -171,11 +174,26 @@ internal bool IsRedirected /// private List _warningVariableList; + /// + /// If non-null, verbose records written to the pipe are also added to this list. + /// + private List _verboseVariableList; + + /// + /// If non-null, debug records written to the pipe are also added to this list. + /// + private List _debugVariableList; + /// /// If non-null, information objects written to the pipe are also added to this list. /// private List _informationVariableList; + /// + /// If non-null, progress records written to the pipe are also added to this list. + /// + private List _progressVariableList; + /// /// If non-null, the current object being written to the pipe is stored in /// this variable. @@ -206,9 +224,18 @@ internal void AppendVariableList(VariableStreamKind kind, object obj) case VariableStreamKind.Output: AddToVarList(_outVariableList, obj); break; + case VariableStreamKind.Verbose: + AddToVarList(_verboseVariableList, obj); + break; + case VariableStreamKind.Debug: + AddToVarList(_debugVariableList, obj); + break; case VariableStreamKind.Information: AddToVarList(_informationVariableList, obj); break; + case VariableStreamKind.Progress: + AddToVarList(_progressVariableList, obj); + break; } } @@ -240,6 +267,22 @@ internal void AddVariableList(VariableStreamKind kind, IList list) _outVariableList.Add(list); break; + case VariableStreamKind.Verbose: + if (_verboseVariableList == null) + { + _verboseVariableList = new List(); + } + + _verboseVariableList.Add(list); + break; + case VariableStreamKind.Debug: + if (_debugVariableList == null) + { + _debugVariableList = new List(); + } + + _debugVariableList.Add(list); + break; case VariableStreamKind.Information: if (_informationVariableList == null) { @@ -248,6 +291,14 @@ internal void AddVariableList(VariableStreamKind kind, IList list) _informationVariableList.Add(list); break; + case VariableStreamKind.Progress: + if (_progressVariableList == null) + { + _progressVariableList = new List(); + } + + _progressVariableList.Add(list); + break; } } @@ -269,9 +320,18 @@ internal void RemoveVariableList(VariableStreamKind kind, IList list) case VariableStreamKind.Output: _outVariableList.Remove(list); break; + case VariableStreamKind.Verbose: + _verboseVariableList.Remove(list); + break; + case VariableStreamKind.Debug: + _debugVariableList.Remove(list); + break; case VariableStreamKind.Information: _informationVariableList.Remove(list); break; + case VariableStreamKind.Progress: + _progressVariableList.Remove(list); + break; } } @@ -286,8 +346,8 @@ internal void RemovePipelineVariable() /// /// When a temporary pipe is used in the middle of execution, then we need to pass along - /// the error and warning variable list to hold the errors and warnings get written out - /// while the temporary pipe is being used. + /// the data stream variable lists to hold messages written out while the temporary pipe + /// is being used. /// /// We don't need to pass along the out variable list because we don't care about the output /// generated in the middle of execution. @@ -296,7 +356,10 @@ internal void SetVariableListForTemporaryPipe(Pipe tempPipe) { CopyVariableToTempPipe(VariableStreamKind.Error, _errorVariableList, tempPipe); CopyVariableToTempPipe(VariableStreamKind.Warning, _warningVariableList, tempPipe); + CopyVariableToTempPipe(VariableStreamKind.Verbose, _verboseVariableList, tempPipe); + CopyVariableToTempPipe(VariableStreamKind.Debug, _debugVariableList, tempPipe); CopyVariableToTempPipe(VariableStreamKind.Information, _informationVariableList, tempPipe); + CopyVariableToTempPipe(VariableStreamKind.Progress, _progressVariableList, tempPipe); } private void CopyVariableToTempPipe(VariableStreamKind streamKind, List variableList, Pipe tempPipe) diff --git a/src/System.Management.Automation/engine/ReflectionParameterBinder.cs b/src/System.Management.Automation/engine/ReflectionParameterBinder.cs index 3e5dcab250..c96b401df8 100644 --- a/src/System.Management.Automation/engine/ReflectionParameterBinder.cs +++ b/src/System.Management.Automation/engine/ReflectionParameterBinder.cs @@ -189,29 +189,56 @@ static ReflectionParameterBinder() s_setterMethods.TryAdd(Tuple.Create(typeof(GetModuleCommand), "ListAvailable"), (o, v) => ((GetModuleCommand)o).ListAvailable = (SwitchParameter)v); s_setterMethods.TryAdd(Tuple.Create(typeof(GetModuleCommand), "FullyQualifiedName"), (o, v) => ((GetModuleCommand)o).FullyQualifiedName = (ModuleSpecification[])v); + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "Debug"), (o, v) => ((CommonParameters)o).Debug = (SwitchParameter)v); + if (ExperimentalFeature.EnabledExperimentalFeatureNames.Contains("PSNewCommonParameters")) + { + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "DebugAction"), + (o, v) => { + v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference)); + ((CommonParameters)o).DebugAction = (ActionPreference)v; + }); + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "DebugVariable"), (o, v) => ((CommonParameters)o).DebugVariable = (string)v); + } s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ErrorAction"), (o, v) => { v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference)); ((CommonParameters)o).ErrorAction = (ActionPreference)v; }); - s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "WarningAction"), - (o, v) => { - v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference)); - ((CommonParameters)o).WarningAction = (ActionPreference)v; - }); + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ErrorVariable"), (o, v) => ((CommonParameters)o).ErrorVariable = (string)v); s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "InformationAction"), (o, v) => { v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference)); ((CommonParameters)o).InformationAction = (ActionPreference)v; }); - s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "Verbose"), (o, v) => ((CommonParameters)o).Verbose = (SwitchParameter)v); - s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "Debug"), (o, v) => ((CommonParameters)o).Debug = (SwitchParameter)v); - s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ErrorVariable"), (o, v) => ((CommonParameters)o).ErrorVariable = (string)v); - s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "WarningVariable"), (o, v) => ((CommonParameters)o).WarningVariable = (string)v); s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "InformationVariable"), (o, v) => ((CommonParameters)o).InformationVariable = (string)v); - s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "OutVariable"), (o, v) => ((CommonParameters)o).OutVariable = (string)v); s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "OutBuffer"), (o, v) => ((CommonParameters)o).OutBuffer = (int)v); + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "OutVariable"), (o, v) => ((CommonParameters)o).OutVariable = (string)v); s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "PipelineVariable"), (o, v) => ((CommonParameters)o).PipelineVariable = (string)v); + if (ExperimentalFeature.EnabledExperimentalFeatureNames.Contains("PSNewCommonParameters")) + { + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ProgressAction"), + (o, v) => { + v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference)); + ((CommonParameters)o).ProgressAction = (ActionPreference)v; + }); + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "ProgressVariable"), (o, v) => ((CommonParameters)o).ProgressVariable = (string)v); + } + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "Verbose"), (o, v) => ((CommonParameters)o).Verbose = (SwitchParameter)v); + if (ExperimentalFeature.EnabledExperimentalFeatureNames.Contains("PSNewCommonParameters")) + { + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "VerboseAction"), + (o, v) => { + v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference)); + ((CommonParameters)o).VerboseAction = (ActionPreference)v; + }); + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "VerboseVariable"), (o, v) => ((CommonParameters)o).VerboseVariable = (string)v); + } + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "WarningAction"), + (o, v) => { + v ??= LanguagePrimitives.ThrowInvalidCastException(null, typeof(ActionPreference)); + ((CommonParameters)o).WarningAction = (ActionPreference)v; + }); + s_setterMethods.TryAdd(Tuple.Create(typeof(CommonParameters), "WarningVariable"), (o, v) => ((CommonParameters)o).WarningVariable = (string)v); } private static readonly ConcurrentDictionary, Func> s_getterMethods diff --git a/src/System.Management.Automation/engine/SpecialVariables.cs b/src/System.Management.Automation/engine/SpecialVariables.cs index aa241b6c94..f84641b62b 100644 --- a/src/System.Management.Automation/engine/SpecialVariables.cs +++ b/src/System.Management.Automation/engine/SpecialVariables.cs @@ -320,6 +320,7 @@ internal static class SpecialVariables SpecialVariables.WarningPreference, SpecialVariables.InformationPreference, SpecialVariables.ConfirmPreference, + SpecialVariables.ProgressPreference, }; internal static readonly Type[] PreferenceVariableTypes = { @@ -330,6 +331,7 @@ internal static class SpecialVariables /* WarningPreference */ typeof(ActionPreference), /* InformationPreference */ typeof(ActionPreference), /* ConfirmPreference */ typeof(ConfirmImpact), + /* ProgressPreference */ typeof(ActionPreference), }; // The following variables are created in every session w/ AllScope. We avoid creating local slots when we @@ -397,5 +399,6 @@ internal enum PreferenceVariable Warning = 13, Information = 14, Confirm = 15, + Progress = 16, } } diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index b3d62c4b06..e36db21f98 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -1045,22 +1045,17 @@ internal static void EnsureModuleLoaded(string module, ExecutionContext context) context.AutoLoadingModuleInProgress.Add(module); - PowerShell ps = null; - try { - ps = PowerShell.Create(RunspaceMode.CurrentRunspace) - .AddCommand(importModuleCommand) - .AddParameter("Name", module) - .AddParameter("Scope", StringLiterals.Global) - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("InformationAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false) - .AddParameter("PassThru"); - - ps.Invoke(); + using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + ps.AddCommand(importModuleCommand) + .AddParameter("Name", module) + .AddParameter("Scope", StringLiterals.Global) + .AddParameter("PassThru") + .IgnoreMessageStreamParameters() + .Invoke(); + } } catch (Exception) { @@ -1069,10 +1064,6 @@ internal static void EnsureModuleLoaded(string module, ExecutionContext context) finally { context.AutoLoadingModuleInProgress.Remove(module); - if (ps != null) - { - ps.Dispose(); - } } } } @@ -1098,28 +1089,26 @@ internal static List GetModules(string module, ExecutionContext co null, null, context); var getModuleCommand = new System.Management.Automation.Runspaces.Command(commandInfo); - PowerShell ps = null; try { - ps = PowerShell.Create(RunspaceMode.CurrentRunspace) - .AddCommand(getModuleCommand) - .AddParameter("Name", module) - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false) - .AddParameter("ListAvailable"); - - Collection gmoOutPut = ps.Invoke(); - if (gmoOutPut != null) + using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) { - if (result == null) - { - result = gmoOutPut.ToList(); - } - else + ps.AddCommand(getModuleCommand) + .AddParameter("Name", module) + .AddParameter("ListAvailable") + .IgnoreMessageStreamParameters(); + + Collection gmoOutPut = ps.Invoke(); + if (gmoOutPut != null) { - result.AddRange(gmoOutPut); + if (result == null) + { + result = gmoOutPut.ToList(); + } + else + { + result.AddRange(gmoOutPut); + } } } } @@ -1127,13 +1116,6 @@ internal static List GetModules(string module, ExecutionContext co { // Call-out to user code, catch-all OK } - finally - { - if (ps != null) - { - ps.Dispose(); - } - } return result; } @@ -1157,30 +1139,27 @@ internal static List GetModules(ModuleSpecification fullyQualified null, null, context); var getModuleCommand = new Runspaces.Command(commandInfo); - PowerShell ps = null; try { - ps = PowerShell.Create(RunspaceMode.CurrentRunspace) - .AddCommand(getModuleCommand) - .AddParameter("FullyQualifiedName", fullyQualifiedName) - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("InformationAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false) - .AddParameter("ListAvailable"); - - Collection gmoOutput = ps.Invoke(); - if (gmoOutput != null) + using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) { - if (result == null) - { - result = gmoOutput.ToList(); - } - else + ps.AddCommand(getModuleCommand) + .AddParameter("FullyQualifiedName", fullyQualifiedName) + .AddParameter("ListAvailable") + .IgnoreMessageStreamParameters(); + + Collection gmoOutput = ps.Invoke(); + if (gmoOutput != null) { - // append to result - result.AddRange(gmoOutput); + if (result == null) + { + result = gmoOutput.ToList(); + } + else + { + // append to result + result.AddRange(gmoOutput); + } } } } @@ -1188,13 +1167,6 @@ internal static List GetModules(ModuleSpecification fullyQualified { // Call-out to user code, catch-all OK } - finally - { - if (ps != null) - { - ps.Dispose(); - } - } return result; } diff --git a/src/System.Management.Automation/engine/cmdlet.cs b/src/System.Management.Automation/engine/cmdlet.cs index b676337531..dc338da481 100644 --- a/src/System.Management.Automation/engine/cmdlet.cs +++ b/src/System.Management.Automation/engine/cmdlet.cs @@ -49,10 +49,27 @@ public static HashSet CommonParameters private static Lazy> s_commonParameters = new Lazy>( () => { - return new HashSet(StringComparer.OrdinalIgnoreCase) { - "Verbose", "Debug", "ErrorAction", "WarningAction", "InformationAction", - "ErrorVariable", "WarningVariable", "OutVariable", - "OutBuffer", "PipelineVariable", "InformationVariable" }; + if (!ExperimentalFeature.EnabledExperimentalFeatureNames.Contains("PSNewCommonParameters")) + { + return new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Verbose", "Debug", "ErrorAction", "WarningAction", "InformationAction", + "ErrorVariable", "WarningVariable", "OutVariable", + "OutBuffer", "PipelineVariable", "InformationVariable" + }; + } + + // If you add new common parameters, please manually sort them into this list + // so that they appear in sorted order to anyone who wants to view the common + // parameters in PowerShell by invoking the following command: + // [System.Management.Automation.Cmdlet]::CommonParameters + return new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Debug", "DebugAction", "DebugVariable", "ErrorAction", "ErrorVariable", + "InformationAction", "InformationVariable", "OutBuffer", "OutVariable", + "PipelineVariable", "ProgressAction", "ProgressVariable", + "Verbose", "VerboseAction", "VerboseVariable", "WarningAction", "WarningVariable" + }; } ); diff --git a/src/System.Management.Automation/engine/hostifaces/PowerShell.cs b/src/System.Management.Automation/engine/hostifaces/PowerShell.cs index ba559a5e1f..57f056c8cb 100644 --- a/src/System.Management.Automation/engine/hostifaces/PowerShell.cs +++ b/src/System.Management.Automation/engine/hostifaces/PowerShell.cs @@ -5305,6 +5305,40 @@ private RemoteRunspacePoolInternal GetRemoteRunspacePoolInternal() return (runspacePool != null) ? (runspacePool.RemoteRunspacePoolInternal) : null; } + internal PowerShell IgnoreMessageStreamParameters() + { + lock (_syncObject) + { + if (_psCommand.Commands.Count == 0) + { + throw PSTraceSource.NewInvalidOperationException(PowerShellStrings.ParameterRequiresCommand); + } + + AssertChangesAreAccepted(); + if (!ExperimentalFeature.EnabledExperimentalFeatureNames.Contains("PSNewCommonParameters")) + { + _psCommand + .AddParameter("ErrorAction", ActionPreference.Ignore) + .AddParameter("WarningAction", ActionPreference.Ignore) + .AddParameter("InformationAction", ActionPreference.Ignore) + .AddParameter("Verbose", false) + .AddParameter("Debug", false); + } + else + { + _psCommand + .AddParameter("DebugAction", ActionPreference.Ignore) + .AddParameter("ErrorAction", ActionPreference.Ignore) + .AddParameter("InformationAction", ActionPreference.Ignore) + .AddParameter("ProgressAction", ActionPreference.Ignore) + .AddParameter("VerboseAction", ActionPreference.Ignore) + .AddParameter("WarningAction", ActionPreference.Ignore); + } + + return this; + } + } + #endregion #region Worker diff --git a/src/System.Management.Automation/engine/pipeline.cs b/src/System.Management.Automation/engine/pipeline.cs index 8b1779de88..bb72b6a42c 100644 --- a/src/System.Management.Automation/engine/pipeline.cs +++ b/src/System.Management.Automation/engine/pipeline.cs @@ -1113,8 +1113,11 @@ private void SetupParameterVariables() commandProcessor.CommandRuntime.SetupOutVariable(); commandProcessor.CommandRuntime.SetupErrorVariable(); commandProcessor.CommandRuntime.SetupWarningVariable(); + commandProcessor.CommandRuntime.SetupVerboseVariable(); + commandProcessor.CommandRuntime.SetupDebugVariable(); commandProcessor.CommandRuntime.SetupPipelineVariable(); commandProcessor.CommandRuntime.SetupInformationVariable(); + commandProcessor.CommandRuntime.SetupProgressVariable(); } } diff --git a/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs b/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs index 819588225b..8aaaccab4b 100644 --- a/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs +++ b/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs @@ -2455,11 +2455,26 @@ private void SetPreferenceVariables() _localsTuple.SetPreferenceVariable(PreferenceVariable.Warning, _commandRuntime.WarningPreference); } + if (_commandRuntime.IsVerboseActionSet) + { + _localsTuple.SetPreferenceVariable(PreferenceVariable.Verbose, _commandRuntime.VerbosePreference); + } + + if (_commandRuntime.IsDebugActionSet) + { + _localsTuple.SetPreferenceVariable(PreferenceVariable.Debug, _commandRuntime.DebugPreference); + } + if (_commandRuntime.IsInformationActionSet) { _localsTuple.SetPreferenceVariable(PreferenceVariable.Information, _commandRuntime.InformationPreference); } + if (_commandRuntime.IsProgressActionSet) + { + _localsTuple.SetPreferenceVariable(PreferenceVariable.Progress, _commandRuntime.ProgressPreference); + } + if (_commandRuntime.IsWhatIfFlagSet) { _localsTuple.SetPreferenceVariable(PreferenceVariable.WhatIf, _commandRuntime.WhatIf); diff --git a/src/System.Management.Automation/resources/FormatStrings.resx b/src/System.Management.Automation/resources/FormatStrings.resx new file mode 100644 index 0000000000..a0d2ef95f7 --- /dev/null +++ b/src/System.Management.Automation/resources/FormatStrings.resx @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + DEBUG: {0} + + + VERBOSE: {0} + + + WARNING: {0} + + + PROGRESS: {0} + + + Processing + This must be the same value as Microsoft.PowerShell.Commands.Utility.WriteProgressResourceStrings.Processing + + diff --git a/src/System.Management.Automation/resources/HelpDisplayStrings.resx b/src/System.Management.Automation/resources/HelpDisplayStrings.resx index 8866eea8d6..b65c4eca01 100644 --- a/src/System.Management.Automation/resources/HelpDisplayStrings.resx +++ b/src/System.Management.Automation/resources/HelpDisplayStrings.resx @@ -192,6 +192,8 @@ This cmdlet supports the common parameters: Verbose, Debug, ErrorAction, ErrorVariable, WarningAction, WarningVariable, + VerboseAction, VerboseVariable, DebugAction, DebugVariable, + InformationAction, InformationVariable, ProgressAction, ProgressVariable, OutBuffer, PipelineVariable, and OutVariable. For more information, see about_CommonParameters (https://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs b/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs index d6d845c728..b63cb83891 100644 --- a/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs +++ b/src/System.Management.Automation/utils/PowerShellExecutionHelper.cs @@ -250,14 +250,7 @@ internal static PowerShell AddCommandWithPreferenceSetting(this PowerShell power powershell.AddCommand(command); } - powershell - .AddParameter("ErrorAction", ActionPreference.Ignore) - .AddParameter("WarningAction", ActionPreference.Ignore) - .AddParameter("InformationAction", ActionPreference.Ignore) - .AddParameter("Verbose", false) - .AddParameter("Debug", false); - - return powershell; + return powershell.IgnoreMessageStreamParameters(); } } } diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index b8b1cc607a..3af2ef7dbf 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -1391,7 +1391,7 @@ Describe "WSMan Config Provider tab complete tests" -Tags Feature,RequireAdminOn @{path = "localhost\plugin"; parameter = "-ru"; expected = "RunAsCredential"}, @{path = "localhost\plugin"; parameter = "-us"; expected = "UseSharedProcess"}, @{path = "localhost\plugin"; parameter = "-au"; expected = "AutoRestart"}, - @{path = "localhost\plugin"; parameter = "-pr"; expected = "ProcessIdleTimeoutSec"}, + @{path = "localhost\plugin"; parameter = "-proc"; expected = "ProcessIdleTimeoutSec"}, @{path = "localhost\Plugin\microsoft.powershell\Resources\"; parameter = "-re"; expected = "ResourceUri"}, @{path = "localhost\Plugin\microsoft.powershell\Resources\"; parameter = "-ca"; expected = "Capability"} ) { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1 index 205d005020..43bdae8407 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1 @@ -1,26 +1,34 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. + Describe "Stream writer tests" -Tags "CI" { - $targetfile = Join-Path -Path $TestDrive -ChildPath "writeoutput.txt" - # A custom function is defined here do handle the debug stream dealing with the confirm prompt - # that would normally - function Write-Messages - { - [CmdletBinding()] + Context "Redirect Stream Tests" { + # These tests validate that a stream is actually being written to by redirecting the output of that stream - param() + BeforeAll { + function Write-Messages { + [CmdletBinding()] - Write-Verbose "Verbose message" + param() - Write-Debug "Debug message" + Write-Verbose "Verbose message" - } + Write-Debug "Debug message" - Context "Redirect Stream Tests" { - # These tests validate that a stream is actually being written to by redirecting the output of that stream + } + } + + BeforeEach { + $targetfile = [System.IO.Path]::GetTempFileName() + } + + AfterEach { + if (Test-Path -LiteralPath $targetfile) { + Remove-Item $targetfile + } + } - AfterEach { Remove-Item $targetfile } It "Should write warnings to the warning stream" { Write-Warning "Test Warning" 3>&1 > $targetfile @@ -55,24 +63,11 @@ Describe "Stream writer tests" -Tags "CI" { } Context "Write-Information cmdlet" { - BeforeAll { - $ps = [powershell]::Create() - - $testInfoData = @( - @{ Name = 'defaults'; Command = "Write-Information TestMessage"; returnCount = 1; returnValue = 'TestMessage' } - @{ Name = '-Object'; Command = "Write-Information -MessageData TestMessage"; returnCount = 1; returnValue = 'TestMessage' } - @{ Name = '-Message'; Command = "Write-Information -Message TestMessage"; returnCount = 1; returnValue = 'TestMessage' } - @{ Name = '-Msg'; Command = "Write-Information -Msg TestMessage"; returnCount = 1; returnValue = 'TestMessage' } - @{ Name = '-Tag'; Command = "Write-Information TestMessage -Tag Test"; returnCount = 1; returnValue = 'TestMessage' } - ) - } - BeforeEach { - $ps.Commands.Clear() - $ps.Streams.ClearStreams() + $ps = [powershell]::Create() } - AfterAll { + AfterEach { $ps.Dispose() } @@ -105,7 +100,15 @@ Describe "Stream writer tests" -Tags "CI" { $result[1].MessageData | Should -BeExactly "12345" } - It "Write-Information works with " -TestCases:$testInfoData { + $testInfoData = @( + @{ Name = 'defaults'; Command = "Write-Information TestMessage"; returnCount = 1; returnValue = 'TestMessage' } + @{ Name = '-Object'; Command = "Write-Information -MessageData TestMessage"; returnCount = 1; returnValue = 'TestMessage' } + @{ Name = '-Message'; Command = "Write-Information -Message TestMessage"; returnCount = 1; returnValue = 'TestMessage' } + @{ Name = '-Msg'; Command = "Write-Information -Msg TestMessage"; returnCount = 1; returnValue = 'TestMessage' } + @{ Name = '-Tag'; Command = "Write-Information TestMessage -Tag Test"; returnCount = 1; returnValue = 'TestMessage' } + ) + + It "Write-Information works with " -TestCases $testInfoData { param($Command, $returnCount, $returnValue) $ps.AddScript($Command).Invoke() @@ -122,4 +125,192 @@ Describe "Stream writer tests" -Tags "CI" { $i.MessageData | Should -Be $null } } + + Context 'Stream Common Parameter Tests' { + # These tests validate the *Variable and *Action common parameters + + $streams = if ($EnabledExperimentalFeatures.Contains('PSNewCommonParameters')) { + @('Error', 'Warning', 'Verbose', 'Debug', 'Information', 'Progress') + } else { + @('Error', 'Warning', 'Information') + } + $streamTestCases = foreach ($stream in $streams) { + @{ + Stream = $stream + } + } + + BeforeAll { + function Test-StreamData { + [CmdletBinding()] + param() + Write-Progress -Activity 'Warming up' -PercentComplete ([Math]::Round(0 / 6 * 100)) + Write-Progress -Activity 'Writing output' -Status 'Outputting an error' -PercentComplete ([Math]::Round(1 / 6 * 100)) + Write-Error -Message 'Error' + Write-Progress -Activity 'Writing output' -Status 'Outputting a warning' -PercentComplete ([Math]::Round(2 / 6 * 100)) + Write-Warning -Message 'Warning' + Write-Progress -Activity 'Writing output' -Status 'Outputting a verbose message' -PercentComplete ([Math]::Round(3 / 6 * 100)) + Write-Verbose -Message 'Verbose' + Write-Progress -Activity 'Writing output' -Status 'Outputting a debug message' -PercentComplete ([Math]::Round(4 / 6 * 100)) + Write-Debug -Message 'Debug' + Write-Progress -Activity 'Writing output' -Status 'Outputting an information message' -PercentComplete ([Math]::Round(5 / 6 * 100)) + Write-Information -MessageData 'Information' + Write-Progress -Activity 'Cooling down' -Completed -PercentComplete ([Math]::Round(6 / 6 * 100)) + } + } + + It 'Should be able to capture messages written to the stream' -TestCases $streamTestCases { + param($Stream) + + $streamData = @() + $parameters = @{ + "${Stream}Variable" = 'streamData' + } + Test-StreamData @parameters *> $null + + ,$streamData | Should -BeOfType [System.Collections.ArrayList] + $streamData.Count | Should -BeGreaterThan 0 + $streamData | Should -BeOfType "System.Management.Automation.${Stream}Record" + } + + It 'Should be able to capture messages written to the stream when the action is set to SilentlyContinue' -TestCases $streamTestCases { + param($Stream) + + $streamData = @() + $parameters = @{ + "${Stream}Variable" = 'streamData' + } + foreach ($streamName in $streams) { + $parameters["${streamName}Action"] = [System.Management.Automation.ActionPreference]::SilentlyContinue + } + Test-StreamData @parameters *> $null + + , $streamData | Should -BeOfType [System.Collections.ArrayList] + $streamData.Count | Should -BeGreaterThan 0 + $streamData | Should -BeOfType "System.Management.Automation.${Stream}Record" + } + + # We only check the error stream here because the others are capturable when ignored right now (see Issue #10248) + It 'Should not be able to capture messages written to the stream when the action is set to Ignore' -TestCases $streamTestCases.where{ $_.Values[0] -eq 'Error' } { + param($Stream) + + $streamData = @() + $parameters = @{ + "${Stream}Variable" = 'streamData' + } + foreach ($streamName in $streams) { + $parameters["${streamName}Action"] = [System.Management.Automation.ActionPreference]::Ignore + } + Test-StreamData @parameters *> $null + + , $streamData | Should -BeOfType [System.Collections.ArrayList] + $streamData.Count | Should -Be 0 + } + + # We skip the progress stream here because it is not redirectable + It 'Should be able to ignore all streams except for the stream' -TestCases $streamTestCases.where{ $_.Values[0] -ne 'Progress' } { + param($Stream) + $parameters = @{} + foreach ($streamName in $streams) { + $parameters["${streamName}Action"] = if ($streamName -eq $Stream) { + [System.Management.Automation.ActionPreference]::Continue + } else { + [System.Management.Automation.ActionPreference]::Ignore + } + } + $streamData = @(Test-StreamData @parameters *>&1) + + ,$streamData | Should -BeOfType [System.Object[]] + $streamData.Count | Should -BeGreaterThan 0 + $streamData | Should -BeOfType "System.Management.Automation.${Stream}Record" + } + + It 'Should prefer -VerboseAction over -Verbose when both are provided' -Skip:$(-not $EnabledExperimentalFeatures.Contains('PSNewCommonParameters')) { + $parameters = @{ + 'Verbose' = $true + } + foreach ($streamName in $streams) { + $parameters["${streamName}Action"] = [System.Management.Automation.ActionPreference]::Ignore + } + $streamData = @() + $streamData = @(Test-StreamData @parameters *>&1) + + , $streamData | Should -BeOfType [System.Object[]] + $streamData.Count | Should -Be 0 + } + + It 'Should prefer -DebugAction over -Debug when both are provided' -Skip:$(-not $EnabledExperimentalFeatures.Contains('PSNewCommonParameters')) { + $parameters = @{ + 'Debug' = $true + } + foreach ($streamName in $streams) { + $parameters["${streamName}Action"] = [System.Management.Automation.ActionPreference]::Ignore + } + $streamData = @() + $streamData = @(Test-StreamData @parameters *>&1) + + , $streamData | Should -BeOfType [System.Object[]] + $streamData.Count | Should -Be 0 + } + } + + Context 'Stream API tests' { + BeforeAll { + # Define a function to test stream output + function Test-StreamOutput { + [CmdletBinding()] + param() + $PSCmdlet.WriteError( + [System.Management.Automation.ErrorRecord]::new( + [System.Management.Automation.CommandNotFoundException]::new('Before'), + 'FirstError', + 'InvalidOperation', + $null)) + $ErrorActionPreference = 'Continue' + $PSCmdlet.WriteError( + [System.Management.Automation.ErrorRecord]::new( + [System.Management.Automation.CommandNotFoundException]::new('After'), + 'SecondError', + 'InvalidOperation', + $null)) + $PSCmdlet.WriteWarning('Before') + $WarningPreference = 'Continue' + $PSCmdlet.WriteWarning('After') + $PSCmdlet.WriteVerbose('Before') + $VerbosePreference = 'Continue' + $PSCmdlet.WriteVerbose('After') + $PSCmdlet.WriteDebug('Before') + $DebugPreference = 'Continue' + $PSCmdlet.WriteDebug('After') + $PSCmdlet.WriteInformation('Before', $null) + $InformationPreference = 'Continue' + $PSCmdlet.WriteInformation('After', $null) + $PSCmdlet.WriteProgress( + [System.Management.Automation.ProgressRecord]::new( + 1, + 'Testing progress stream', + 'Starting test' + ) + ) + $ProgressPreference = 'Continue' + $PSCmdlet.WriteProgress( + [System.Management.Automation.ProgressRecord]::new( + 1, + 'Testing progress stream', + 'Finished test' + ) + ) + } + } + + It 'Should not output anything from stream APIs when all streams are ignored at invocation time' { + & { + # Ignore all messages in a child scope so that they get reset afterwards + $ErrorActionPreference = $WarningPreference = $VerbosePreference = $DebugPreference = $InformationPreference = $ProgressPreference = 'Ignore' + + # Invoke the function and verify no stream data was returned + Test-StreamOutput *>&1 | Should -BeNullOrEmpty + } + } + } } diff --git a/test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1 b/test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1 index 2e86672b3a..3cbcbbc269 100644 --- a/test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1 +++ b/test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1 @@ -12,7 +12,7 @@ Describe "Experimental Feature Basic Tests - Feature-Disabled" -tags "CI" { $PSDefaultParameterValues["it:skip"] = $true } else { ## Common parameters are defined in the type 'CommonParameters' as public properties. - $CommonParameterCount = [System.Management.Automation.Internal.CommonParameters].GetProperties().Length + $CommonParameterCount = [System.Management.Automation.Cmdlet]::CommonParameters.Count $TestModule = Join-Path $PSScriptRoot "assets" "ExpTest" $AssemblyPath = Join-Path $TestModule "ExpTest.dll" if (-not (Test-Path $AssemblyPath)) { @@ -185,7 +185,7 @@ Describe "Experimental Feature Basic Tests - Feature-Enabled" -Tag "CI" { $PSDefaultParameterValues["it:skip"] = $true } else { ## Common parameters are defined in the type 'CommonParameters' as public properties. - $CommonParameterCount = [System.Management.Automation.Internal.CommonParameters].GetProperties().Length + $CommonParameterCount = [System.Management.Automation.Cmdlet]::CommonParameters.Count $TestModule = Join-Path $PSScriptRoot "assets" "ExpTest" $AssemblyPath = Join-Path $TestModule "ExpTest.dll" if (-not (Test-Path $AssemblyPath)) { diff --git a/test/powershell/engine/Remoting/PSSession.Tests.ps1 b/test/powershell/engine/Remoting/PSSession.Tests.ps1 index e985ff6ab3..c05150ead7 100644 --- a/test/powershell/engine/Remoting/PSSession.Tests.ps1 +++ b/test/powershell/engine/Remoting/PSSession.Tests.ps1 @@ -23,7 +23,7 @@ Describe "New-PSSessionOption parameters for non-Windows platforms" -Tag "CI" { $cmdInfo = Get-Command New-PSSessionOption - $commonParameterCount = [System.Management.Automation.Internal.CommonParameters].GetProperties().Length + $commonParameterCount = [System.Management.Automation.Cmdlet]::CommonParameters.Count $cmdInfo.Parameters.Count | Should -Be ($commonParameterCount + 2) -Because "Only -SkipCACheck and -SkipCNCheck switch parameters are available" { $null = $cmdInfo.ResolveParameter("SkipCACheck") } | Should -Not -Throw -Because "SkipCACheck parameter should be available" diff --git a/test/tools/TestMetadata.json b/test/tools/TestMetadata.json index fede31d68e..d5b93f5910 100644 --- a/test/tools/TestMetadata.json +++ b/test/tools/TestMetadata.json @@ -5,6 +5,7 @@ "test/powershell/Language/Operators/NullConditional.Tests.ps1", "test/powershell/Language/Parser/Parsing.Tests.ps1", "test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1" ], - "PSCultureInvariantReplaceOperator": [ "test/powershell/Language/Operators/ReplaceOperator.Tests.ps1" ] + "PSCultureInvariantReplaceOperator": [ "test/powershell/Language/Operators/ReplaceOperator.Tests.ps1" ], + "PSNewCommonParameters": ["test/powershell/Modules/Microsoft.PowerShell.Utility/Write-Stream.Tests.ps1"] } }