Перехват нажатия клавиш в приложениях Windows

Нередко в приложениях с графическим интерфейсом Windows возникает необходимость обработки событий от клавиатуры, а именно: нажатие какой-либо клавиши. Чаще всего это делается с помощью обработки оконных сообщений, типа WM_KEYDOWN или WM_CHAR (для элементов типа EDIT). С этим связано ряд неудобств: сообщения от клавиатуры получает именно то окно, которое на текущий момент находится в фокусе. Соответственно, если на окне диалога имеется несколько полей редактирования текста, для которых нужно произвести предварительную обработку сообщений, то обработку сообщений от клавиатуры придётся делать для каждого такого поля или создавать отдельный субкласс (subclass) для элементов диалога данного типа. Но есть более универсальный способ, который избавляет от необходимости привязывать обработку сообщений от клавиатуры к процедуре какого-либо окна. Это делается с помощью функции SetWindowsHookEx с первым параметром WH_KEYBOARD. Для языка программисрования C++ я сделал специальный класс CKeyboardHook, в котором метод StartHook запускает обработку сообщений от клавиатуры, а метод StopHook её останавливает с помощью функции UnhookWindowsHookEx. Весь класс реализован в одном заголовочном файле _KeyHook.h Вот его программный код:

#ifndef __KEYHOOK_H__
#define __KEYHOOK_H__
 
class CKeyboardHook
{
public:
      class Handler
      {
      public:
            virtual BOOL OnPressKey(WPARAM wParam, LPARAM lParam)
            {
                  return TRUE;
            }
      };
      //
      CKeyboardHook() : m_pHandler(NULL)
      {
      }
      ~CKeyboardHook()
      {
            StopHook();
      }
      HHOOK StartHook(Handler* pHandler)
      {
            m_pHandler = pHandler;
            m_Hook = ::SetWindowsHookEx(WH_KEYBOARD, KeyboardProc,
                                   (HINSTANCE)0, ::GetCurrentThreadId());
            return m_Hook;
      }
      BOOL StopHook()
      {
            m_pHandler = NULL;
            //
            if (m_Hook != NULL)
            {
                  return ::UnhookWindowsHookEx(m_Hook);
            } else
                  return FALSE;
      }
      Handler* GetKeyboardHandler()
      {
            return m_pHandler;
      }
      static LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
      {
            if (nCode < 0 || (HIBYTE(HIWORD(lParam)) & 0xF0) == 0xC0); else
            {
                  CKeyboardHook& theHook = CKeyboardHook::GetObjectInstance();
                  Handler* pHandler = theHook.GetKeyboardHandler();
                  if (pHandler != NULL)
                  {
                        if (pHandler->OnPressKey(wParam, lParam) == FALSE)
                             return 1;
                  }
            }
            //
            return CallNextHookEx(NULL, nCode, wParam, lParam);
      }
      static CKeyboardHook& GetObjectInstance()
      {
            static CKeyboardHook theHook;
            return theHook;
      }
private:
      HHOOK m_Hook;
      Handler* m_pHandler;
};
 
inline HHOOK KeyboardHookStart(CKeyboardHook::Handler* pHandler)
{
      CKeyboardHook& theHook = CKeyboardHook::GetObjectInstance();
      return theHook.StartHook(pHandler);
}
 
inline BOOL KeyboardHookStop()
{
      CKeyboardHook& theHook = CKeyboardHook::GetObjectInstance();
      return theHook.StopHook();
}
 
#endif __KEYHOOK_H__

Пример использования этого класса в проекте на Visual C++ 2005 можно загрузить отсюда: keyhook.zip