3030package paths
3131
3232import (
33+ "fmt"
3334 "os/exec"
3435 "syscall"
36+ "unsafe"
37+
38+ "golang.org/x/sys/windows"
3539)
3640
3741func tellCommandNotToSpawnShell (oscmd * exec.Cmd ) {
@@ -46,5 +50,41 @@ func tellCommandToStartOnNewProcessGroup(_ *exec.Cmd) {
4650}
4751
4852func kill (oscmd * exec.Cmd ) error {
49- return oscmd .Process .Kill ()
53+ return killProcessTree (uint32 (oscmd .Process .Pid ))
54+ }
55+
56+ func killProcessTree (pid uint32 ) error {
57+ // Inspired by: https://stackoverflow.com/a/36089871/1655275
58+
59+ // Make a snapshot of the current running processes
60+ snapshot , err := windows .CreateToolhelp32Snapshot (windows .TH32CS_SNAPPROCESS , 0 )
61+ if err != nil {
62+ return fmt .Errorf ("getting running processes snapshot: %w" , err )
63+ }
64+
65+ // Iterate and filter child processes of the current process
66+ var processEntry windows.ProcessEntry32
67+ processEntry .Size = uint32 (unsafe .Sizeof (processEntry ))
68+ if windows .Process32First (snapshot , & processEntry ) == nil {
69+ for {
70+ if processEntry .ParentProcessID == pid {
71+ // Recurse on the childrens
72+ if err := killProcessTree (processEntry .ProcessID ); err != nil {
73+ return fmt .Errorf ("error killing child process: %w" , err )
74+ }
75+ }
76+
77+ if windows .Process32Next (snapshot , & processEntry ) != nil {
78+ break
79+ }
80+ }
81+ }
82+
83+ // And finally, kill the parent
84+ process , err := windows .OpenProcess (windows .PROCESS_ALL_ACCESS , false , pid )
85+ if err != nil {
86+ return fmt .Errorf ("opening process for kill: %w" , err )
87+ }
88+ defer windows .CloseHandle (process )
89+ return windows .TerminateProcess (process , 128 )
5090}
0 commit comments