picoclaw/pkg/pid/pidfile_windows.go

75 lines
1.9 KiB
Go
Raw Normal View History

//go:build windows
package pid
import (
"strings"
"syscall"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procOpenProcess = kernel32.NewProc("OpenProcess")
procGetExitCodeProcess = kernel32.NewProc("GetExitCodeProcess")
procCloseHandle = kernel32.NewProc("CloseHandle")
procQueryFullProcessImageNameW = kernel32.NewProc("QueryFullProcessImageNameW")
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
}
handle, _, _ := procOpenProcess.Call(
uintptr(processQueryLimitedInformation),
0,
uintptr(pid),
)
if handle == 0 {
return false
}
defer procCloseHandle.Call(handle)
var exitCode uint32
ret, _, _ := procGetExitCodeProcess.Call(handle, uintptr(unsafe.Pointer(&exitCode)))
if ret == 0 {
return false
}
return exitCode == stillActive
}
// 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")
}