Page 1 of 1

LowLevelProc template

Posted: 09 May 2021, 04:35
by Onimuru
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.
}

Re: LowLevelProc template

Posted: 09 May 2021, 08:26
by guest3456
useful thanks