Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
go lang keyboard capture
package main
import (
"fmt"
"syscall"
//~ "time"
"unsafe"
"golang.org/x/sys/windows"
)
// String returns a human-friendly display name of the hotkey
// such as "Hotkey[Id: 1, Alt+Ctrl+O]"
var (
user32 = windows.NewLazySystemDLL("user32.dll")
procSetWindowsHookEx = user32.NewProc("SetWindowsHookEx")
procLowLevelKeyboard = user32.NewProc("LowLevelKeyboardProc")
procCallNextHookEx = user32.NewProc("CallNextHookEx")
procUnhookWindowsHookEx = user32.NewProc("UnhookWindowsHookEx")
procGetMessage = user32.NewProc("GetMessageW")
procTranslateMessage = user32.NewProc("TranslateMessage")
procDispatchMessage = user32.NewProc("DispatchMessageW")
keyboardHook HHOOK
)
const (
WH_KEYBOARD_LL = 13
WH_KEYBOARD = 2
WM_KEYDOWN = 256
WM_SYSKEYDOWN = 260
WM_KEYUP = 257
WM_SYSKEYUP = 261
WM_KEYFIRST = 256
WM_KEYLAST = 264
PM_NOREMOVE = 0x000
PM_REMOVE = 0x001
PM_NOYIELD = 0x002
WM_LBUTTONDOWN = 513
WM_RBUTTONDOWN = 516
NULL = 0
)
type (
DWORD uint32
WPARAM uintptr
LPARAM uintptr
LRESULT uintptr
HANDLE uintptr
HINSTANCE HANDLE
HHOOK HANDLE
HWND HANDLE
)
type HOOKPROC func(int, WPARAM, LPARAM) LRESULT
type KBDLLHOOKSTRUCT struct {
VkCode DWORD
ScanCode DWORD
Flags DWORD
Time DWORD
DwExtraInfo uintptr
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162805.aspx
type POINT struct {
X, Y int32
}
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644958.aspx
type MSG struct {
Hwnd HWND
Message uint32
WParam uintptr
LParam uintptr
Time uint32
Pt POINT
}
func SetWindowsHookEx(idHook int, lpfn HOOKPROC, hMod HINSTANCE, dwThreadId DWORD) HHOOK {
ret, _, _ := procSetWindowsHookEx.Call(
uintptr(idHook),
uintptr(syscall.NewCallback(lpfn)),
uintptr(hMod),
uintptr(dwThreadId),
)
return HHOOK(ret)
}
func CallNextHookEx(hhk HHOOK, nCode int, wParam WPARAM, lParam LPARAM) LRESULT {
ret, _, _ := procCallNextHookEx.Call(
uintptr(hhk),
uintptr(nCode),
uintptr(wParam),
uintptr(lParam),
)
return LRESULT(ret)
}
func UnhookWindowsHookEx(hhk HHOOK) bool {
ret, _, _ := procUnhookWindowsHookEx.Call(
uintptr(hhk),
)
return ret != 0
}
func GetMessage(msg *MSG, hwnd HWND, msgFilterMin uint32, msgFilterMax uint32) int {
ret, _, _ := procGetMessage.Call(
uintptr(unsafe.Pointer(msg)),
uintptr(hwnd),
uintptr(msgFilterMin),
uintptr(msgFilterMax))
return int(ret)
}
func TranslateMessage(msg *MSG) bool {
ret, _, _ := procTranslateMessage.Call(
uintptr(unsafe.Pointer(msg)))
return ret != 0
}
func DispatchMessage(msg *MSG) uintptr {
ret, _, _ := procDispatchMessage.Call(
uintptr(unsafe.Pointer(msg)))
return ret
}
func LowLevelKeyboardProc (nCode int, wParam WPARAM, lParam LPARAM) LRESULT {
ret, _, _ := procLowLevelKeyboard.Call(
uintptr(nCode),
uintptr(wParam),
uintptr(lParam),
)
return LRESULT(ret)
}
func Start() {
// defer user32.Release()
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,
(HOOKPROC)(func(nCode int, wparam WPARAM, lparam LPARAM) LRESULT {
if nCode == 0 && wparam == WM_KEYDOWN {
fmt.Println("key pressed:")
kbdstruct := (*KBDLLHOOKSTRUCT)(unsafe.Pointer(lparam))
code := byte(kbdstruct.VkCode)
fmt.Sprintf("%q", code)
}
return CallNextHookEx(keyboardHook, nCode, wparam, lparam)
}), 0, 0)
var msg MSG
for GetMessage(&msg, 0, 0, 0) != 0 {
}
//~ for {
//~
//~ GetMessage(uintptr(unsafe.Pointer(msg)), 0, 0, 0)
//~ TranslateMessage(msg)
//~ DispatchMessage(msg)
//~ // fmt.Println("key pressed:")
//~
//~ }
UnhookWindowsHookEx(keyboardHook)
keyboardHook = 0
}
func main() {
go Start()
}
@jordansissel

This comment has been minimized.

Copy link

@jordansissel jordansissel commented Sep 25, 2017

Hey! Thank you for sharing this.

I had to make two small modifications to make this work for me under Go 1.9 on Windows 10:

  • Change SetWindowsHookEx to SetWindowsHookExA (SetWindowsHookExW probably also works). Without this change, the following would panic:

panic: Failed to find SetWindowsHookEx procedure in user32.dll: The specified procedure could not be found.

-	procSetWindowsHookEx    = user32.NewProc("SetWindowsHookEx")
+	procSetWindowsHookEx    = user32.NewProc("SetWindowsHookExA")
  • See the key by calling Printf instead of Sprintf
-				fmt.Sprintf("%q", code)
+				fmt.Printf("%q", code)
@janpfeifer

This comment has been minimized.

Copy link

@janpfeifer janpfeifer commented Jan 11, 2020

+1 Thanks, very useful. And thanks @jordansissel for the fixes :)

@obonyojimmy

This comment has been minimized.

Copy link
Owner Author

@obonyojimmy obonyojimmy commented Jan 11, 2020

Cool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.