ttimt/Short_Cutteer

View on GitHub
hook/windows/hook.go

Summary

Maintainability
A
0 mins
Test Coverage
package windows

import (
    "unsafe"

    "golang.org/x/sys/windows"
)

type (
    SHORT     int16
    WORD      uint16
    DWORD     uint32
    LRESULT   int64
    HHOOK     uintptr
    HINSTANCE uintptr
    HWND      uintptr
    LPMSG     uintptr
    WPARAM    uintptr
    LPARAM    uintptr
    ULONG_PTR uintptr
    LPINPUT   TagINPUT

    // HOOKPROC Callback function after SendMessageW function is called (Keyboard input received).
    //
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-hookproc
    //
    // LPARAM is a pointer to a KBDLLHOOKSTRUCT struct :
    //
    // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644985(v=vs.85)
    HOOKPROC func(int, WPARAM, LPARAM) LRESULT
)

// Low-level keyboard input event.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-kbdllhookstruct
type TagKBDLLHOOKSTRUCT struct {
    VkCode      DWORD
    scanCode    DWORD
    flags       DWORD
    time        DWORD
    dwExtraInfo ULONG_PTR
}

// Input events
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
type TagINPUT struct {
    InputType DWORD
    Ki        KEYBDINPUT
    padding   uint64
}

// KEYBDINPUT Simulated keyboard event
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
type KEYBDINPUT struct {
    WVk         WORD
    WScan       WORD
    DwFlags     DWORD
    Time        DWORD
    DwExtraInfo ULONG_PTR
}

const (
    // Types of hook procedure:
    //
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw
    WH_KEYBOARD_LL = 13

    // Virtual Key Codes:
    //
    // https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
    VK_BACK       = 0x08 // Key: BACKSPACE
    VK_TAB        = 0x09 // Key: TAB
    VK_RETURN     = 0x0D // Key: Enter
    VK_SHIFT      = 0x10 // Key: SHIFT
    VK_CONTROL    = 0x11 // Key: CTRL
    VK_MENU       = 0x12 // Key: ALT
    VK_CAPITAL    = 0x14 // Key: CAPS LOCK
    VK_ESCAPE     = 0x1B // Key: ESC
    VK_SPACE      = 0x20 // Key: SPACEBAR
    VK_END        = 0x23 // Key: END
    VK_HOME       = 0x24 // Key: HOME
    VK_LEFT       = 0x25 // Key: LEFT ARROW
    VK_UP         = 0x26 // Key: UP ARROW
    VK_RIGHT      = 0x27 // Key: RIGHT ARROW
    VK_DOWN       = 0x28 // Key: DOWN ARROW
    VK_DELETE     = 0x2E // Key: DEL
    VK_ZERO       = 0x30 // Key: 0
    VK_ONE        = 0x31 // Key: 1
    VK_TWO        = 0x32 // Key: 2
    VK_THREE      = 0x33 // Key: 3
    VK_FOUR       = 0x34 // Key: 4
    VK_FIVE       = 0x35 // Key: 5
    VK_SIX        = 0x36 // Key: 6
    VK_SEVEN      = 0x37 // Key: 7
    VK_EIGHT      = 0x38 // Key: 8
    VK_NINE       = 0x39 // Key: 9
    VK_A          = 0x41 // Key: A
    VK_B          = 0x42 // Key: B
    VK_C          = 0x43 // Key: C
    VK_D          = 0x44 // Key: D
    VK_E          = 0x45 // Key: E
    VK_F          = 0x46 // Key: F
    VK_G          = 0x47 // Key: G
    VK_H          = 0x48 // Key: H
    VK_I          = 0x49 // Key: I
    VK_J          = 0x4A // Key: J
    VK_K          = 0x4B // Key: K
    VK_L          = 0x4C // Key: L
    VK_M          = 0x4D // Key: M
    VK_N          = 0x4E // Key: N
    VK_O          = 0x4F // Key: O
    VK_P          = 0x50 // Key: P
    VK_Q          = 0x51 // Key: Q
    VK_R          = 0x52 // Key: R
    VK_S          = 0x53 // Key: S
    VK_T          = 0x54 // Key: T
    VK_U          = 0x55 // Key: U
    VK_V          = 0x56 // Key: V
    VK_W          = 0x57 // Key: W
    VK_X          = 0x58 // Key: X
    VK_Y          = 0x59 // Key: Y
    VK_Z          = 0x5A // Key: Z
    VK_NUMPAD0    = 0x60 // Key: Numeric keypad 0
    VK_NUMPAD1    = 0x61 // Key: Numeric keypad 1
    VK_NUMPAD2    = 0x62 // Key: Numeric keypad 2
    VK_NUMPAD3    = 0x63 // Key: Numeric keypad 3
    VK_NUMPAD4    = 0x64 // Key: Numeric keypad 4
    VK_NUMPAD5    = 0x65 // Key: Numeric keypad 5
    VK_NUMPAD6    = 0x66 // Key: Numeric keypad 6
    VK_NUMPAD7    = 0x67 // Key: Numeric keypad 7
    VK_NUMPAD8    = 0x68 // Key: Numeric keypad 8
    VK_NUMPAD9    = 0x69 // Key: Numeric keypad 9
    VK_MULTIPLY   = 0x6A // Key: Numeric keypad *
    VK_ADD        = 0x6B // Key: Numeric keypad +
    VK_SUBTRACT   = 0x6D // Key: Numeric keypad -
    VK_DECIMAL    = 0x6E // Key: Numeric keypad .
    VK_DIVIDE     = 0x6F // Key: Numeric keypad /
    VK_LSHIFT     = 0xA0 // Key: Left SHIFT
    VK_RSHIFT     = 0xA1 // Key: Right SHIFT
    VK_LCONTROL   = 0xA2 // Key: Left CONTROL
    VK_RCONTROL   = 0xA3 // Key: RIGHT CONTROL
    VK_OEM_1      = 0xBA // Key: ;:
    VK_OEM_PLUS   = 0xBB // Key: +
    VK_OEM_COMMA  = 0xBC // Key: ,
    VK_OEM_MINUS  = 0xBD // Key: -
    VK_OEM_PERIOD = 0xBE // Key: .
    VK_OEM_2      = 0xBF // Key: /?
    VK_OEM_3      = 0xc0 // Key: `~
    VK_OEM_4      = 0xDB // Key: [{
    VK_OEM_5      = 0xDC // Key: \|
    VK_OEM_6      = 0xDD // Key: ]}
    VK_OEM_7      = 0xDE // Key: '"

    // INPUT_MOUSE Types of input event:
    //
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input#members
    INPUT_MOUSE    = 0
    INPUT_KEYBOARD = 1
    INPUT_HARDWARE = 2

    // KEYEVENTF_EXTENDEDKEY Keystroke for dwFlags in KEYBDINPUT
    //
    // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput#members
    KEYEVENTF_EXTENDEDKEY = 0x0001
    KEYEVENTF_KEYUP       = 0x0002
    KEYEVENTF_SCANCODE    = 0x0008
    KEYEVENTF_UNICODE     = 0x0004

    // Window Messages: Code to get and send messages between applications
    //
    // https://docs.microsoft.com/en-us/windows/win32/winmsg/window-messages
    WM_SETTEXT       = 0x000C
    WM_GETTEXT       = 0x000D
    WM_GETTEXTLENGTH = 0x000E

    // Keyboard Input Notifications
    //
    // https://docs.microsoft.com/en-us/windows/win32/inputdev/keyboard-input-notifications
    WM_KEYDOWN    = 0x0100 // Decimal: 256
    WM_KEYUP      = 0x0101 // Decimal: 257
    WM_SYSKEYDOWN = 0x0104 // When ALT key pressed || a key pressed with ALT pressed
    WM_SYSKEYUP   = 0x0105 // When ALT key pressed + a key released while pressed with ALT key
)

var (
    // Microsoft Windows DLLs
    winDLLUser32 = windows.NewLazyDLL("user32.dll")

    // User32.dll procedures
    winDLLUser32_ProcCallNextHookEx      = winDLLUser32.NewProc("CallNextHookEx")
    winDLLUser32_ProcSetWindowsHookExW   = winDLLUser32.NewProc("SetWindowsHookExW")
    winDLLUser32_ProcUnhookWindowsHookEx = winDLLUser32.NewProc("UnhookWindowsHookEx")
    winDLLUser32_GetMessageW             = winDLLUser32.NewProc("GetMessageW")
    winDLLUser32_SendInput               = winDLLUser32.NewProc("SendInput")
    winDLLUser32_GetKeyState             = winDLLUser32.NewProc("GetKeyState")
    winDLLUser32_GetForegroundWindow     = winDLLUser32.NewProc("GetForegroundWindow")
    winDLLUser32_SendMessageW            = winDLLUser32.NewProc("SendMessageW")
)

// LoadDLLs loads all required DLLs and return if error(s) occurred.
func LoadDLLs() error {
    // Load user32.dll
    err := winDLLUser32.Load()

    return err
}

// CallNextHookEx Pass the hook information to the next hook procedure.
// A hook procedure can call this function either before or after processing the hook information.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-callnexthookex
func CallNextHookEx(hhk HHOOK, nCode int, wParam WPARAM, lParam LPARAM) LRESULT {
    result, _, _ := winDLLUser32_ProcCallNextHookEx.Call(uintptr(hhk), uintptr(nCode), uintptr(wParam), uintptr(lParam))

    return LRESULT(result)
}

// SetWindowsHookExW Install hook procedure into a hhook chain.
// Result is null if error.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexw
func SetWindowsHookExW(idHook int, lpfn HOOKPROC, hmod HINSTANCE, dwThreadID DWORD) (HHOOK, error) {
    result, _, err := winDLLUser32_ProcSetWindowsHookExW.Call(uintptr(idHook), windows.NewCallback(lpfn), uintptr(hmod), uintptr(dwThreadID))

    return HHOOK(result), err
}

// UnhookWindowsHookEx Remove a hook procedure installed in a hook chain by the SetWindowsHookEx function.
// Result is zero if error.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-unhookwindowshookex
func UnhookWindowsHookEx(hhk HHOOK) (bool, error) {
    result, _, err := winDLLUser32_ProcUnhookWindowsHookEx.Call(uintptr(hhk))

    return result != 0, err
}

// GetMessageW Retrieves a message.
// Result is -1 if error.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagew
func GetMessageW(lpMsg LPMSG, hWnd HWND, wMsgFilterMin uint, wMsgFilterMax uint) (bool, error) {
    result, _, err := winDLLUser32_GetMessageW.Call(uintptr(lpMsg), uintptr(hWnd), uintptr(wMsgFilterMin), uintptr(wMsgFilterMax))

    return result != 0, err
}

// SendInput Simulate keyboard inputs to the operating system.
// Result is zero if error.
//
// Ex: SendInput(uint(len(tagInputs)), (*LPINPUT)(&tagInputs[0]), int(unsafe.Sizeof(tagInputs[0])))
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendinput
func SendInput(cInputs uint, pInputs *LPINPUT, cbSize int) (uint, error) {

    result, _, err := winDLLUser32_SendInput.Call(uintptr(cInputs), uintptr(unsafe.Pointer(pInputs)), uintptr(cbSize)) // skipcq GSC-G103

    return uint(result), err
}

// GetKeyState Retrieves the status of the specified virtual key.
// The status specifies whether the key is up, down or toggled
// (on, off - alternating each time the key is pressed).
// Returned bits = 16 bits.
//
// If high-order bit is 1, the key is down, otherwise it is up.
//
// If low-order bit is 1, the key is toggled on, otherwise the key is off.
//
// Since SHORT is int16, a negative value will indicates high-order bit is 1
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate
func GetKeyState(nVirtKey int) (SHORT, error) {
    result, _, err := winDLLUser32_GetKeyState.Call(uintptr(nVirtKey))

    return SHORT(result), err
}

// Retrieve a handle to the user active foreground window.
// Result is null if empty handle.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getforegroundwindow
func GetForegroundWindow() (HWND, error) {
    result, _, err := winDLLUser32_GetForegroundWindow.Call()

    return HWND(result), err
}

// Send the specified message to a window.
// The method does not return until the window procedure processed the message.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-sendmessage
func SendMessageW(hWnd HWND, Msg uint, wParam WPARAM, lParam LPARAM) (LRESULT, error) {
    result, _, err := winDLLUser32_SendMessageW.Call(uintptr(hWnd), uintptr(Msg), uintptr(wParam), uintptr(lParam))

    return LRESULT(result), err
}