Skip to content

Instantly share code, notes, and snippets.

@inkychris
Last active November 8, 2022 19:19
Show Gist options
  • Save inkychris/e9b7bd1eb4caf710cc60c53a8d6f4bfb to your computer and use it in GitHub Desktop.
Save inkychris/e9b7bd1eb4caf710cc60c53a8d6f4bfb to your computer and use it in GitHub Desktop.
Timeout process wrapper for Powershell
param(
[int][Parameter(Mandatory)]$Timeout,
[int][Parameter(Mandatory)]$KillTimeout,
[string[]][Parameter(Mandatory, ValueFromRemainingArguments)]$Arguments
)
$ErrorActionPreference = "stop"
$ArgumentsString = $Arguments[1..$Arguments.Length] -replace "^|$", "`"" -join " "
$timeout_reached = $null
$process = Start-Process $Arguments[0] -NoNewWindow -PassThru -ArgumentList $ArgumentsString
$process | Wait-Process -Timeout $Timeout -ErrorAction SilentlyContinue -ErrorVariable timeout_reached
if ($timeout_reached) {
Write-Host "[$($process.id)] ${Timeout}s timeout reached: sending ctrl-C event ..." -ForegroundColor DarkYellow
Start-Job -ArgumentList $process.id -ScriptBlock {
param([int]$process_id)
write-host "job pid $process_id"
$MemberDefinition = '
[DllImport("kernel32.dll")]public static extern bool FreeConsole();
[DllImport("kernel32.dll")]public static extern bool AttachConsole(uint p);
[DllImport("kernel32.dll")]public static extern bool GenerateConsoleCtrlEvent(uint e, uint p);
public static void SendCtrlC(uint p) {
FreeConsole();
if (AttachConsole(p)) {
GenerateConsoleCtrlEvent(0, p);
FreeConsole();
}
AttachConsole(uint.MaxValue);
}'
Add-Type -Name 'Cleanup' -Namespace 'Process' -MemberDefinition $MemberDefinition
[Process.Cleanup]::SendCtrlC($process_id)
} | Out-Null
}
$kill_timeout_reached = $null
$process | Wait-Process -Timeout $KillTimeout -ErrorAction SilentlyContinue -ErrorVariable kill_timeout_reached
if ($kill_timeout_reached) {
Write-Host "[$($process.id)] ${KillTimeout}s kill-timeout reached: killing process ..." -ForegroundColor DarkRed
$process.kill($true)
exit 1
}
exit $process.ExitCode
@inkychris
Copy link
Author

Huge thanks to everyone in this Stackoverflow thread that provided all the important bits!

I'd gotten stuck for a while with having the calling Powershell process being terminated too, until I tried calling SendCtrlC from a separate process too. Got that idea from the Python solution, which was spawning a subprocess for the script.

To properly work correctly with any sub-process, this script should be called like so:

> timeout.ps1 -Timeout 60 -KillTimeout 5 -- <program> <args...<>>

to avoid powershell trying and sometimes failing to parse program arguments, e.g. -i.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment