-
Notifications
You must be signed in to change notification settings - Fork 312
Description
Description of the new feature/enhancement
Under certain (unexpected) circumstances the Console.ReadKey
and KeyAvailable
APIs can throw an InvalidOperationException
. See: dotnet/runtime#88697
(TL;DR: if a second process attached to the console is also waiting for input, and then is terminated, .NET gets back 0 records, and decides to throw.)
Ideally, that bug would be fixed in .NET. However, it is unclear when or if that will happen. I would like to propose that we update PSReadLine's use of these APIs to handle these situations instead of crashing.
For those who know what "razzle" is: the end goal here is to fix ctrl+c in PowerShell razzle.
(For those who don't know: "razzle" is an internal build environment, in which it is not too hard to hit the situation described in dotnet/runtime#88697.)
Proposed technical implementation details
The general idea is simple: wrap in a try
/catch
, and if it fails, just reissue the call.
One possible wrinkle: those APIs are expected to throw if the "application does not have a console or when console input has been redirected". I would not expect PSReadLine to come into play at all in such a case. Since a user script/module has no opportunity to catch the exception, such an exception should always crash the process. But it could be possible that someone is depending on this behavior (to crash in a console-input-redirected case)... in which case, we could try to detect this (not sure off the top of my head how) or adopt a heuristic, like "if the operation throws 5 times in a row, give up and let the exception fly". Let me know if you think going to such lengths is necessary.
Exhibit A: stack trace when the problem is hit:
An error has occurred that was not properly handled. Additional information is shown below. The PowerShell process will exit.
Unhandled exception. System.InvalidOperationException: Cannot read keys when either application does not have a console or when console input has been redirected. Try Console.Read.
at System.ConsolePal.ReadKey(Boolean intercept)
at Microsoft.PowerShell.PSConsoleReadLine.ReadOneOrMoreKeys()
at Microsoft.PowerShell.PSConsoleReadLine.ReadKeyThreadProc()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)