diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs index fd6823d6b2a..0bfb719e7be 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs @@ -169,6 +169,30 @@ public override void WriteWarningLine(string message) internal class CommandLineParameterParser { + internal static string[] validParameters = { + "psconsoleFile", + "version", + "nologo", + "noexit", +#if !CORECLR + "sta", + "mta", +#endif + "noprofile", + "noninteractive", + "inputformat", + "outputformat", +#if !UNIX + "windowstyle", +#endif + "encodedcommand", + "configurationname", + "file", + "executionpolicy", + "command", + "help" + }; + internal CommandLineParameterParser(PSHostUserInterface hostUI, string bannerText, string helpText) { if (hostUI == null) { throw new PSArgumentNullException("hostUI"); } @@ -593,7 +617,7 @@ private void ParseHelper(string[] args) break; } } -#if !CORECLR // windowstyle parameter not supported on NanoServer because ProcessWindowStyle does Not exist on CoreCLR +#if !UNIX else if (MatchSwitch(switchKey, "windowstyle", "w")) { ++i; @@ -657,7 +681,7 @@ private void ParseHelper(string[] args) WriteCommandLineError( "The -module option can only be specified with the -iss option.", showHelp: true, - showBanner: true); + showBanner: false); break; } @@ -880,7 +904,7 @@ bool TryGetBoolValue(string arg, out bool boolValue) WriteCommandLineError( CommandLineParameterParserStrings.MissingFileArgument, showHelp: true, - showBanner: true); + showBanner: false); return false; } @@ -924,15 +948,36 @@ bool TryGetBoolValue(string arg, out bool boolValue) { WriteCommandLineError( string.Format(CultureInfo.CurrentCulture, CommandLineParameterParserStrings.InvalidFileArgument, args[i], exceptionMessage), - showBanner: true); + showBanner: false); return false; } if (!System.IO.File.Exists(_file)) { + if (args[i].StartsWith("-") && args[i].Length > 1) + { + string param = args[i].Substring(1, args[i].Length - 1).ToLower(); + StringBuilder possibleParameters = new StringBuilder(); + foreach (string validParameter in validParameters) + { + if (validParameter.Contains(param)) + { + possibleParameters.Append("\n -"); + possibleParameters.Append(validParameter); + } + } + if (possibleParameters.Length > 0) + { + WriteCommandLineError( + string.Format(CultureInfo.CurrentCulture, CommandLineParameterParserStrings.InvalidArgument, args[i]), + showBanner: false); + WriteCommandLineError(possibleParameters.ToString(), showBanner: false); + return false; + } + } WriteCommandLineError( string.Format(CultureInfo.CurrentCulture, CommandLineParameterParserStrings.ArgumentFileDoesNotExist, args[i]), - showBanner: true); + showBanner: false); return false; } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs index 2513fe9e963..0f0c7f5cc4d 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs @@ -445,8 +445,7 @@ internal enum KeyboardFlag : uint internal const int SW_FORCEMINIMIZE = 11; internal const int SW_MAX = 11; - -#if !CORECLR // ProcessWindowStyle does Not exist on CoreCLR +#if !UNIX /// /// Code to control the display properties of the a window... /// diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index c2eb41a1f85..a2bb66e1ce8 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -53,10 +53,10 @@ internal sealed partial class ConsoleHost { #region static methods - internal const uint ExitCodeSuccess = 0x00000000; - internal const uint ExitCodeCtrlBreak = 0xFFFE0000; - internal const uint ExitCodeInitFailure = 0xFFFF0000; - internal const uint ExitCodeBadCommandLineParameter = 0xFFFD0000; + internal const int ExitCodeSuccess = 0; + internal const int ExitCodeCtrlBreak = 128+21; // SIGBREAK + internal const int ExitCodeInitFailure = 70; // Internal Software Error + internal const int ExitCodeBadCommandLineParameter = 64; // Command Line Usage Error // NTRAID#Windows Out Of Band Releases-915506-2005/09/09 // Removed HandleUnexpectedExceptions infrastructure @@ -212,10 +212,7 @@ internal static int Start( { s_theConsoleHost.ui.WriteErrorLine(ConsoleHostStrings.ConflictingServerModeParameters); } - unchecked - { - return (int)ExitCodeBadCommandLineParameter; - } + return ExitCodeBadCommandLineParameter; } // First check for and handle PowerShell running in a server mode. diff --git a/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx b/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx index 9209d37a603..92ef090cd2a 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx +++ b/src/Microsoft.PowerShell.ConsoleHost/resources/CommandLineParameterParserStrings.resx @@ -252,7 +252,7 @@ EXAMPLES Processing -File '{0}' failed because the file does not have a '.ps1' extension. Specify a valid Windows PowerShell script file name, and then try again. - The argument '{0}' to the -File parameter does not exist. Provide the path to an existing '.ps1' file as an argument to the -File parameter. + The argument '{0}' is not recognized as the name of a script file. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. Cannot process the command because the value specified with -EncodedArguments is not properly encoded. The value must be Base64 encoded. @@ -269,4 +269,7 @@ EXAMPLES Cannot process the command because -Configuration requires an argument that is a remote endpoint configuration name. Specify this argument and try again. + + Invalid argument '{0}', did you mean: + diff --git a/test/powershell/Host/ConsoleHost.Tests.ps1 b/test/powershell/Host/ConsoleHost.Tests.ps1 index 4bb8b8b0581..16c8032381c 100644 --- a/test/powershell/Host/ConsoleHost.Tests.ps1 +++ b/test/powershell/Host/ConsoleHost.Tests.ps1 @@ -52,6 +52,7 @@ Describe "ConsoleHost unit tests" -tags "Feature" { BeforeAll { $powershell = Join-Path -Path $PsHome -ChildPath "powershell" + $ExitCodeBadCommandLineParameter = 64 function NewProcessStartInfo([string]$CommandLine, [switch]$RedirectStdIn) { @@ -492,6 +493,95 @@ foo powershell -v | Should Match $psversiontable.GitCommitId } } + + Context "Ambiguous arguments" { + It "Ambiguous argument '' should return possible matches" -TestCases @( + @{testArg="-no";expectedMatches=@("-nologo","-noexit","-noprofile","-noninteractive")}, + @{testArg="-format";expectedMatches=@("-inputformat","-outputformat")} + ) { + param($testArg, $expectedMatches) + $output = & $powershell $testArg -File foo 2>&1 + $LASTEXITCODE | Should Be $ExitCodeBadCommandLineParameter + $outString = [String]::Join(",", $output) + foreach ($expectedMatch in $expectedMatches) + { + $outString | Should Match $expectedMatch + } + } + } +} + +Describe "WindowStyle argument" -Tag Feature { + BeforeAll { + $defaultParamValues = $PSDefaultParameterValues.Clone() + $PSDefaultParameterValues["it:skip"] = !$IsWindows + + if ($IsWindows) + { + $ExitCodeBadCommandLineParameter = 64 + Add-Type -Name User32 -Namespace Test -MemberDefinition @" +public static WINDOWPLACEMENT GetPlacement(IntPtr hwnd) +{ + WINDOWPLACEMENT placement = new WINDOWPLACEMENT(); + placement.length = Marshal.SizeOf(placement); + GetWindowPlacement(hwnd, ref placement); + return placement; +} + +[DllImport("user32.dll", SetLastError = true)] +[return: MarshalAs(UnmanagedType.Bool)] +public static extern bool GetWindowPlacement( + IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + +[Serializable] +[StructLayout(LayoutKind.Sequential)] +public struct WINDOWPLACEMENT +{ + public int length; + public int flags; + public ShowWindowCommands showCmd; + public System.Drawing.Point ptMinPosition; + public System.Drawing.Point ptMaxPosition; + public System.Drawing.Rectangle rcNormalPosition; +} + +public enum ShowWindowCommands : int +{ + Hidden = 0, + Normal = 1, + Minimized = 2, + Maximized = 3, +} +"@ + } + } + + AfterAll { + $global:PSDefaultParameterValues = $defaultParamValues + } + + It "-WindowStyle should work on Windows" -TestCases @( + @{WindowStyle="Normal"}, + @{WindowStyle="Minimized"}, + @{WindowStyle="Maximized"} # hidden doesn't work in CI/Server Core + ) { + param ($WindowStyle) + $ps = Start-Process powershell -ArgumentList "-WindowStyle $WindowStyle -noexit -interactive" -PassThru + $startTime = Get-Date + $showCmd = "Unknown" + while (((Get-Date) - $startTime).TotalSeconds -lt 10 -and $showCmd -ne $WindowStyle) + { + Start-Sleep -Milliseconds 100 + $showCmd = ([Test.User32]::GetPlacement($ps.MainWindowHandle)).showCmd + } + $showCmd | Should BeExactly $WindowStyle + $ps | Stop-Process -Force + } + + It "Invalid -WindowStyle returns error" { + powershell -WindowStyle invalid + $LASTEXITCODE | Should Be $ExitCodeBadCommandLineParameter + } } Describe "Console host api tests" -Tag CI {