Author Posts

January 10, 2017 at 5:15 pm

Hi

I wrote a function that check if you are pressing or pressed Ctrl-Q from the last time the function run and if so exit the script

This is useful for example if you have a long loop and you want to have the option to stop it by pressing a key but unlike Ctrl-C do it in a controlled position at the end of the loop

So you can click Ctrl-Q, the loop will continue as normal until the end of the current loop and then stop

I used the [console]/$Host methods KeyAvailable and ReadKey() and it's working grate but not in Powershell ISE
so I fund a workaround for ISE by using GetAsyncKeyState() instead in it

But the problem now is that if I Enter-PSSession this two solutions not working in there, GetAsyncKeyState() always return 0 and ReadKey return "Cannot see if a key has been pressed when either application does not have a console or when console input has been redirected from a file. Try Console.In.Peek."

function Stop-IfKey
{
    
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$false, Position=0)]
        [Switch]
        $InitializeIse
    )
    if ($InitializeIse)
    {
        if (!('Win32Functions.Win32GetAsyncKeyState' -as [type]))
        {
            $Signature = '[DllImport("user32.dll")]public static extern short GetAsyncKeyState(int vKey);'
            Add-Type -MemberDefinition $Signature -Name 'Win32GetAsyncKeyState' -Namespace Win32Functions
            Add-Type -AssemblyName System.Windows.Forms
        }
        $null = [Win32Functions.Win32GetAsyncKeyState]::GetAsyncKeyState([Windows.Forms.Keys]::LControlKey)
        $null = [Win32Functions.Win32GetAsyncKeyState]::GetAsyncKeyState([Windows.Forms.Keys]::RControlKey)
        $null = [Win32Functions.Win32GetAsyncKeyState]::GetAsyncKeyState([Windows.Forms.Keys]::Q)
        Return
    }
    
    $QuittingMassage = 'Quitting, user pressed control Q...'
    if ($Host.Name -notin 'Windows PowerShell ISE Host')
    {
        #[console]::TreatControlCAsInput = $true # For Ctrl-C
        if ([console]::KeyAvailable) # Or ($Host.UI.RawUI.KeyAvailable)
        {
            $key = [console]::readkey($true) # Or $key = $Host.UI.RawUI.ReadKey()
            if (($key.modifiers -band 'control') -and ($key.key -eq 'Q'))
            {
                #Write-Warning -Message $QuittingMassage
                #Exit
                throw $QuittingMassage
            }
        }
    }
    else
    {
        if (([Win32Functions.Win32GetAsyncKeyState]::GetAsyncKeyState([Windows.Forms.Keys]::LControlKey) -or [Win32Functions.Win32GetAsyncKeyState]::GetAsyncKeyState([Windows.Forms.Keys]::RControlKey)) -and [Win32Functions.Win32GetAsyncKeyState]::GetAsyncKeyState([Windows.Forms.Keys]::Q))
        {
            #Write-Warning -Message $QuittingMassage
            #Exit
            throw $QuittingMassage
        }
    }
}
Stop-IfKey -InitializeIse

$collection = @(1..5)
foreach ($item in $collection)
{
    'Loop {0}' -f $item
    Start-Sleep -Seconds 5 # Do stuff
    Stop-IfKey
}
'End'

January 14, 2017 at 7:03 pm

You're not going to be able to duplicate this. PSSessions aren't like a Telnet or SSH session; you're still typing in your local console until you hit Enter, at which point the remote process runs. There's no "console connection" per se.