//go:build windows package winapi import ( "unsafe" "golang.org/x/sys/windows" ) var ( user32 = windows.NewLazySystemDLL("user32.dll") procGetWindowTextW = user32.NewProc("GetWindowTextW") procShowWindow = user32.NewProc("ShowWindow") ) // ForegroundWindow returns the current foreground window's handle (as a uintptr // so platform-neutral callers need not import windows), its title, and its // on-task class (process exe base name; see ClassFromImagePath). ok is false // when GetForegroundWindow returns null, which happens transiently during focus // changes and some secure-desktop transitions (UAC / lock screen). func ForegroundWindow() (hwnd uintptr, title, class string, ok bool) { h := windows.GetForegroundWindow() if h == 0 { return 0, "", "", false } return uintptr(h), windowTitle(h), windowClass(h), true } func windowTitle(h windows.HWND) string { // 512 uint16s; titles over 511 chars are truncated — acceptable for a sensor tag. const max = 512 buf := make([]uint16, max) n, _, _ := procGetWindowTextW.Call( uintptr(h), uintptr(unsafe.Pointer(&buf[0])), uintptr(max), ) // n==0 covers both call failure and a genuinely empty title; both are fine here. return windows.UTF16ToString(buf[:n]) } func windowClass(h windows.HWND) string { var pid uint32 if _, err := windows.GetWindowThreadProcessId(h, &pid); err != nil || pid == 0 { return "" } proc, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid) if err != nil { return "" } defer windows.CloseHandle(proc) buf := make([]uint16, windows.MAX_PATH) size := uint32(len(buf)) // MAX_PATH suffices for almost all paths; an over-long path fails here and // degrades windowClass to "" (window left unclassified), never a crash. if err := windows.QueryFullProcessImageName(proc, 0, &buf[0], &size); err != nil { return "" } return ClassFromImagePath(windows.UTF16ToString(buf[:size])) } // MinimizeForeground minimizes the current foreground window. It returns nil // when nothing is focused. ShowWindow's BOOL return reports prior visibility, // not success, so there is nothing to error-check; minimize is best-effort by // the Guard contract. func MinimizeForeground() error { h := windows.GetForegroundWindow() if h == 0 { return nil } procShowWindow.Call(uintptr(h), uintptr(windows.SW_MINIMIZE)) return nil }