//go:build windows // +build windows package main import ( "errors" "os" "syscall" "time" "unsafe" ) const ( processQueryLimitedInformation = 0x1000 stillActive = 259 // STILL_ACTIVE exit code ) var ( findProcess = os.FindProcess kernel32 = syscall.NewLazyDLL("kernel32.dll") getProcessTimes = kernel32.NewProc("GetProcessTimes") fileTimeToUnixFn = fileTimeToUnix ) // isProcessRunning returns true if a process with the given pid is running on Windows. func isProcessRunning(pid int) bool { if pid <= 0 { return false } if _, err := findProcess(pid); err != nil { return false } handle, err := syscall.OpenProcess(processQueryLimitedInformation, false, uint32(pid)) if err != nil { if errors.Is(err, syscall.ERROR_ACCESS_DENIED) { return true } return false } defer syscall.CloseHandle(handle) var exitCode uint32 if err := syscall.GetExitCodeProcess(handle, &exitCode); err != nil { return true } return exitCode == stillActive } // getProcessStartTime returns the start time of a process on Windows. // Returns zero time if the start time cannot be determined. func getProcessStartTime(pid int) time.Time { if pid <= 0 { return time.Time{} } handle, err := syscall.OpenProcess(processQueryLimitedInformation, false, uint32(pid)) if err != nil { return time.Time{} } defer syscall.CloseHandle(handle) var creationTime, exitTime, kernelTime, userTime syscall.Filetime ret, _, _ := getProcessTimes.Call( uintptr(handle), uintptr(unsafe.Pointer(&creationTime)), uintptr(unsafe.Pointer(&exitTime)), uintptr(unsafe.Pointer(&kernelTime)), uintptr(unsafe.Pointer(&userTime)), ) if ret == 0 { return time.Time{} } return fileTimeToUnixFn(creationTime) } // fileTimeToUnix converts Windows FILETIME to Unix time. func fileTimeToUnix(ft syscall.Filetime) time.Time { // FILETIME is 100-nanosecond intervals since January 1, 1601 UTC nsec := ft.Nanoseconds() return time.Unix(0, nsec) }