Windows: $host.UI.RawUI.KeyAvailable has false positives. · Issue #959 · PowerShell/PSReadLine · GitHub
Skip to content

Windows: $host.UI.RawUI.KeyAvailable has false positives. #959

Open

Description

Environment data

PS version: 7.0.0-preview.1
PSReadline version: 2.0.0-beta4
os: 10.0.18362.1 (WinBuild.160101.0800)
PS file version: 7.0.0.0
BufferWidth: 180
BufferHeight: 9999

Steps to reproduce or exception report

Note: The problem surfaces on Windows only.

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.

Activity

  1. lzybkr commented on Jul 6, 2019

    @lzybkr
    Contributor

    Duplicate of #901 and #291.

  2. daxian-dbw commented on Nov 11, 2019

    @daxian-dbw
    Member

    Quoted from #291 (comment)

    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?

  3. lzybkr commented on Nov 11, 2019

    @lzybkr
    Contributor

    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:

    • 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$)?
  4. Stewartarmbrecht commented on Jun 16, 2020

    @Stewartarmbrecht

    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?

  5. iSazonov commented on Jun 16, 2020

    @iSazonov

    We have PowerShell/PowerShell#7971 - Read-Host is slow on Linux. Perhaps it is related to this.

  6. added a commit that references this issue on Dec 21, 2020
  7. forevanyeung commented on Dec 21, 2020

    @forevanyeung

    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.

    start-sleep -milliseconds 100; 
    $host.ui.RawUI.FlushInputBuffer(); 
    $i=0; while(-not $host.UI.RawUI.KeyAvailable) { Write-Host -NoNewline ("`r{0}" -f '/?\|'[($i++ % 4)]); start-sleep -Milliseconds 200 }
  8. jeremyhagan commented on Aug 27, 2021

    @jeremyhagan

    Using [Console]::KeyAvailable worked for me

  9. mklement0 commented on Sep 21, 2021

    @mklement0
    Author

    Interestingly, in Windows Terminal (but not in regular console windows) the problem also surfaces when PSReadLine is not loaded.

  10. ghost closed this as completedon Feb 9, 2023
  11. added
    Needs-More InvestigationInitial investigation is done, but need to follow up.
    and removed
    Resolution-DuplicateThere's another issue on the tracker that's pretty much the same thing.
    on Feb 9, 2023
  12. Slion commented on Mar 18, 2025

    @Slion

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs-More InvestigationInitial investigation is done, but need to follow up.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions