@BarryG
Thanks for the detective work! You're absolutely right, internally AHK uses a hook if it can't register a hotkey. Didn't know that...
@ChrisR
Thanks for the C++ code, I'll put it into my local repository for reference!
@Axolotl
Thanks! That's a very sophisticated way to show that / how it works (with a GUI)
Seems to work flawless from my tests!
I've trying to adapt it to my workflow and allow to register / unregister shortcut keys on the fly (which works so far) but to make it perfect it should be possible to call a procedure from the hook that is given via the structured map as well. Have to think a bit more if it can be done and how...
That's my current state:
Code: Select all
EnumerationBinary KeyModifierStates 1
#KM_Shift ; 1
#KM_Ctrl ; 2
#KM_Alt ; 4
#KM_Win ; 8
EndEnumeration
Structure KBD_STRUCT
kbdState.i ; The keyboard modifier state (from KeyModifierStates)
EndStructure
; Keyboard shortcut(s)
Structure SHORTCUT_STRUCT
modifier.i ; Modifier, #KM_Shift, #KM_Ctrl, #KM_Alt, #KM_Win
functionID.i ; The function ID to execute
EndStructure
; Fill this map first!
Global NewMap KeyboardShortcuts.SHORTCUT_STRUCT()
Global kbd.KBD_STRUCT
Global.i hKeyboardShortcutsHook
Procedure KeyboardShortcutsHook(nCode, wParam, *lParam.KBDLLHOOKSTRUCT)
Static.i kbdState, shiftState, ctrlState, altState, winState
If nCode < 0 Or nCode <> #HC_ACTION
ProcedureReturn CallNextHookEx_(hKeyboardShortcutsHook, nCode, wParam, *lParam)
EndIf
Select wParam
Case #WM_KEYDOWN, #WM_SYSKEYDOWN ; #WM_SYSKEYDOWN is a necessity for testing the ALT key!
Select *lParam\vkCode
Case #VK_LSHIFT, #VK_RSHIFT : shiftState = #KM_Shift
Case #VK_LCONTROL, #VK_RCONTROL : ctrlState = #KM_Ctrl
Case #VK_LMENU, #VK_RMENU : altState = #KM_Alt
Case #VK_LWIN, #VK_RWIN : winState = #KM_Win
EndSelect
Case #WM_KEYUP, #WM_SYSKEYUP
Select *lParam\vkCode
Case #VK_LSHIFT, #VK_RSHIFT : shiftState = #False
Case #VK_LCONTROL, #VK_RCONTROL : ctrlState = #False
Case #VK_LMENU, #VK_RMENU : altState = #False
Case #VK_LWIN, #VK_RWIN : winState = #False
EndSelect
EndSelect
; Put the state into the structure
kbdState = shiftState + ctrlState + altState + winState
If kbd\kbdState <> kbdState
kbd\kbdState = kbdState
EndIf
If wParam = #WM_KEYDOWN Or wParam = #WM_SYSKEYDOWN
If FindMapElement(KeyboardShortcuts(), Str(*lParam\vkCode))
With KeyboardShortcuts()
If kbdState = \modifier
Debug "Registered shortcut pressed"
; Execute \functionID ...
EndIf
EndWith
; Swallow the modifier key
ProcedureReturn #True
EndIf
EndIf
ProcedureReturn CallNextHookEx_(hKeyboardShortcutsHook, nCode, wParam, *lParam)
EndProcedure
If OpenWindow(0, 0, 0, 300, 200, "Keyboard hook", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
; Hooking
hKeyboardShortcutsHook = SetWindowsHookEx_(#WH_KEYBOARD_LL, @KeyboardShortcutsHook(), #Null, 0)
ClearMap(KeyboardShortcuts())
; Register "WIN+F"
KeyboardShortcuts(Str(#VK_F))\modifier = #KM_Win
; Register "SHIFT+F10"
KeyboardShortcuts(Str(#VK_F10))\modifier = #KM_Shift
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
If hKeyboardShortcutsHook : UnhookWindowsHookEx_(hKeyboardShortcutsHook) : EndIf
EndIf