//go:build windows package procscan import ( "fmt" "path/filepath" "syscall" "golang.org/x/sys/windows" ) // ResolvePID returns the exe basename for a given PID, or an error // if the PID has already exited or we lack the rights to query it. // // Used by the engine's socketLoop to do a lazy lookup when SOCKET // Connect events arrive for processes we haven't yet seen via the // 2-second procscan tick — Update.exe's full lifecycle (spawn → // connect → exit) routinely fits inside one tick window, so without // this lookup the engine would miss its connections entirely and // Discord's "Checking for updates" would hit its 30 s timeout. func ResolvePID(pid uint32) (string, error) { h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid) if err != nil { return "", fmt.Errorf("OpenProcess pid=%d: %w", pid, err) } defer windows.CloseHandle(h) buf := make([]uint16, windows.MAX_PATH) size := uint32(len(buf)) if err := windows.QueryFullProcessImageName(h, 0, &buf[0], &size); err != nil { return "", fmt.Errorf("QueryFullProcessImageName pid=%d: %w", pid, err) } full := syscall.UTF16ToString(buf[:size]) return filepath.Base(full), nil }