You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Windows: $host.UI.RawUI.KeyAvailable has false positives. #959
Run the following code with and without PSReadLine imported:
# Show a spinner until the user presses a key.$i=0; while (-not$host.UI.RawUI.KeyAvailable) { Write-Host-NoNewline ("`r{0}"-f'/―\|'[($i++%4)]); start-sleep-ms 200 }
Expected: A spinner should appear and keep spinning until the user presses a key.
Actual, with PSReadLine imported: The while loop is either never entered (1st invocation immediately after start of the PS session) or stops after one iteration even if no key is pressed (subsequent invocations).
Once you run Remove-Module PSReadLine, the behavior is as expected in regular console windows; interestingly, the problem persists in a Windows Terminal window.
For consoles / terminals only, using [Console]::KeyAvailable in lieu of $host.UI.RawUI.KeyAvailable is an effective workaround.
It seems reasonable for PSReadLine to consume the final KeyUp before returning to the caller.
First, this is a Windows only issue because on Linux/macOS, the KeyAvailable and ReadKey implementation in ConsoleHost use Console.ReadKey directly.
I looked into consuming the final KeyUp events before returning from PSConsoleReadLine.ReadLine using the Win32 APIs GetNumberOfConsoleInputEvents, PeekConsoleInput and GetConsoleInput. The problem is, after Console.ReadKey(), there is a delay till the unread KeyUp events can be queried by those APIs.
On my dev machine, the delay is about 100 ms. With Thread.Sleep(100) after Console.ReadKey(), the APIs GetNumberOfConsoleInputEvents, PeekConsoleInput and GetConsoleInput can return the unread KeyUp keys, while without Thread.Sleep or sleeping for a shorter time span, those 3 Win32 APIs shows 0 event read. This explains the reported symptom: The while loop is either never entered or stops after one iteration even if no key is pressed. It stops after one iteration because the first time $host.UI.RawUI.KeyAvailable is call, the unread KeyUp events haven't shown up to the Win32 APIs yet, and the second call to the property, they show up.
Given the delay, I don't think there is a reliable way to consume the KeyUp events before returning to the caller. Jason Shirk (@lzybkr) any suggestion?
Previous alternatives I've considered - but not really good options:
Change .Net to return on key up.
Use P/Invoke directly on Windows.
And a new idea based your investigation:
Consume the keyboard event in the background. This is might be a horrible idea - it would introduce a race condition unless you can lock the correct handle (CONIO$)?
Is there a work around for this? $Host.UI.RawUI.KeyAvailable always returns true and if I ReadKey after this it breaks in my script. Is there anyway to defect a key press other than KeyAvailable?
Here is my workaround for this. Apparently FlushInputBuffer() is not quick enough so I need to throw in a quick sleep at the beginning. I have found 100ms does the trick reliably, I was able to go down as low as 75ms but it was a hit or miss. Tested in PowerShell 5.1 and 7.0.3.
Still an issue over 5 years later on v7.5.0.
I was porting a script from PowerShell 5 and ran into this issue.
Luckily as mentioned above using [Console]::KeyAvailable instead is working fine so far.
Activity
lzybkr commentedon Jul 6, 2019
Duplicate of #901 and #291.
daxian-dbw commentedon Nov 11, 2019
Quoted from #291 (comment)
First, this is a Windows only issue because on Linux/macOS, the
KeyAvailableandReadKeyimplementation inConsoleHostuseConsole.ReadKeydirectly.I looked into consuming the final
KeyUpevents before returning fromPSConsoleReadLine.ReadLineusing the Win32 APIsGetNumberOfConsoleInputEvents,PeekConsoleInputandGetConsoleInput. The problem is, afterConsole.ReadKey(), there is a delay till the unreadKeyUpevents can be queried by those APIs.On my dev machine, the delay is about 100 ms. With
Thread.Sleep(100)afterConsole.ReadKey(), the APIsGetNumberOfConsoleInputEvents,PeekConsoleInputandGetConsoleInputcan return the unreadKeyUpkeys, while withoutThread.Sleepor sleeping for a shorter time span, those 3 Win32 APIs shows 0 event read. This explains the reported symptom:The while loop is either never entered or stops after one iteration even if no key is pressed. It stops after one iteration because the first time$host.UI.RawUI.KeyAvailableis call, the unreadKeyUpevents haven't shown up to the Win32 APIs yet, and the second call to the property, they show up.Given the delay, I don't think there is a reliable way to consume the
KeyUpevents before returning to the caller. Jason Shirk (@lzybkr) any suggestion?lzybkr commentedon Nov 11, 2019
Dongbo Wang (@daxian-dbw) 100ms? Maybe Dustin L. Howett (MSFT) (@DHowett-MSFT) has an idea why that might be.
Previous alternatives I've considered - but not really good options:
And a new idea based your investigation:
Stewartarmbrecht commentedon Jun 16, 2020
Is there a work around for this? $Host.UI.RawUI.KeyAvailable always returns true and if I ReadKey after this it breaks in my script. Is there anyway to defect a key press other than KeyAvailable?
iSazonov commentedon Jun 16, 2020
We have PowerShell/PowerShell#7971 - Read-Host is slow on Linux. Perhaps it is related to this.
Fix ghost Enter bug
forevanyeung commentedon Dec 21, 2020
Here is my workaround for this. Apparently
FlushInputBuffer()is not quick enough so I need to throw in a quick sleep at the beginning. I have found 100ms does the trick reliably, I was able to go down as low as 75ms but it was a hit or miss. Tested in PowerShell 5.1 and 7.0.3.Use `[Console]::KeyAvailable` to work around PSReadLine bug
jeremyhagan commentedon Aug 27, 2021
Using
[Console]::KeyAvailableworked for memklement0 commentedon Sep 21, 2021
Interestingly, in Windows Terminal (but not in regular console windows) the problem also surfaces when
PSReadLineis not loaded.Slion commentedon Mar 18, 2025
Still an issue over 5 years later on v7.5.0.
I was porting a script from PowerShell 5 and ran into this issue.
Luckily as mentioned above using
[Console]::KeyAvailableinstead is working fine so far.