Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why $host.UI.RawUI.ReadKey() method captures a not-really-pressed Enter key?

I want to write a function in PowerShell 7 to wait the user to press a key or a key combination (such as Alt+Ctrl+D). There are two options I can choose: The Console.ReadKey() method in .net core, and $host.UI.RawUI.ReadKey() method in PowerShell.

I have tested these two methods. The Console.ReadKey() method works very well, but the $host.UI.RawUI.ReadKey() method has a strange behavior: It will capture a not-really-pressed "Enter" key (key code: 13).

The code (in a .ps1 script file, for testing the method):

using namespace System.Threading
using namespace System.Management.Automation.Host

Write-Host "Content before waiting keys."
Write-Host "Begin waiting keys..... Press 'Esc' to quit."

while($true)
{
    while(-not $host.UI.RawUI.KeyAvailable)
    {
        Write-Host '.' -NoNewLine
        [Thread]::Sleep(1000)
    }
    
    # No matter any combination of values of the ReadKeyOptions enum, 
    # the behavior of capturing not-really-pressed Enter key is the same.
    $ki = $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyUp")
    
    Write-Host "[$($ki.ControlKeyState)]" -ForegroundColor Yellow
    
    $altPressed = (($ki.ControlKeyState -band [ControlKeyStates]::LeftAltPressed) -gt 0) -or 
        (($ki.ControlKeyState -band [ControlKeyStates]::RightAltPressed) -gt 0)
    $ctrlPressed = (($ki.ControlKeyState -band [ControlKeyStates]::LeftCtrlPressed) -gt 0) -or 
        (($ki.ControlKeyState -band [ControlKeyStates]::RightCtrlPressed) -gt 0)
    $shiftPressed = (($ki.ControlKeyState -band [ControlKeyStates]::ShiftPressed) -gt 0)
    
    $keyState = $ki.KeyDown ? "Down" : "UP"
    
    Write-Host "`nGot a key:"
    Write-Host "`tkey: $($ki.Character)"  # Char
    Write-Host "`tkey code: $($ki.VirtualKeyCode)"  # int.
    
    Write-Host "`tAlt: $altPressed"
    Write-Host "`tCtrl: $ctrlPressed"
    Write-Host "`tShift: $shiftPressed"
    
    Write-Host "`tkey state: $keyState"
    
    if($ki.VirtualKeyCode -eq 27)
    {
        break
    }
}

Write-Host "`nContent after waiting keys."

After I run the script in PowerShell 7 console, before I pressed any key, I got:

Content before waiting keys.
Begin waiting keys..... Press 'Esc' to quit.
.[NumLockOn, EnhancedKey]

Got a key:
        key:
        key code: 13
        Alt: False
        Ctrl: False
        Shift: False
        key state: UP
...[NumLockOn]

Even I call the $host.UI.RawUI.FlushInputBuffer() method before the while loop, or before the calling of the ReadKey() method, this strange behavior will still occur.

If I use $host.UI.RawUI.ReadKey() method, this behavior will break the working of my function, make it cannot handle a single Enter key correctly, and it cannot be used to block until any key is pressed because it will capture an "Enter" key even though the user hasn't pressed the Enter key.

The Console.ReadKey() method hasn't this problem.

Why?

like image 865
Ding Xin Avatar asked Dec 14 '25 07:12

Ding Xin


1 Answers

This is a known bug $Host.UI.RawUI.ReadKey Gets a extra enter key for no reason

as a workaround you can do this (which is not pretty :))

$ki = $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyUp")
$ki = $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyUp")

but $ki.Character will return null, you can still use this [char]$ki.VirtualKeyCode

like image 194
CFou Avatar answered Dec 16 '25 21:12

CFou