Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Listen for a key press with Powershell, but don't wait for it

Tags:

powershell

I'm trying to write a script that toggles the caps lock key periodically, but I also want to be able to toggle the script because it causes some issues with other functionality (like alt tabbing, and rolling over windows in the task bar to see a preview)

The script I have so far is

:outer while($true){
    do{ 
    echo "Toggle with F12";
    $x = [System.Console]::ReadKey() 
    }while( $x.Key -ne "F12" )
    while($true){
        $wsh = New-Object -ComObject WScript.Shell
        $wsh.SendKeys('{CAPSLOCK}')
        sleep 0.3
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wsh)
        Remove-Variable wsh
        if ($Host.UI.RawUI.KeyAvailable) {
            $key = $Host.UI.RawUI.ReadKey()
            if($key.Key -ne "F12"){
                break outer
            }
        }
    }
}

The script waits for the user to press F12, and then once they press F12 I want it to start toggling the caps lock key ever 0.3 seconds until the user presses F12 again. Ideally, I want this to happen in the background, but I don't know if that's possible. I don't want the user to have to have the console window open to toggle the script.

The way this runs now, after the user presses F12 the script will toggle capslock once and then exit. If I remove the conditional after Remove-Variable, the script will run as I want it to except the only way it can be stopped is if the console window is closed.

like image 954
ozzymado Avatar asked Oct 27 '14 17:10

ozzymado


2 Answers

Here is a try. BTW I changed the sleep to 1 second so it doesn't flash capslock too much while testing:

$continue = $true
while($continue)
{

    if ([console]::KeyAvailable)
    {
        echo "Toggle with F12";
        $x = [System.Console]::ReadKey() 

        switch ( $x.key)
        {
            F12 { $continue = $false }
        }
    } 
    else
    {
        $wsh = New-Object -ComObject WScript.Shell
        $wsh.SendKeys('{CAPSLOCK}')
        sleep 1
        [System.Runtime.Interopservices.Marshal]::ReleaseComObject($wsh)| out-null
        Remove-Variable wsh
    }    
}
like image 50
Micky Balladelli Avatar answered Nov 12 '22 02:11

Micky Balladelli


add-type -Path '.\documents\WindowsPowerShell\VISE_WinKeyboardHook.dll'
$KeyboardInterceptor = new-object VISE.WinKeyboardHook.KeyboardInterceptor
function HandleKeyDown($keyId)
{
    write-host $keyID.KeyCode
    if($keyID.KeyCode -eq "Escape"){
        $KeyboardInterceptor.StopCapturing()
    }
}
Unregister-Event -SourceIdentifier KeyDown -ErrorAction SilentlyContinue
$keyevent = Register-ObjectEvent -InputObject $KeyboardInterceptor -EventName KeyDown -SourceIdentifier KeyDown -Action {HandleKeyDown($event.sourceEventArgs)}
$KeyboardInterceptor.StartCapturing()

Here is a C# assembly which provides event for global keyboard events. https://ianmorrish.wordpress.com/v-ise/keyboard-hook/ Advantage of this is that it is non-blocking and also works in ISE.

like image 36
Ian Morrish Avatar answered Nov 12 '22 01:11

Ian Morrish