Page 1 of 1
Register global hotkey with WIN+... fails
Posted: Sun Dec 05, 2021 1:38 pm
by highend
Hi,
Somewhere from this forum:
Code: Select all
Procedure SetHotKey(hWnd, key, modifiers)
Static id
id + 1
If RegisterHotKey_(hWnd, id, modifiers, key) = 0
MessageRequester("Test", "FAILED: " + key)
ProcedureReturn -1
EndIf
ProcedureReturn id
EndProcedure
Procedure HotKeyMsg()
Protected.MSG msg
GetMessage_(@msg, 0, 0, 0)
If msg\message = #WM_HOTKEY
ProcedureReturn msg\wParam
EndIf
EndProcedure
If OpenWindow(0, 0, 0, 0, 0, "", #PB_Window_Invisible)
Define.i event
hk_ctrl_a = SetHotKey(WindowID(0), #VK_A, #MOD_CONTROL)
hk_f7 = SetHotKey(WindowID(0), #VK_F7, 0)
hk_esc = SetHotKey(WindowID(0), #VK_ESCAPE, 0)
hk_win_f = SetHotKey(WindowID(0), #VK_F, #MOD_WIN)
Repeat
event = HotKeyMsg()
If event
Select event
Case hk_ctrl_a
MessageRequester("Test", "CTRL+A")
Case hk_f7
MessageRequester("Test", "F7")
Case hk_esc
MessageRequester("Test", "ESC")
Break
Case hk_win_f
MessageRequester("Test", "WIN+F")
EndSelect
EndIf
Delay(20)
ForEver
EndIf
I can't register e.g. WIN+F (the other three work fine)...
I do know that the OS has registered it but in AutoHotkey I can re-register that hotkey just fine:
Code: Select all
#NoEnv
;#NoTrayIcon
#Persistent
#SingleInstance Force
#F::Msgbox, % "WIN+F"
Re: Register global hotkey with WIN+... fails
Posted: Sun Dec 05, 2021 4:58 pm
by Axolotl
As far as I know you need something like GlobalAddAtom() instead of local id's for System-wide hotkeys. Sorry cannot show an example, codes are on the other computer.
Re: Register global hotkey with WIN+... fails
Posted: Sun Dec 05, 2021 6:31 pm
by AZJIO
Re: Register global hotkey with WIN+... fails
Posted: Tue Dec 07, 2021 10:29 pm
by highend
@Axolotl
I would be grateful if you can post an example if you have access again
@AZJIO
That looks very hacky

Re: Register global hotkey with WIN+... fails
Posted: Wed Dec 08, 2021 1:19 am
by BarryG
Axolotl wrote: Sun Dec 05, 2021 4:58 pmAs far as I know you need something like GlobalAddAtom() instead of local id's for System-wide hotkeys
I tried that, and it still fails (see below). Win+F is reserved for Win 10's "Feedback" tool, so it can't be registered.
I think AutoHotkey must be setting a
keyboard hook to overcome this, ie. it's pretending to register a hotkey but actually doing it with a hook. I haven't checked the source to know for sure, but it seems very likely. RegisterHotKey_() with Win+F definitely isn't permitted, as shown below.
Code: Select all
OpenWindow(0,100,100,200,100,"Win+F Hotkey",#PB_Window_SystemMenu)
id=GlobalAddAtom_("Win+F") ; Any text here.
hk=RegisterHotKey_(WindowID(0),id,#MOD_WIN,#VK_F)
If hk=0
Debug "Hotkey failed to register"
Else
Repeat
event=WaitWindowEvent()
If event=#WM_HOTKEY
n+1
Debug n
EndIf
Until event=#PB_Event_CloseWindow
EndIf
[Edit] I was correct about AutoHotkey. It uses a
keyboard hook when RegisterHotkey_() fails. From its "
Hotkey.cpp" source (line 353):
Code: Select all
// If the hotkey is normal, try to register it. If the register fails, use the hook to try
// to override any other script or program that might have it registered (as documented):
if (hot.mType == HK_NORMAL)
{
if (!hot.Register()) // Can't register it, usually due to some other application or the OS using it.
if (!is_win9x) // If the OS supports the hook, it can be used to override the other application.
hot.mType = HK_KEYBD_HOOK;
Re: Register global hotkey with WIN+... fails
Posted: Wed Dec 08, 2021 4:00 pm
by Axolotl
@highend
yepp. BarryG is right. I have implemented it exactly like this. I read some recommendations about low level
keyboard hook stuff.
That is a different story I have no experiences with, but I hacked something together.
I hope this gives you a good starting point.
@BarryG
thank you, otherwise I would have send my 'not working code' and embarrassed myself.
Here comes the '
LowLevel' code...
Any kind of Feedback would be very nice
Code: Select all
; ---------------------------------------------------------------------------------------------------------------------
;:: FILE : LowLevelKeyboardHook.pb
;::
;:: Target OS : Windows
;:: License : Free, unrestricted, no warranty whatsoever
;:: Use at your own risk
;::
;:: Copyright (c) 2021 by AHa
; ---------------------------------------------------------------------------------------------------------------------
#WINDOW_Main = 1
#GADGET_btnHook = 1
#GADGET_edtOutput = 2
Structure KEYBOARDHOOKSTRUCTURE
vkCode.l
scanCode.l
flags.l
time.l
dwExtraInfo.l
EndStructure
#WM_KEY = #WM_USER + 123
Global Hotkey_hWnd
Global Hotkey_hKeyboardHook
Procedure Hotkey_KeyboardProc(nCode, wParam, *kb.KEYBOARDHOOKSTRUCTURE)
Static fMod = 0, vkCode = 0
If nCode = #HC_ACTION
If wParam = #WM_KEYDOWN
If *kb\vkCode = #VK_LWIN Or *kb\vkCode = #VK_RWIN
fMod = #MOD_WIN
ElseIf *kb\vkCode = #VK_F
vkCode = *kb\vkCode
EndIf
ElseIf wParam = #WM_KEYUP
If *kb\vkCode = #VK_LWIN Or *kb\vkCode = #VK_RWIN
fMod = 0
ElseIf *kb\vkCode = #VK_F
vkCode = 0
EndIf
EndIf
If fMod And vkCode
PostMessage_(Hotkey_hWnd, #WM_KEY, vkCode, fMod)
ProcedureReturn #True ;:: do not start Feedback Hub
EndIf
EndIf
ProcedureReturn CallNextHookEx_(0, nCode, wParam, lParam) ;:: hhk is ignored
EndProcedure ;()
Procedure SetKeyboardHook(State) ;::
Static hKeyboardHook = 0
If State = #False ;:: unhook
If hKeyboardHook
UnhookWindowsHookEx_(hKeyboardHook) ;:: NONZERO
hKeyboardHook = 0
EndIf
ElseIf State = #True ;:: hook
If hKeyboardHook
UnhookWindowsHookEx_(hKeyboardHook)
EndIf
hKeyboardHook = SetWindowsHookEx_(#WH_KEYBOARD_LL, @Hotkey_KeyboardProc(), GetModuleHandle_(0), 0)
EndIf
EndProcedure ;()
Procedure Main()
Protected fHook
If OpenWindow(#WINDOW_Main, 0, 0, 600, 300, "Low Level Hook", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
Hotkey_hWnd = WindowID(#WINDOW_Main)
ButtonGadget(#GADGET_btnHook, 8, 4, 96, 24, "Hook ON")
EditorGadget(#GADGET_edtOutput, 8, 32, 584, 260)
AddGadgetItem(#GADGET_edtOutput, -1, "Press WIN+F with Hook On and Off should show different results. :) ")
Repeat
Select WaitWindowEvent()
Case #WM_KEY
AddGadgetItem(#GADGET_edtOutput, -1, "Hook Info: You pressed WIN+F :) ")
;MessageRequester("Hook Info ", "You pressed WIN+F")
Case #PB_Event_Gadget
Select EventGadget()
Case #GADGET_btnHook
If fHook ; hook is active
fHook = #False
SetKeyboardHook(#False)
SetGadgetText(#GADGET_btnHook, "Hook ON")
Else ; hook not active
fHook = #True
SetKeyboardHook(#True)
SetGadgetText(#GADGET_btnHook, "Hook OFF")
EndIf
EndSelect
Case #PB_Event_CloseWindow : Break
EndSelect
ForEver
SetKeyboardHook(#False) ;:: do not forget to unhook :)
EndIf
ProcedureReturn 0
EndProcedure
;
End Main()
;// BoF
Re: Register global hotkey with WIN+... fails
Posted: Wed Dec 08, 2021 10:44 pm
by ChrisR
If it can be of any help, I have this cpp program that works. It uses the
WinIO library.:
The WinIo library was developed to allow 32-bit Windows applications to directly access I/O ports and physical memory. It bypasses Windows protection mechanisms by using a combination of a kernel-mode device driver and several low-level programming techniques.
Code: Select all
// WinF.cpp : Defines the entry point for the win32 application.
// Build here in Ms Visual Studio 2008 SP1: File > New > Project > Win32 project.
//
// The program uses WinIo library http://www.internals.com/
// WinIo - Version 3.0 supports 32 or 64 bit platforms and concurrent use from multiple applications.
// The WinIo library allows 32 or 64 bit Windows applications to directly access I/O ports and physical memory access under Windows NT/2000/XP/2003/Vista/7/2008/8/2012/10.
// It bypasses Windows protection mechanisms by using a combination of a kernel-mode device driver and several low-level programming techniques.
#include "stdafx.h"
#include <windows.h>
typedef BOOL(WINAPI *INITIALIZEWINIO)(void);
typedef BOOL(WINAPI *SHUTDOWNWINIO)(void);
typedef BOOL(WINAPI *GETPORTVAL)(WORD wPortAddr, PDWORD pdwPortVal, BYTE bSize);
typedef BOOL(WINAPI *SETPORTVAL)(WORD wPortAddr, DWORD dwPortVal, BYTE bSize);
INITIALIZEWINIO InitializeWinIo = NULL;
SHUTDOWNWINIO ShutdownWinIo = NULL;
GETPORTVAL GetPortVal = NULL;
SETPORTVAL SetPortVal = NULL;
#define key_cmd 0x64 // Keyboard command port
#define key_dat 0x60 // Keyboard data port
BOOL InitFuncs(void)
{
// Directives, controls compilation
#if defined( _WIN64 )
HMODULE hMod = LoadLibrary(L"WinIo64.dll");
#else
HMODULE hMod = LoadLibrary(L"WinIo32.dll");
#endif
InitializeWinIo= (INITIALIZEWINIO)GetProcAddress(hMod,"InitializeWinIo");
ShutdownWinIo= (SHUTDOWNWINIO)GetProcAddress(hMod,"ShutdownWinIo");
GetPortVal= (GETPORTVAL)GetProcAddress(hMod,"GetPortVal");
SetPortVal= (SETPORTVAL)GetProcAddress(hMod,"SetPortVal");
if(InitializeWinIo==NULL) return FALSE;
if(ShutdownWinIo==NULL) return FALSE;
if(GetPortVal==NULL) return FALSE;
if(SetPortVal==NULL) return FALSE;
return TRUE;
}
// Wait for the buffer is empty
void KBCwait4IBE() {
DWORD ch=0;
do { GetPortVal(key_cmd,&ch,1); // Read keyboard command port, come ch
Sleep (4);
} while(ch & 0x2); // Bit1 is 1, then the input buffer is full explanation, repeated testing! Until empty.
}
// Key is pressed
void KeyDown(DWORD VirtualKey) {
DWORD K_Make_Code=MapVirtualKey(VirtualKey,0); // winuser.h defined function
KBCwait4IBE();
SetPortVal(key_cmd,0xd2,1); // Send keyboard write command
KBCwait4IBE();
SetPortVal(key_dat,K_Make_Code,1); // Write key scan code
}
// Key release
void KeyUp(DWORD VirtualKey) {
DWORD K_Make_Code=MapVirtualKey(VirtualKey,0); // Key scan code
DWORD K_Break_Code=K_Make_Code+0x80; // Key break code
KBCwait4IBE();
SetPortVal(key_cmd,0xd2,1); // Send keyboard write command
KBCwait4IBE();
SetPortVal(key_dat,K_Break_Code,1); // Write key break code
}
// Key is pressed. Extended
void KeyDownEx(DWORD VirtualKey) {
DWORD K_Make_Code=MapVirtualKey(VirtualKey,0); // winuser.h defined function
KBCwait4IBE();
SetPortVal(key_cmd,0xd2,1); // Send keyboard write command
KBCwait4IBE();
SetPortVal(key_dat,0xe0,1); // Write extended key flag
KBCwait4IBE();
SetPortVal(key_cmd,0xd2,1); // Send keyboard write command
KBCwait4IBE();
SetPortVal(key_dat,K_Make_Code,1); // Write key scan code
}
// Key release. Extended
void KeyUpEx(DWORD VirtualKey) {
DWORD K_Make_Code=MapVirtualKey(VirtualKey,0); // Key scan code
DWORD K_Break_Code=K_Make_Code+0x80; // Key break code
KBCwait4IBE();
SetPortVal(key_cmd,0xd2,1); // Send keyboard write command
KBCwait4IBE();
SetPortVal(key_dat,0xe0,1); // Write extended key flag
KBCwait4IBE();
SetPortVal(key_cmd,0xd2,1); // Send keyboard write command
KBCwait4IBE();
SetPortVal(key_dat,K_Break_Code,1); // Write key break code
}
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
BOOL init;
if(InitFuncs()!=TRUE)
{
MessageBox(0, L"Load WinIo.dll failed!", L"WinF Error", MB_ICONEXCLAMATION | MB_OK);
//printf("Load WinIo.dll failed!\n");
return 1;
}
init = InitializeWinIo();
if(init!=TRUE)
{
MessageBox(0, L"InitializeWinIo failed!", L"WinF Error", MB_ICONEXCLAMATION | MB_OK);
//printf("InitializeWinIo failed!\n");
return 2;
}
KeyDownEx(VK_LWIN); // Left Win is pressed (extended key)
KeyDown('F'); // F is pressed
Sleep(100); // Wait for fast CPU
KeyUp('F'); // F is release
KeyUpEx(VK_LWIN); // Left Win is release (extended key)
ShutdownWinIo();
return 0;
}
Re: Register global hotkey with WIN+... fails
Posted: Thu Dec 09, 2021 2:05 pm
by highend
@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
Re: Register global hotkey with WIN+... fails
Posted: Thu Dec 09, 2021 3:01 pm
by Axolotl
I am always happy to help.
This already looks quite useful. Please keep in mind that the low-level routine affects the execution of other applications. I think that the longer you stay in it, the slower the response of other applications will be.
That's why I had passed processing through PostMessage() and left the LL procedure asap.
BTW: the constants you are using were already defined in PureBasic:
Code: Select all
Debug #MOD_ALT ;:: #MOD_ALT = 0x01
Debug #MOD_CONTROL ;:: #MOD_CONTROL = 0x02
Debug #MOD_SHIFT ;:: #MOD_SHIFT = 0x04
Debug #MOD_WIN ;:: #MOD_WIN = 0x08
Happy coding and stay healthy.