We have a kiosk app where we would like to allow use of the Win + H hotkey to bring up Voice Typing, but still filter out other Win + ... hotkeys and the Win key by itself.
The hook is set with SetWindowsHookEx(WH_KEYBOARD_LL, callback, hInstance, 0);
Log from pressing Win + H:
// w = bool, GetAsyncKeyState for left and right windows keys
// h = bool, ...for "h" key
// key = KBDLLHOOKSTRUCY* pkh->vkCode, 72 = h, 91 = left Windows key
// flags = KBDLLHOOKSTRUCT* pkh->flags
// pressed Windows, then H, then released
w=0 h=0 key= 91 flags = 1
w=-1 h=0 key= 91 flags = 1
... repeats ....
w=-1 h=0 key= 91 flags = 1
w=-1 h=0 key= 72 flags = 0
w=-1 h=-1 key= 72 flags = 80
w=-1 h=0 key= 91 flags = 81
What I'm finding while trying different permutations of code in the callback is, either I get no keypress, or I get a plain h
(so Voice Typing is not launched), or all other Win + ... hotkeys get through not just Win + H.
This treats the press as a plain h
, so apparently at least some of the Win key events before the h
events must be allowed past the filter:
if (nCode == HC_ACTION)
{
BOOL bWinKeyDown = (GetAsyncKeyState(VK_LWIN) >> 15) | (GetAsyncKeyState(VK_RWIN) >> 15);
if (pkh->vkCode == 'H')
{
// w=-1 h=0 key= 72 flags = 0
// w=-1 h=-1 key= 72 flags = 80
return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
}
else if ((pkh->vkCode == VK_LWIN || pkh->vkCode == VK_RWIN))
{
// also just "h" if we allow through the w=-1 h=0 key= 91 flags = 1 events
// if ((bWinKeyDown && (pkh->flags == 1)) )
// return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
// else
return -1;
}
}
return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
This lets all Win + ... hotkeys through, it seems like the first event acts as a "dead key" that gets applied to any next letter key:
if (nCode == HC_ACTION)
{
BOOL bWinKeyDown = (GetAsyncKeyState(VK_LWIN) >> 15) | (GetAsyncKeyState(VK_RWIN) >> 15);
if (pkh->vkCode == 'H')
{
// w=-1 h=0 key= 72 flags = 0
// w=-1 h=-1 key= 72 flags = 80
return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
}
else if ((pkh->vkCode == VK_LWIN || pkh->vkCode == VK_RWIN))
{
if ((!bWinKeyDown && (pkh->flags == 1))) // // w=0 h=0 key= 91 flags = 1 - first event in log
return CallNextHookEx(g_hHookKbdLL, nCode, wp, lp);
else
return -1;
}
Is there any way to trap all Win + ... hotkey except for Win + H?
According to my test, win+h
produces four messages, Index 1 to 4.
You can trap them at the second message. The following code which traps all
Win+...
hotkey except for win+h
works for me.
LRESULT CALLBACK Proc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* s= (KBDLLHOOKSTRUCT*)lParam;
std::cout << " Index=" << nwinhooksdll++ << "wParam="<< wParam <<" s->vkCode=" << s->vkCode << " s->scanCode=" << s->scanCode << " s->flags=" << s->flags << " s->dwExtraInfo=" << s->dwExtraInfo << "\n";
if (GetAsyncKeyState(VK_LWIN))
{
if (s->vkCode == 'H')
{
}
else
{
return 1;
}
}
}
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
Update:
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
KBDLLHOOKSTRUCT* s = (KBDLLHOOKSTRUCT*)lParam;
std::cout << " Index=" << nwinhooksdll++ << "wParam=" << wParam << " s->vkCode=" << s->vkCode << " s->scanCode=" << s->scanCode << " s->flags=" << s->flags << " s->dwExtraInfo=" << s->dwExtraInfo << "\n";
static BOOL H = FALSE;
static BOOL Other = FALSE;
static BOOL Send = FALSE;
if (Send) {
Send = FALSE;
INPUT inputs = {};
inputs.type = INPUT_KEYBOARD;
inputs.ki.wVk = VK_LWIN;
inputs.ki.dwFlags = KEYEVENTF_KEYUP;
UINT uSent = SendInput(1, &inputs, sizeof(INPUT));
}
if (GetAsyncKeyState(VK_LWIN) >> 15) {
if (s->vkCode == 'H') {
H = TRUE;
}
else if (s->vkCode == 91) {
if (H || Other)
{
H = FALSE;
Other = FALSE;
Send = TRUE;
std::cout << "return 1\n";
return 1;
}else{
}
}else{
Other = TRUE;
if (wParam == 256) {
std::cout << "return 1\n";
return 1;
}
}
}
}
return CallNextHookEx(_hook, nCode, wParam, lParam);
}
Update 2:
LRESULT CALLBACK Proc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION)
{
if (wParam > 0)
{
std::cout << "sent by the current thread wParam=" << wParam;
}
KBDLLHOOKSTRUCT* s= (KBDLLHOOKSTRUCT*)lParam;
std::cout << " Index=" << nwinhooksdll++ << "wParam="<< wParam <<" s->vkCode=" << s->vkCode << " s->scanCode=" << s->scanCode << " s->flags=" << s->flags << " s->dwExtraInfo=" << s->dwExtraInfo << "\n";
static BOOL H = FALSE;
static BOOL Other = FALSE;
static BOOL Send = FALSE;
//TODO
if (Send)
{
Send = FALSE;
if (s->vkCode != 91 && s->scanCode != 0 && wParam == 256)
{
if (!H && !Other)
{
INPUT inputs = {};
inputs.type = INPUT_KEYBOARD;
inputs.ki.wVk = s->vkCode;
inputs.ki.dwFlags = KEYEVENTF_KEYUP;
UINT uSent = SendInput(1, &inputs, sizeof(INPUT));
}
INPUT inputs = {};
inputs.type = INPUT_KEYBOARD;
inputs.ki.wVk = VK_LWIN;
inputs.ki.dwFlags = KEYEVENTF_KEYUP;
UINT uSent = SendInput(1, &inputs, sizeof(INPUT));
}
}
H = FALSE;
Other = FALSE;
if (GetAsyncKeyState(VK_LWIN)>>15 && s->scanCode != 0)
{
if (s->vkCode == 'H')
{
H = TRUE;
}
else if (s->vkCode == 91)
{
if (wParam == 257)
{
if (s->scanCode !=0)
{
Send = TRUE;
std::cout << "return 1\n";
return 1;
}
}
}
else
{
Other = TRUE;
if (wParam == 256)
{
std::cout << "return 1\n";
return 1;
}
}
}
}
return CallNextHookEx(_hook, nCode, wParam, lParam);
}