Files

76 lines
2.3 KiB
Go

//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
}