Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ private TableHeaderInfo GenerateTableHeaderInfoFromDataBaseInfo(PSObject so)
ci.alignment = colHeader.alignment;
if (colHeader.label != null)
{
ci.HeaderMatchesProperty = so.Properties[colHeader.label.text] is not null || !ExperimentalFeature.IsEnabled(ExperimentalFeature.PSCustomTableHeaderLabelDecoration);
ci.HeaderMatchesProperty = so.Properties[colHeader.label.text] is not null;

ci.label = this.dataBaseInfo.db.displayResourceManagerCache.GetTextTokenString(colHeader.label);
}
Expand Down
4 changes: 1 addition & 3 deletions src/System.Management.Automation/engine/BytePipe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ internal static FileBytePipe Create(string fileName, bool append)
throw new RuntimeException(null, e, errorRecord);
}

ApplicationInsightsTelemetry.SendExperimentalUseData(
ExperimentalFeature.PSNativeCommandPreserveBytePipe,
"f");
ApplicationInsightsTelemetry.SendExperimentalUseData("PSNativeCommandPreserveBytePipe", "f");
Comment thread
daxian-dbw marked this conversation as resolved.

return new FileBytePipe(fileStream);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,9 @@ public class ExperimentalFeature
#region Const Members

internal const string EngineSource = "PSEngine";
internal const string PSNativeCommandErrorActionPreferenceFeatureName = "PSNativeCommandErrorActionPreference";
internal const string PSNativeCommandPreserveBytePipe = "PSNativeCommandPreserveBytePipe";
internal const string PSModuleAutoLoadSkipOfflineFilesFeatureName = "PSModuleAutoLoadSkipOfflineFiles";
internal const string PSCustomTableHeaderLabelDecoration = "PSCustomTableHeaderLabelDecoration";
internal const string PSFeedbackProvider = "PSFeedbackProvider";
internal const string PSCommandWithArgs = "PSCommandWithArgs";
internal const string PSConstrainedAuditLogging = "PSConstrainedAuditLogging";
internal const string PSWindowsNativeCommandArgPassing = "PSWindowsNativeCommandArgPassing";

#endregion

Expand Down Expand Up @@ -120,30 +115,15 @@ static ExperimentalFeature()
new ExperimentalFeature(
name: "PSLoadAssemblyFromNativeCode",
description: "Expose an API to allow assembly loading from native code"),
new ExperimentalFeature(
name: PSNativeCommandErrorActionPreferenceFeatureName,
description: "Native commands with non-zero exit codes issue errors according to $ErrorActionPreference when $PSNativeCommandUseErrorActionPreference is $true"),
new ExperimentalFeature(
name: PSModuleAutoLoadSkipOfflineFilesFeatureName,
description: "Module discovery will skip over files that are marked by cloud providers as not fully on disk."),
new ExperimentalFeature(
name: PSCustomTableHeaderLabelDecoration,
description: "Formatting differentiation for table header labels that aren't property members"),
new ExperimentalFeature(
name: PSNativeCommandPreserveBytePipe,
description: "Byte output is retained when piping between two or more native commands"),
new ExperimentalFeature(
name: PSFeedbackProvider,
description: "Replace the hard-coded suggestion framework with the extensible feedback provider"),
new ExperimentalFeature(
name: PSCommandWithArgs,
description: "Enable `-CommandWithArgs` parameter for pwsh"),
new ExperimentalFeature(
name: PSConstrainedAuditLogging,
description: "PowerShell restriction logging when WDAC (Windows Defender Application Control) Code Integrity policy is set to Audit mode."),
new ExperimentalFeature(
name: "PSWindowsNativeCommandArgPassing",
description: "Enable 'Windows' as the native command argument passing mode"),
};

EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);
Expand Down
27 changes: 9 additions & 18 deletions src/System.Management.Automation/engine/InitialSessionState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4652,16 +4652,13 @@ static InitialSessionState()
#endregion
};

if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName))
{
builtinVariables.Add(
new SessionStateVariableEntry(
SpecialVariables.PSNativeCommandUseErrorActionPreference,
value: true, // when this feature is changed to stable, this should default to `false`
RunspaceInit.PSNativeCommandUseErrorActionPreferenceDescription,
ScopedItemOptions.None,
new ArgumentTypeConverterAttribute(typeof(bool))));
}
builtinVariables.Add(
new SessionStateVariableEntry(
SpecialVariables.PSNativeCommandUseErrorActionPreference,
value: false,
RunspaceInit.PSNativeCommandUseErrorActionPreferenceDescription,
ScopedItemOptions.None,
new ArgumentTypeConverterAttribute(typeof(bool))));

builtinVariables.Add(
new SessionStateVariableEntry(
Expand All @@ -4677,20 +4674,14 @@ static InitialSessionState()
/// <summary>
/// Assigns the default behavior for native argument passing.
/// If the system is non-Windows, we will return Standard.
/// If the experimental feature is enabled, we will return Windows.
/// Otherwise, we will return Legacy.
/// Otherwise, we will return Windows.
/// </summary>
private static NativeArgumentPassingStyle GetPassingStyle()
{
#if UNIX
return NativeArgumentPassingStyle.Standard;
#else
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSWindowsNativeCommandArgPassing))
{
return NativeArgumentPassingStyle.Windows;
}

return NativeArgumentPassingStyle.Legacy;
return NativeArgumentPassingStyle.Windows;
#endif
}

Expand Down
66 changes: 22 additions & 44 deletions src/System.Management.Automation/engine/NativeCommandProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,7 @@ internal override void ProcessRecord()
{
// If upstream is a native command it'll be writing directly to our stdin stream
// so we can skip reading here.
if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
|| !UpstreamIsNativeCommand)
if (!UpstreamIsNativeCommand)
{
while (Read())
{
Expand Down Expand Up @@ -547,7 +546,7 @@ private void InitNativeProcess()

// Send Telemetry indicating what argument passing mode we are in.
ApplicationInsightsTelemetry.SendExperimentalUseData(
ExperimentalFeature.PSWindowsNativeCommandArgPassing,
"PSWindowsNativeCommandArgPassing",
Comment thread
daxian-dbw marked this conversation as resolved.
NativeParameterBinderController.ArgumentPassingStyle.ToString());

#if !UNIX
Expand Down Expand Up @@ -720,9 +719,7 @@ private void InitNativeProcess()

lock (_sync)
{
if (!_stopped
&& (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
|| !UpstreamIsNativeCommand))
if (!_stopped && !UpstreamIsNativeCommand)
{
_inputWriter.Start(_nativeProcess, inputFormat);
}
Expand Down Expand Up @@ -785,21 +782,16 @@ private void InitOutputQueue()
if (CommandRuntime.ErrorMergeTo is MshCommandRuntime.MergeDataStream.Output)
{
StdOutDestination = null;
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe))
if (DownStreamNativeCommand is not null)
{
if (DownStreamNativeCommand is not null)
{
DownStreamNativeCommand.UpstreamIsNativeCommand = false;
DownStreamNativeCommand = null;
}
DownStreamNativeCommand.UpstreamIsNativeCommand = false;
DownStreamNativeCommand = null;
}
}

_nativeProcessOutputQueue = new BlockingCollection<ProcessOutputObject>();
// we don't assign the handler to anything, because it's used only for objects marshaling
BytePipe stdOutDestination = ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
? StdOutDestination ?? DownStreamNativeCommand?.CreateBytePipe(stdout: false)
: null;
BytePipe stdOutDestination = StdOutDestination ?? DownStreamNativeCommand?.CreateBytePipe(stdout: false);

BytePipe stdOutSource = null;
if (stdOutDestination is not null)
Expand All @@ -822,8 +814,7 @@ private ProcessOutputObject DequeueProcessOutput(bool blocking)
{
if (blocking)
{
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
&& _stdOutByteTransfer is not null)
if (_stdOutByteTransfer is not null)
{
_stdOutByteTransfer.EOF.GetAwaiter().GetResult();
return null;
Expand Down Expand Up @@ -852,8 +843,7 @@ private ProcessOutputObject DequeueProcessOutput(bool blocking)
}
else
{
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
&& _stdOutByteTransfer is not null)
if (_stdOutByteTransfer is not null)
{
return null;
}
Expand Down Expand Up @@ -896,8 +886,7 @@ internal override void Complete()
if (!_isRunningInBackground)
{
// Wait for input writer to finish.
if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
|| !UpstreamIsNativeCommand)
if (!UpstreamIsNativeCommand)
{
_inputWriter.Done();
}
Expand Down Expand Up @@ -951,12 +940,6 @@ internal override void Complete()

this.commandRuntime.PipelineProcessor.ExecutionFailed = true;

// Feature is not enabled, so return
if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName))
{
return;
}

// We send telemetry information only if the feature is enabled.
// This shouldn't be done once, because it's a run-time check we should send telemetry every time.
// Report on the following conditions:
Expand All @@ -972,12 +955,12 @@ internal override void Complete()
// The variable is unset
if (useDefaultSetting)
{
ApplicationInsightsTelemetry.SendExperimentalUseData(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName, "unset");
ApplicationInsightsTelemetry.SendExperimentalUseData("PSNativeCommandErrorActionPreference", "unset");
Comment thread
JamesWTruher marked this conversation as resolved.
return;
}

// Send the value that was set.
ApplicationInsightsTelemetry.SendExperimentalUseData(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName, nativeErrorActionPreferenceSetting.ToString());
ApplicationInsightsTelemetry.SendExperimentalUseData("PSNativeCommandErrorActionPreference", nativeErrorActionPreferenceSetting.ToString());
Comment thread
JamesWTruher marked this conversation as resolved.

// if it was explicitly set to false, return
if (!nativeErrorActionPreferenceSetting)
Expand Down Expand Up @@ -1266,8 +1249,7 @@ internal void StopProcessing()
if (!_runStandAlone)
{
// Stop input writer
if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
|| !UpstreamIsNativeCommand)
if (!UpstreamIsNativeCommand)
{
_inputWriter.Stop();
}
Expand Down Expand Up @@ -1815,8 +1797,7 @@ public ProcessOutputHandler(
return;
}

if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
|| stdOutDestination is null)
if (stdOutDestination is null)
{
_isFirstOutput = true;
_isXmlCliOutput = false;
Expand Down Expand Up @@ -2073,19 +2054,16 @@ internal void Add(object input)

object baseObjInput = PSObject.Base(input);

if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe))
if (baseObjInput is byte[] bytes)
{
if (baseObjInput is byte[] bytes)
{
_streamWriter.BaseStream.Write(bytes, 0, bytes.Length);
return;
}
_streamWriter.BaseStream.Write(bytes, 0, bytes.Length);
return;
}

if (baseObjInput is byte b)
{
_streamWriter.BaseStream.WriteByte(b);
return;
}
if (baseObjInput is byte b)
{
_streamWriter.BaseStream.WriteByte(b);
return;
}

AddTextInput(input);
Expand Down
33 changes: 14 additions & 19 deletions src/System.Management.Automation/engine/pipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,31 +261,26 @@ private void LogToEventLog()
/// <exception cref="ObjectDisposedException"></exception>
internal int Add(CommandProcessorBase commandProcessor)
{
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe))
if (commandProcessor is NativeCommandProcessor nativeCommand)
{
if (commandProcessor is NativeCommandProcessor nativeCommand)
if (_lastNativeCommand is not null)
{
if (_lastNativeCommand is not null)
// Only report experimental feature usage once per pipeline.
if (!_haveReportedNativePipeUsage)
{
// Only report experimental feature usage once per pipeline.
if (!_haveReportedNativePipeUsage)
{
ApplicationInsightsTelemetry.SendExperimentalUseData(
ExperimentalFeature.PSNativeCommandPreserveBytePipe,
"p");
_haveReportedNativePipeUsage = true;
}

_lastNativeCommand.DownStreamNativeCommand = nativeCommand;
nativeCommand.UpstreamIsNativeCommand = true;
ApplicationInsightsTelemetry.SendExperimentalUseData("PSNativeCommandPreserveBytePipe", "p");
Comment thread
daxian-dbw marked this conversation as resolved.
_haveReportedNativePipeUsage = true;
}

_lastNativeCommand = nativeCommand;
}
else
{
_lastNativeCommand = null;
_lastNativeCommand.DownStreamNativeCommand = nativeCommand;
nativeCommand.UpstreamIsNativeCommand = true;
}

_lastNativeCommand = nativeCommand;
}
else
{
_lastNativeCommand = null;
}

commandProcessor.CommandRuntime.PipelineProcessor = this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,7 @@ private static CommandProcessorBase AddCommand(PipelineProcessor pipe,
bool redirectedInformation = false;
if (redirections != null)
{
bool shouldProcessMergesFirst = ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe)
&& isNativeCommand;

if (shouldProcessMergesFirst)
if (isNativeCommand)
{
foreach (CommandRedirection redirection in redirections)
{
Expand All @@ -237,7 +234,7 @@ private static CommandProcessorBase AddCommand(PipelineProcessor pipe,

foreach (CommandRedirection redirection in redirections)
{
if (!shouldProcessMergesFirst || redirection is not MergingRedirection)
if (!isNativeCommand || redirection is not MergingRedirection)
{
redirection.Bind(pipe, commandProcessor, context);
}
Expand Down Expand Up @@ -1081,16 +1078,13 @@ public override string ToString()
// dir > out
internal override void Bind(PipelineProcessor pipelineProcessor, CommandProcessorBase commandProcessor, ExecutionContext context)
{
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandPreserveBytePipe))
if (commandProcessor is NativeCommandProcessor nativeCommand
&& nativeCommand.CommandRuntime.ErrorMergeTo is not MshCommandRuntime.MergeDataStream.Output
&& FromStream is RedirectionStream.Output
&& !string.IsNullOrWhiteSpace(File))
{
if (commandProcessor is NativeCommandProcessor nativeCommand
&& nativeCommand.CommandRuntime.ErrorMergeTo is not MshCommandRuntime.MergeDataStream.Output
&& FromStream is RedirectionStream.Output
&& !string.IsNullOrWhiteSpace(File))
{
nativeCommand.StdOutDestination = FileBytePipe.Create(File, Appending);
return;
}
nativeCommand.StdOutDestination = FileBytePipe.Create(File, Appending);
return;
}

Pipe pipe = GetRedirectionPipe(context, pipelineProcessor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@
Describe 'Native command byte piping tests' -Tags 'CI' {
BeforeAll {
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
if (-not [ExperimentalFeature]::IsEnabled('PSNativeCommandPreserveBytePipe'))
{
$PSDefaultParameterValues['It:Skip'] = $true
return
}

# Without this the test would otherwise be hard coded to a specific set
# of [Console]::OutputEncoding/$OutputEncoding settings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@
Describe 'Native command error handling tests' -Tags 'CI' {
BeforeAll {
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
if (-not [ExperimentalFeature]::IsEnabled('PSNativeCommandErrorActionPreference'))
{
$PSDefaultParameterValues['It:Skip'] = $true
return
}

$exeName = $IsWindows ? 'testexe.exe' : 'testexe'
$exePath = @(Get-Command $exeName -Type Application)[0].Path

Expand Down