LowLevelProc template
Posted: 09 May 2021, 04:35
Here is a template to create mouse and keyboard hooks. If you return anything other than `CallNextHookEx`, any other processes that has not yet seen the message will not know about the event. I wrote this to circumvent closing an attached console having the side effect of closing script it was attached to also for instance.
Code: Select all
#Persistent
#Warn, ClassOverwrite, MsgBox
mouseHook := SetWindowsHookEx(14, "LowLevelMouseProc")
, keyboardHook := SetWindowsHookEx(13, "LowLevelKeyboardProc")
exit
SetWindowsHookEx(idHook, callback) {
if (!hHook := DllCall("SetWindowsHookEx", "Int", idHook, "Ptr", RegisterCallback(callback, "Fast"), "Ptr", DllCall("GetModuleHandle", "UInt", 0, "Ptr"), "UInt", 0, "Ptr")) {
throw (Exception(Format("0x{:X}", A_LastError), -1, FormatMessage(A_LastError)))
}
Static instance := {"__Class": "__HookEx"
, "__Delete": Func("UnhookWindowsHookEx")}
(hookEx := new instance()).Handle := hHook
return (hookEx)
}
UnhookWindowsHookEx(hookEx) {
if (!DllCall("UnhookWindowsHookEx", "Ptr", hookEx.Handle, "UInt")) {
throw (Exception(Format("0x{:X}", A_LastError), -1, FormatMessage(A_LastError)))
}
return (True)
}
LowLevelMouseProc(nCode, wParam, lParam) {
Critical, On
x := NumGet(lParam + 0, "Int"), y := NumGet(lParam + 4, "Int")
switch (wParam) {
case 0x0201: { ;? 0x0201 = WM_LBUTTONDOWN
ToolTip, % "WM_LBUTTONDOWN"
}
case 0x0202: { ;? 0x0202 = WM_LBUTTONUP
ToolTip, % "WM_LBUTTONUP"
}
case 0x0204: { ;? 0x0204 = WM_RBUTTONDOWN
ToolTip, % "WM_RBUTTONDOWN"
}
case 0x0205: { ;? 0x0205 = WM_RBUTTONUP
ToolTip, % "WM_RBUTTONUP"
}
case 0x0207: { ;? 0x0207 = WM_MBUTTONDOWN
ToolTip, % "WM_MBUTTONDOWN"
}
case 0x0208: { ;? 0x0208 = WM_MBUTTONUP
ToolTip, % "WM_MBUTTONUP"
}
case 0x020A: { ;? 0x020A = WM_MOUSEWHEEL (Vertical)
ToolTip, % Format("WM_MOUSEWHEEL {}", (NumGet(lParam + 8, "UInt") >> 16 == 120) ? ("Up") : ("Down"))
}
case 0x020E: { ;? 0x020E = WM_MOUSEWHEEL (Horizontal)
ToolTip, % Format("WM_MOUSEWHEEL {}", (NumGet(lParam + 8, "UInt") >> 16 == 120) ? ("Right") : ("Left"))
}
}
return (DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "UInt", wParam, "UInt", lParam))
}
LowLevelKeyboardProc(nCode, wParam, lParam) {
Critical, On
scCode := ((scCode := (NumGet(lParam + 0, 8, "UInt") & 1 << 8) | NumGet(lParam + 4, "UInt")) == 0x136) ? (0x36) : (scCode), vkCode := Format("{:X}", NumGet(lParam + 0, "UInt"))
, key := GetKeyName(Format("vk{}", vkCode))
switch (wParam) {
case 0x100: { ;? 0x100 = WM_KEYDOWN
ToolTip, % "WM_KEYDOWN"
}
case 0x101: { ;? 0x101 = WM_KEYUP
ToolTip, % "WM_KEYUP"
}
case 0x104: { ;? 0x104 = WM_SYSKEYDOWN
ToolTip, % "WM_SYSKEYDOWN"
}
case 0x105: { ;? 0x105 = WM_SYSKEYUP
ToolTip, % "WM_SYSKEYUP"
}
}
return (DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "UInt", wParam, "UInt", lParam))
}
;* FormatMessage(messageID)
FormatMessage(messageID) { ;: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessage
Local
if (!length := DllCall("Kernel32\FormatMessage", "UInt", 0x1100, "Ptr", 0, "UInt", messageID, "UInt", 0, "Ptr*", buffer := 0, "UInt", 0, "Ptr", 0, "UInt")) {
return (FormatMessage(DllCall("Kernel32\GetLastError")))
}
return (StrGet(buffer, length - 2), DllCall("Kernel32\LocalFree", "Ptr", buffer, "Ptr")) ;* Account for the newline and carriage return characters.
}