Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class ExperimentalFeature
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 @@ -139,7 +140,10 @@ static ExperimentalFeature()
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.")
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
22 changes: 21 additions & 1 deletion src/System.Management.Automation/engine/InitialSessionState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4534,14 +4534,34 @@ static InitialSessionState()
builtinVariables.Add(
new SessionStateVariableEntry(
SpecialVariables.NativeArgumentPassing,
Platform.IsWindows ? NativeArgumentPassingStyle.Windows : NativeArgumentPassingStyle.Standard,
GetPassingStyle(),
RunspaceInit.NativeCommandArgumentPassingDescription,
ScopedItemOptions.None,
new ArgumentTypeConverterAttribute(typeof(NativeArgumentPassingStyle))));

BuiltInVariables = builtinVariables.ToArray();
}

/// <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.
/// </summary>
private static NativeArgumentPassingStyle GetPassingStyle()
{
#if UNIX
return NativeArgumentPassingStyle.Standard;
#else
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSWindowsNativeCommandArgPassing))
{
return NativeArgumentPassingStyle.Windows;
}

return NativeArgumentPassingStyle.Legacy;
#endif
}

internal static readonly SessionStateVariableEntry[] BuiltInVariables;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,11 @@ private void InitNativeProcess()
// Get the start info for the process.
ProcessStartInfo startInfo = GetProcessStartInfo(redirectOutput, redirectError, redirectInput, soloCommand);

// Send Telemetry indicating what argument passing mode we are in.
ApplicationInsightsTelemetry.SendExperimentalUseData(
ExperimentalFeature.PSWindowsNativeCommandArgPassing,
NativeParameterBinderController.ArgumentPassingStyle.ToString());

#if !UNIX
string commandPath = this.Path.ToLowerInvariant();
if (commandPath.EndsWith("powershell.exe") || commandPath.EndsWith("powershell_ise.exe"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ param()

Describe "Behavior is specific for each platform" -tags "CI" {
It "PSNativeCommandArgumentPassing is set to 'Windows' on Windows systems" -skip:(-not $IsWindows) {
$PSNativeCommandArgumentPassing | Should -Be "Windows"
if ([Version]::TryParse($PSVersiontable.PSVersion.ToString(), [ref]$null)) {
$PSNativeCommandArgumentPassing | Should -BeExactly "Legacy"
}
else {
$PSNativeCommandArgumentPassing | Should -BeExactly "Windows"
}
}
It "PSNativeCommandArgumentPassing is set to 'Standard' on non-Windows systems" -skip:($IsWindows) {
$PSNativeCommandArgumentPassing | Should -Be "Standard"
Expand Down