Функціонал WinAPI дозволяє на низькому рівні за допомогою хуків відстежувати події системи, такі як рух та кліки миші. Цей функціонал працює на основі функцій callback, тому якщо хочеться використовувати ООП та систему сигналів та слотів , то потрібно буде один з методів передавати як функцію callback у функцію реєстрації callback в системі Windows. Але метод має бути статичним, тому потрібно розробляти клас як Сінглтон.
ВстановитиWindowsHookEx
Ця функція використовується для реєстрації функції-обробника подій у ланцюжку hook-обробників для відстеження деяких подій у Windows
HHOOK WINAPI SetWindowsHookEx( _In_ int idHook, _In_ HOOKPROC lpfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadId );
Параметри
idHook [в]
Тип: int
Тип хука, який було встановлено. Параметр може набувати наступних значень.
- WH_CALLWNDPROC - Встановлює hook процедуру, яка відстежує повідомлення до того, як система перешле їх в обробку вікна призначення.
- WH_CALLWNDPROCRET - Встановлює hook процедуру, яка відстежує повідомлення після того, як вони були оброблені вікном призначення.
- WH_CBT - Встановлює hook процедуру, яка приймає корисні повідомлення для прикладної програми.
- WH_DEBUG - встановлює hook процедуру, яка корисна при налагодженні інших hook процедур.
- WH_FOREGROUNDIDLE - Встановлює hook процедуру, яка буде викликатися в тому випадку, коли програма простоює. Може бути корисним для виконання завдань з низьким пріоритетом під час простою.
- WH_GETMESSAGE - встановлює hook процедуру, яка контролює повідомлення, надіслані у чергу повідомлень.
- WH_JOURNALPLAYBACK - встановлює hook процедуру, що викликає повідомлення, раніше записані за допомогою процедури WH_JOURNALRECORD.
- WH_JOURNALRECORD - встановлює hook процедуру, яка записує вхідні повідомлення, надіслані в системну чергу повідомлень. Цей hook корисний для запису макросів.
- WH_KEYBOARD - встановлює hook процедуру, яка відстежує натискання клавіш.
- WH_KEYBOARD_LL - встановлює hook процедуру, яка контролює події клавіатури на низькому рівні.
- WH_MOUSE - встановлює hook процедуру, яка відстежує події миші.
- WH_MOUSE_LL - встановлює hook процедуру, яка відстежує події миші на низькому рівні.
- WH_MSGFILTER - встановлює hook процедуру, яка контролює повідомлення, що згенеровані в результаті події введення в діалоговому вікні, вікні повідомлень, меню або смуги прокручування.
- WH_SHELL - встановлює hook процедуру, яка отримує повідомлення, корисні для додатків оболонки.
- WH_SYSMSGFILTER - встановлює hook процедуру, яка контролює повідомлення, що згенеровані в результаті події введення в діалоговому вікні, вікні повідомлень, меню або смуги прокручування. Процедура, що підключається, контролює ці повідомлення для всіх програм в тому ж робочому столі, що і викликає потік.
lpfn [в]
Тип: HOOKPROC
Вказівник на hook процедуру. Якщо dwThreadId параметр дорівнює нулю або визначає ідентифікатор треда, створеного іншим процесом, параметр lpfn повинен вказувати на процедуру, що підключається в DLL. В іншому випадку lpfn може вказувати на процедуру, що підключається в коді, пов'язаному з поточним процесом.
hMod [в]
Тип: HINSTANCE
Дескриптор бібліотеки DLL, що містить процедуру hook, на який вказує параметр lpfn. .
dwThreadId [в]
Тип: DWORD
Ідентифікатор потоку, з яким процедура, що підключається, повинна бути пов'язана. Для настільних додатків, якщо цей параметр дорівнює нулю, процедура, що підключається, пов'язана з усіма існуючими потоками, запущеними в тому ж робочому столі, що і викликає потік. Для програм Windows Store, у розділі Примітки.
Повертається значення
Тип: * HHOOK *
Якщо функція завершується успішно, то значення, що повертається, дорівнює обробнику хука. Інакше буде повернено NULL. Для отримання більш детальної інформації про помилку викликайте GetLastError.
Структура проекта
Після короткого ознайомлення з функцією, яку використовуватимемо для установки hook, подивимося на структуру проекту, який продемонструє варіант підключення функції до відстеження миші.
- MouseHook.pro - профайл проекту;
- main.cpp - файл із main функцією;
- mouselogger.h - заголовний файл класу для відстеження подій миші в системі;
- mouselogger.cpp - файл вихідних кодів для відстеження подій миші в системі.
MouseHook.pro
Профайл створюється за замовчуванням.
QT += core QT -= gui CONFIG += c++11 TARGET = MouseHook CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp \ mouselogger.cpp HEADERS += \ mouselogger.h
main.cpp
У цьому файлі, щоб продемонструвати роботу сигналів і слотів, показано підключення лямбда функції до об'єкта для логування подій миші, але застосоване підключення до об'єкта з використанням патерну "Сінглтон".
#include <QCoreApplication> #include <QDebug> #include "mouselogger.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QObject::connect(&MouseLogger::instance(), &MouseLogger::mouseEvent, [](){ qDebug() << "Mouse Event"; }); return a.exec(); }
mouselogger.h
Заголовний файл класу для логування повідомлень про миші. У цьому файлі необхідно оголосити статичні методи:
- Для повернення посилання на об'єкт синглтона, через яку було підключено слот до сигналу про подію миші.
- Статичний метод mouseProc , який використовуватиметься для обробки подій.
#ifndef MOUSELOGGER_H #define MOUSELOGGER_H #include <QObject> #include <windows.h> class MouseLogger : public QObject { Q_OBJECT Q_DISABLE_COPY(MouseLogger) public: static MouseLogger &instance(); explicit MouseLogger(QObject *parent = nullptr); virtual ~MouseLogger(){} // Статический метод, который будет выступать в качестве // callback-функции static LRESULT CALLBACK mouseProc(int Code, WPARAM wParam, LPARAM lParam); signals: // Сигнал, который будет сообщать о возникновении события void mouseEvent(); public slots: private: // обработчик хука HHOOK mouseHook; }; #endif // MOUSELOGGER_H
mouselogger.cpp
#include "mouselogger.h" #include <QDebug> MouseLogger &MouseLogger::instance() { static MouseLogger _instance; return _instance; } MouseLogger::MouseLogger(QObject *parent) : QObject(parent) { HINSTANCE hInstance = GetModuleHandle(NULL); // Устанавливаем хук mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, hInstance, 0); // Проверяем, что установка хука прошла успешно if (mouseHook == NULL) { qWarning() << "Mouse Hook failed"; } } LRESULT CALLBACK MouseLogger::mouseProc(int Code, WPARAM wParam, LPARAM lParam) { Q_UNUSED(Code) // Получив событие хука, кастуем аргумент lParam // к структуре именно хука мыши. MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam; // Далее проверяем, какое именно событие пришло, // если структура не является указателем на nullptr if(pMouseStruct != nullptr) { switch (wParam) { case WM_MOUSEMOVE: qDebug() << "WM_MOUSEMOVE"; break; case WM_LBUTTONDOWN: qDebug() << "WM_LBUTTONDOWN"; break; case WM_LBUTTONUP: qDebug() << "WM_LBUTTONUP"; break; case WM_RBUTTONDOWN: qDebug() << "WM_RBUTTONDOWN"; break; case WM_RBUTTONUP: qDebug() << "WM_RBUTTONUP"; break; case WM_MBUTTONDOWN: qDebug() << "WM_MBUTTONDOWN"; break; case WM_MBUTTONUP: qDebug() << "WM_MBUTTONUP"; break; case WM_MOUSEWHEEL: qDebug() << "WM_MOUSEWHEEL"; break; default: break; } emit instance().mouseEvent(); } // После чего событие хука необходимо вернуть обратно в цепочку обработчиков return CallNextHookEx(NULL, Code, wParam, lParam); }
Підсумок
В результаті вдасться відстежувати події миші незалежно від того, чи знаходиться курсор миші у фокусі програми, якщо програма має графічний інтерфейс, чи не знаходиться у фокусі програми.