0
votes

I'm trying to limit the access of the users of my program to the keyboard. To do that I've defined a low level keyboard hook:

LRESULT CALLBACK lowLevelKeyboardProc(int key, WPARAM wParam, LPARAM lParam)
{
    KBDLLHOOKSTRUCT* pkbhs = (KBDLLHOOKSTRUCT*)lParam;

    switch (key)
    {
        case HC_ACTION:
....

and hooked it:

m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)lowLevelKeyboardProc, 0, 0);

I need the users to be able to use only the alphanumeric chars, ~, @, # ... only the chars that can be in a password (printable chars). What would be the easiest way to differentiate between those chars and all the others using the low level keyboard hook parameters: int key, WPARAM wParam, LPARAM lParam ?

The program is written in c++ and compiled in VC2010.

All help is appreciated!

1
Don't use a global solution to a local problem. Simply filter the WM_CHAR messages.Hans Passant

1 Answers

2
votes

Before actually answering your question, I have a couple of questions for you:

  1. Why would you want to do this globally, using a global low-level keyboard hook?

    Although sometimes they are the only way to solve a problem, using a global hook like this is generally strongly discouraged for many reasons. There are many better ways of preventing a user from entering invalid or unacceptable data. For example, I would just disable the keys you don't want for a particular control or set of controls (e.g. all textboxes). That way, the user can still use keyboard shortcuts and other non-alphanumeric keys to interact with your application, which is critical for accessibility reasons. Remember that a global hook will affect all of the other threads running on the machine, not just your app—that is probably not what you want.

  2. Even if you do decide to use a global hook, are you sure that you really need to disable all of the non-alphanumeric keys? What about Backspace and Delete? Shouldn't the user be able to delete things? And what about Tab? Enter? And what about modifier keys, like Shift, Ctrl, and Alt: are those allowed?

Please seriously reconsider whether you really need a low-level hook before continuing forward with this design. If you need help on devising another solution, please ask a new question, describing what you want to accomplish (e.g., I want to prevent users from entering any non-alphanumeric characters in a textbox control; here is the code I use to create my textbox…).

But if you insist on ignoring my advice, the solution is rather simple: investigate the members of the KBDLLHOOKSTRUCT structure that is passed to the hook procedure. The vkCode member gives you the virtual key code of the key that was pressed. Chances are, that is all the information you need. But just in case it's not, the hardware scan code of the key is also provided in the scanCode member.

Unfortunately, the code that you currently have is wrong. The first parameter to the hook callback procedure is indeed an int, but it is not a key code. Rather, it is a code that signals how the hook procedure should process the message. Use it like this:

LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
   // If nCode is greater than or equal to HC_ACTION, process the message.
   if (nCode >= HC_ACTION)
   {
      KBDLLHOOKSTRUCT* pkbhs = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);

      // Handle the keys as you wish here.
      // 
      // Remember that pkbhs->vkCode gives you the virtual key code
      // of the key that was pressed.
      // 
      // To prevent a particular key from being processed, you should
      // return a non-zero value (e.g. 1) immediately.
   }

   // Pass the message on.
   return CallNextHookEx(m_hHook, nCode, wParam, lParam);
}

And when you install the hook, there is absolutely no need to cast the function pointer. Pointless casts like this just hide potential compile-time errors, leading to a crash at runtime. Write it simply:

m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, 0, 0);