这在大多数具有简单系统调用的操作系统上都是可能的,在 Go 中,您可以使用包 syscall 进行系统调用,而无需任何额外的 C 代码或 cgo 编译器。
注意syscall包的在线官方文档只显示了linux界面。其他操作系统的界面略有不同,例如在 Windows 上,syscall 包还包含 syscall.DLL 类型、syscall.LoadDLL() 和 syscall.MustLoadDLL() 函数等。要查看这些,请在本地运行 godoc 工具,例如
godoc -http=:6060
这将启动一个托管类似于godoc.org 的网页的网络服务器,导航到http://localhost:6060/pkg/syscall/。也可以在线查看特定平台的文档,详情见How to access platform specific package documentation?
在这里,我展示了一个完整的、可运行的纯 Go 的 Windows 解决方案。 Go Playground 上提供了完整的示例应用程序。它不在操场上运行,下载并在本地运行(在 Windows 上)。
让我们定义类型来描述我们想要使用的热键。这不是特定于 Windows 的,只是为了使我们的代码更好。 Hotkey.String() 方法提供了一个人性化的热键显示名称,例如 "Hotkey[Id: 1, Alt+Ctrl+O]"。
const (
ModAlt = 1 << iota
ModCtrl
ModShift
ModWin
)
type Hotkey struct {
Id int // Unique id
Modifiers int // Mask of modifiers
KeyCode int // Key code, e.g. 'A'
}
// String returns a human-friendly display name of the hotkey
// such as "Hotkey[Id: 1, Alt+Ctrl+O]"
func (h *Hotkey) String() string {
mod := &bytes.Buffer{}
if h.Modifiers&ModAlt != 0 {
mod.WriteString("Alt+")
}
if h.Modifiers&ModCtrl != 0 {
mod.WriteString("Ctrl+")
}
if h.Modifiers&ModShift != 0 {
mod.WriteString("Shift+")
}
if h.Modifiers&ModWin != 0 {
mod.WriteString("Win+")
}
return fmt.Sprintf("Hotkey[Id: %d, %s%c]", h.Id, mod, h.KeyCode)
}
Windows user32.dll 包含全局热键管理功能。让我们加载它:
user32 := syscall.MustLoadDLL("user32")
defer user32.Release()
它有一个RegisterHotkey()函数用于注册全局热键:
reghotkey := user32.MustFindProc("RegisterHotKey")
使用这个,让我们注册一些热键,即ALT+CTRL+O、ALT+SHIFT+M、ALT+CTRL+X (将用于退出应用程序):
// Hotkeys to listen to:
keys := map[int16]*Hotkey{
1: &Hotkey{1, ModAlt + ModCtrl, 'O'}, // ALT+CTRL+O
2: &Hotkey{2, ModAlt + ModShift, 'M'}, // ALT+SHIFT+M
3: &Hotkey{3, ModAlt + ModCtrl, 'X'}, // ALT+CTRL+X
}
// Register hotkeys:
for _, v := range keys {
r1, _, err := reghotkey.Call(
0, uintptr(v.Id), uintptr(v.Modifiers), uintptr(v.KeyCode))
if r1 == 1 {
fmt.Println("Registered", v)
} else {
fmt.Println("Failed to register", v, ", error:", err)
}
}
我们需要一种方法来“监听”按下这些热键的事件。为此,user32.dll 包含 PeekMessage() 函数:
peekmsg := user32.MustFindProc("PeekMessageW")
PeekMessage() 将消息存储到 MSG 结构中,让我们定义它:
type MSG struct {
HWND uintptr
UINT uintptr
WPARAM int16
LPARAM int64
DWORD int32
POINT struct{ X, Y int64 }
}
现在这是我们的监听循环,它监听并作用于全局按键(这里只是简单地将按下的热键打印到控制台,如果按下 CTRL+ALT+X,则退出应用程序) :
for {
var msg = &MSG{}
peekmsg.Call(uintptr(unsafe.Pointer(msg)), 0, 0, 0, 1)
// Registered id is in the WPARAM field:
if id := msg.WPARAM; id != 0 {
fmt.Println("Hotkey pressed:", keys[id])
if id == 3 { // CTRL+ALT+X = Exit
fmt.Println("CTRL+ALT+X pressed, goodbye...")
return
}
}
time.Sleep(time.Millisecond * 50)
}
我们完成了!
启动上述应用程序打印:
Registered Hotkey[Id: 1, Alt+Ctrl+O]
Registered Hotkey[Id: 2, Alt+Shift+M]
Registered Hotkey[Id: 3, Alt+Ctrl+X]
现在让我们按下一些已注册的热键(任何应用都处于焦点位置,不一定是我们的应用),我们将在控制台上看到:
Hotkey pressed: Hotkey[Id: 1, Alt+Ctrl+O]
Hotkey pressed: Hotkey[Id: 1, Alt+Ctrl+O]
Hotkey pressed: Hotkey[Id: 2, Alt+Shift+M]
Hotkey pressed: Hotkey[Id: 3, Alt+Ctrl+X]
CTRL+ALT+X pressed, goodbye...