0

I'm trying to write an keyboard software debounce program in C(++) for my crappy keyboard that double-clicks.

I apparently need to set a hook to WM_KEYBOARD_LL, but I a) couldn't do it, I have "invalid handle" errors and b) don't know how to cancel the keypresses, as I also want to do this for gaming.

How would I properly implement this?

Thanks in advance!

EDIT: Here is the non-working code I found somewhere

#include "windows.h"
#include <iostream>
using namespace std;

HHOOK hookHandle;

LRESULT CALLBACK keyHandler(int nCode, WPARAM wParam, LPARAM lParam);

int main(int argc, char *argv[])
{

  hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, keyHandler, NULL, 0);

  if (hookHandle == NULL)
  {
    cout << "ERROR CREATING HOOK: ";
    cout << GetLastError() << endl;
    getchar();
    return 0;
  }

  MSG message;

  while (GetMessage(&message, NULL, 0, 0) != 0)
  {
    TranslateMessage(&message);
    DispatchMessage(&message);
  }

  cout << "Press any key to quit...";
  getchar();

  UnhookWindowsHookEx(hookHandle);

  return 0;
}

LRESULT CALLBACK keyHandler(int nCode, WPARAM wParam, LPARAM lParam)
{
  cout << "Hello!" << endl;

  // Checks whether params contain action about keystroke
  if (nCode == HC_ACTION)
  {
    cout << ((KBDLLHOOKSTRUCT *)lParam)->vkCode << endl;
  }

  return CallNextHookEx(hookHandle, nCode,
                        wParam, lParam);
}

I am compiling it with Mingw-W64 on Linux, but error reproduces on MSVC and Mingw-W64 on Windows.

  • 1
    Where did you get these invalid handle errors? Without some code it's hard to tell anything. – Jabberwocky Mar 01 '22 at 21:13
  • Yes, you should give us a [mre]. Without that, we are helpless. – Paul Sanders Mar 01 '22 at 21:18
  • Did you first try changing the keyboard character repeat rate in the Control Panel properties? – Weather Vane Mar 01 '22 at 21:18
  • @WeatherVane That is not the point. The keyboard itself is repeating, not Windows. – Matteo Leullier Mar 01 '22 at 21:20
  • @PaulSanders OK, I'm editing the answer now. – Matteo Leullier Mar 01 '22 at 21:20
  • @MatteoLeullier OK, we're getting there. And the output of this program is? That said, don't pass the `dwThreadId` parameter as 0. From the [documentation](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa): "For desktop apps, if this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.". So pass the value returned by [`GetCurrentThreadId`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid) instead. – Paul Sanders Mar 01 '22 at 21:29
  • This might help: https://stackoverflow.com/questions/22975916/global-keyboard-hook-with-wh-keyboard-ll-and-keybd-event-windows – Jerry Jeremiah Mar 01 '22 at 21:30
  • 1
    @pau A low-level keyboard hook is global by design. You cannot (and should not) pass a thread ID. If you do, the value is ignored. – IInspectable Mar 01 '22 at 21:38
  • *"don't know how to cancel the keypresses"* - As the [documentation](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644985(v=vs.85)) explains: *"If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure."* It's unlikely that any of this has any effect on games. Games will generally observe input at a level below input messages. – IInspectable Mar 01 '22 at 21:44
  • @IInspectable Ah yes of course, forget I spoke. I will leave my comment there as a confession of my thoughtlessness. – Paul Sanders Mar 01 '22 at 21:44
  • 1
    @MatteoLeullier "*for my crappy keyboard*" - why can't you just get a better keyboard? – Remy Lebeau Mar 01 '22 at 21:44
  • 2 months late, but @RemyLebeau ah, if all things could be solved by buying new things. I don't want to buy another one. Why should I buy a new one if I can mitigate the issue? – Matteo Leullier May 28 '22 at 00:33
  • @MatteoLeullier "*Why should I buy a new one*" - because it is not working properly, so get a new one that does. Otherwise, try checking if your system's Accessibility settings can account for the issue by handling the debouncing for you at the system level before keystrokes get sent to apps. – Remy Lebeau May 28 '22 at 00:35

1 Answers1

3

I apparently need to set a hook to WM_KEYBOARD_LL, but I ... couldn't do it, I have "invalid handle" errors

Per the SetWindowsHookEx() documentation:

An error may occur if the hMod parameter is NULL and the dwThreadId parameter is zero or specifies the identifier of a thread created by another process.

Which is exactly what you are doing.

So, if your goal is to hook every running process globally (ie, dwThreadId=0), you need to pass a non-NULL HMODULE to the hMod parameter. Low-level hooks are not required to be implemented as DLLs, so you should be able to use the GetModuleHandle() function (for instance, with either lpModuleName=NULL or lpModuleName="kernel32.dll" should suffice).

[I] don't know how to cancel the keypresses

Per the LowLevelKeyboardProc documentation:

Return value

...

If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD_LL hooks will not receive hook notifications and may behave incorrectly as a result. If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770