我正在尝试编写一个简单的 Go 程序,该程序将在后台监听全局 Windows 热键,并在按下特定热键时发送 API 调用。这是我第一次学习如何使用 Windows 消息,所以这可能只是我不理解它们是如何工作的。
即使按下了热键,我对 PeekMessageW 的调用也经常返回 0,然后大约一分钟后突然立即返回所有值。这只是预期的行为吗?
我在同一个线程上做所有事情。首先,我创建一个新窗口并保存 HWND,如下所示:
func createWindow(
user32 *syscall.DLL,
dwExStyle uint32,
lpClassName, lpWindowName *uint16,
dwStyle uint32,
x, y, nWidth, nHeight int,
hWndParent, hMenu, hInstance uintptr,
lpParam unsafe.Pointer,
) uintptr {
procCreateWindowEx := user32.MustFindProc("CreateWindowExW")
ret, _, _ := procCreateWindowEx.Call(
uintptr(dwExStyle),
uintptr(unsafe.Pointer(lpClassName)),
uintptr(unsafe.Pointer(lpWindowName)),
uintptr(dwStyle),
uintptr(x),
uintptr(y),
uintptr(nWidth),
uintptr(nHeight),
hWndParent,
hMenu,
hInstance,
uintptr(lpParam),
)
return ret
}
func GiveSimpleWindowPls(user32 *syscall.DLL) uintptr {
var hwnd uintptr
className, _ := syscall.UTF16PtrFromString("STATIC")
windowName, _ := syscall.UTF16PtrFromString("Simple Window")
hwnd = createWindow(
user32,
0,
className,
windowName,
0,
0,
0,
0,
0,
0,
0,
0,
nil,
)
if hwnd != 0 {
fmt.Println("HWND:", hwnd)
} else {
fmt.Println("Could not create window")
}
return hwnd
}
然后我注册我的热键:
func Register(user32 *syscall.DLL, hwnd uintptr) (map[int]*Hotkey, error) {
reghotkey := user32.MustFindProc("RegisterHotKey")
// Hotkeys to listen to:
// (hardcoded for now)
keys := map[int]*Hotkey{
6: {6, ModAlt + ModCtrl + ModShift, '6'},
7: {7, ModAlt + ModCtrl + ModShift, '7'},
8: {8, ModAlt + ModCtrl + ModShift, '8'},
9: {9, ModAlt + ModCtrl + ModShift, '9'},
10: {10, ModAlt + ModCtrl + ModShift, '0'},
}
// Register hotkeys:
for _, v := range keys {
r1, _, err := reghotkey.Call(hwnd, uintptr(v.Id), uintptr(v.Modifiers), uintptr(v.KeyCode))
if r1 != 1 {
return nil, fmt.Errorf("error registering hotkey %#v: %w", v, err)
}
}
return keys, nil
}
最后循环调用PeekMessageW:
const WM_HOTKEY = 0x0312
func Listen(keys map[int]*Hotkey, switcher Switcher, hwnd uintptr) {
peekmsg := user32.MustFindProc("PeekMessageW")
for {
var msg = &MSG{}
a, _, _ := peekmsg.Call(uintptr(unsafe.Pointer(msg)), hwnd, WM_HOTKEY, WM_HOTKEY, 1)
fmt.Printf("%#v %d \n", msg, a)
if a == 0 {
time.Sleep(time.Millisecond * 50)
continue
}
if key, ok := keys[int(msg.WPARAM)]; ok {
fmt.Println("Hotkey was pressed:", key)
switcher.Switch(key.Id) // for now this just does nothing and returns
}
}
}
起初这似乎工作得很好,但每隔一分钟左右,即使我按下热键,PeekMessageW 也开始只返回 0。然后过了一会儿,它就一个接一个地返回所有消息,就好像队列拥塞了一段时间,终于被释放了。
我期望在按下热键后能够立即(或至少在几秒钟内)查看 WM_HOTKEY 消息,这是错误的吗?该程序有时会一次停止接收这些消息长达一分钟,然后突然一次处理所有消息。队列是否被我运行的 Windows 优先处理的其他进程阻塞?
这是我第一次尝试弄清楚这是如何工作的,但我花了几个小时在网上搜索解决方案,但看不出哪里出了问题。
我似乎已经解决了这个问题。我认为我调用 PeekMessageW 的频率太高(每 50 毫秒),因为将睡眠时间增加到 500 可以解决问题。它的响应速度不是很好,但就我的目的而言,它做得很好。