2026-03-28 17:14:39 +00:00
|
|
|
//go:build windows
|
|
|
|
|
|
|
|
|
|
package pid
|
|
|
|
|
|
|
|
|
|
import (
|
2026-06-04 09:50:54 +00:00
|
|
|
"strings"
|
2026-03-28 17:14:39 +00:00
|
|
|
"syscall"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
|
|
|
procOpenProcess = kernel32.NewProc("OpenProcess")
|
|
|
|
|
procGetExitCodeProcess = kernel32.NewProc("GetExitCodeProcess")
|
|
|
|
|
procCloseHandle = kernel32.NewProc("CloseHandle")
|
2026-06-04 09:50:54 +00:00
|
|
|
procQueryFullProcessImageNameW = kernel32.NewProc("QueryFullProcessImageNameW")
|
2026-03-28 17:14:39 +00:00
|
|
|
processQueryLimitedInformation = uint32(0x1000)
|
|
|
|
|
stillActive = uint32(259)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// isProcessRunning checks whether a process with the given PID is alive
|
|
|
|
|
// on Windows using OpenProcess + GetExitCodeProcess.
|
|
|
|
|
func isProcessRunning(pid int) bool {
|
|
|
|
|
if pid <= 0 {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 07:46:45 +00:00
|
|
|
handle, _, _ := procOpenProcess.Call(
|
2026-03-28 17:14:39 +00:00
|
|
|
uintptr(processQueryLimitedInformation),
|
|
|
|
|
0,
|
|
|
|
|
uintptr(pid),
|
|
|
|
|
)
|
2026-04-07 07:46:45 +00:00
|
|
|
if handle == 0 {
|
2026-03-28 17:14:39 +00:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
defer procCloseHandle.Call(handle)
|
|
|
|
|
|
|
|
|
|
var exitCode uint32
|
2026-04-07 07:46:45 +00:00
|
|
|
ret, _, _ := procGetExitCodeProcess.Call(handle, uintptr(unsafe.Pointer(&exitCode)))
|
|
|
|
|
if ret == 0 {
|
2026-03-28 17:14:39 +00:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return exitCode == stillActive
|
|
|
|
|
}
|
2026-06-04 09:50:54 +00:00
|
|
|
|
|
|
|
|
// isPicoclawProcess uses QueryFullProcessImageNameW to confirm the
|
|
|
|
|
// process image name contains "picoclaw". Returns false when the name
|
|
|
|
|
// clearly does not match. Returns true if the query fails, falling
|
|
|
|
|
// back to trusting the liveness check alone.
|
|
|
|
|
func isPicoclawProcess(pid int) bool {
|
|
|
|
|
handle, _, _ := procOpenProcess.Call(
|
|
|
|
|
uintptr(processQueryLimitedInformation),
|
|
|
|
|
0,
|
|
|
|
|
uintptr(pid),
|
|
|
|
|
)
|
|
|
|
|
if handle == 0 {
|
|
|
|
|
return true // cannot open — trust liveness check
|
|
|
|
|
}
|
|
|
|
|
defer procCloseHandle.Call(handle)
|
|
|
|
|
|
|
|
|
|
var buf [260]uint16
|
|
|
|
|
var size uint32 = 260
|
|
|
|
|
ret, _, _ := procQueryFullProcessImageNameW.Call(
|
|
|
|
|
uintptr(handle),
|
|
|
|
|
0, // WIN32_NAME_FORMAT
|
|
|
|
|
uintptr(unsafe.Pointer(&buf[0])),
|
|
|
|
|
uintptr(unsafe.Pointer(&size)),
|
|
|
|
|
)
|
|
|
|
|
if ret == 0 {
|
|
|
|
|
return true // cannot verify — trust liveness check
|
|
|
|
|
}
|
|
|
|
|
name := strings.ToLower(syscall.UTF16ToString(buf[:size]))
|
|
|
|
|
return strings.Contains(name, "picoclaw")
|
|
|
|
|
}
|