2015-10-19

Handling keyboard input and CTRL+C in Powershell without pausing

Recently, I had a requirement to update my script console output depending on user key-press. Since calculation was done by background threads, I also wanted to prevent CTRL+C from stopping the script without proper cleanup.
However, looks like seamless handling of key-press along with handling special sequences is not available in Powershell. This was to be expected given that [system.console]::readkey is designed for accepting the user input which is mostly answers to flow control questions. Register-EngineEvent does not help either.

Anyway, let's tackle both problems one by one:
1) Disable CTRL+C from stopping the script:
       
            [console]::TreatControlCAsInput = $true
       
Note that CTRL+BREAK will end the session entirely and do the proper cleanup thus it's not a problem.

2) Handle the user key-press:
       
if ($Host.UI.RawUI.KeyAvailable) { #Make sure there is something to handle.
    #$k = [system.console]::readkey($true) -This will wait for ENTER after any key-press, thus unacceptable.
    $k = $Host.UI.RawUI.ReadKey("AllowCtrlC,IncludeKeyDown,IncludeKeyUp,NoEcho").Character
    if ("p" -eq $k) {
        #Update the displaying
        $HOST.UI.RawUI.Flushinputbuffer() #Flush the key buffer
    } else {
        if ("r" -eq $k) {
            #Update the displaying
            $HOST.UI.RawUI.Flushinputbuffer()
        } else {
            if ("m" -eq $k) {
                #Update the displaying
                $HOST.UI.RawUI.Flushinputbuffer()
            } else {
                if ("n" -eq $k) {
                    #Update the displaying
                    $HOST.UI.RawUI.Flushinputbuffer()
                } else {
                    if ($k -eq "q") { #my wish was to map CTRL+C here but no way.
                        #Do the cleanup
                        Write-Host "Exiting...." -Background DarkRed
                        Stop-Job -Job $j
                        $null = Receive-Job $j
                        Remove-Job $j
                        ...
                        break;
                    }
                }
            }
        }
    }
}
       
The problem with proper solution is that one has to register his own functions and callbacks (system.console.cancelkeypress, system.console.canceleventhandler, set application-defined HandlerRoutine, install a control handler ...) which was just too much for the scope of my task especially since the library implementing desired behavior exists (PSEventing).

I do feel PowerShell should address this some time in the future through Register-Event cmdlets.

No comments:

Post a Comment