3

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?

Ding Xin
  • 307
  • 1
  • 3
  • 11
  • I guess that the commands are executed at the moment the `enter` key is pressed down. Meaning that it captures a **really-released-enter** key. What happens when you hold down the enter key when starting it (presumably *entering* the name of the script)? – iRon Nov 06 '20 at 09:33
  • @iRon I just enter the file name of the script in PS console prompt (e.g. ">.\thescript.ps1") and then press enter to start running the script. After the script is started, I didn't pressed any key before this strange behavior occurs. – Ding Xin Nov 06 '20 at 09:41

1 Answers1

4

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

CFou
  • 978
  • 3
  • 13
  • Thank you very much. But the $host.UI.RawUI.ReadKey() method always captures a not-really-pressed Enter key? I'm not sure about this. – Ding Xin Nov 06 '20 at 09:44
  • You are right, see my edit answer (set the $ki twice, the first to flush the Enter key) – CFou Nov 06 '20 at 11:15