Search code examples
multithreadingpowershellresource-cleanup

How do I detect Ctrl-C in PowerShell in a responsive way?


When I break out of a PowerShell 7 script using Ctrl-C I'll get any jobs running, left running. I might also leave files I've been working with in state that is not desirable.

I did find a way to detect the key press, but either it's consuming to much CPU or is very unresponsive (or both).

See

When using the example below, the detection of Ctrl-C is very sluggish or will consume a lot of CPU resources (or both).

[Console]::TreatControlCAsInput = $true
sleep 1
$Host.UI.RawUI.FlushInputBuffer()

Start-ThreadJob {# Do something
  sleep 5
} -Name 'SomeJob'

Start-ThreadJob {# Do another thing
  sleep 5
} -Name 'OtherJob'

while (Get-Job | where State -ne 'Stopped') {# Run the main loop

  if (# Some key was pressed
    $Host.UI.RawUI.KeyAvailable -and
    ($Key = $Host.UI.RawUI.ReadKey("AllowCtrlC,NoEcho,IncludeKeyUp"))
  ) {
    if ([int]$Key.Character -eq 3) {# Ctrl-C pressed
      Get-Job | Remove-Job -Force

      [Console]::TreatControlCAsInput = $false

      return

    }#end Ctrl-C pressed

  }#end Some key was pressed

  # sleep -Milliseconds 200
  $Host.UI.RawUI.FlushInputBuffer()
  
}

Is there a good way to increase the responsiveness in the key detection?


Solution

  • I went with the solution of only stopping the jobs.

    Get-Job | Remove-Job -Force # Clean-up jobs from previous runs
    try {
        $jJobs = @(
            Start-ThreadJob {
                Start-Sleep 10
            } -Name 'SomeJob'
    
            Start-ThreadJob {
                Start-Sleep 10
            } -Name 'OtherJob'
        )
    
        $Jobs | Receive-Job -Keep -Wait # Keep output for trouble shooting
    }
    # this block will always run, even on CTRL+C
    finally {
        $Jobs | Stop-Job
    }
    
    Get-Job # should not contain any running jobs