Evgenii Legotckoi
Evgenii Legotckoi13 червня 2016 р. 13:35

Qt WinAPI - Урок 009. SetWindowsHookEx - Реєстрація подій миші через WinAPI

Функціонал 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

Заголовний файл класу для логування повідомлень про миші. У цьому файлі необхідно оголосити статичні методи:

  1. Для повернення посилання на об'єкт синглтона, через яку було підключено слот до сигналу про подію миші.
  2. Статичний метод 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);
}

Підсумок

В результаті вдасться відстежувати події миші незалежно від того, чи знаходиться курсор миші у фокусі програми, якщо програма має графічний інтерфейс, чи не знаходиться у фокусі програми.

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
d
  • dsfs
  • 26 квітня 2024 р. 04:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
d
  • dsfs
  • 26 квітня 2024 р. 04:45

C++ - Тест 002. Константы

  • Результат:50бали,
  • Рейтинг балів-4
d
  • dsfs
  • 26 квітня 2024 р. 04:35

C++ - Тест 001. Первая программа и типы данных

  • Результат:73бали,
  • Рейтинг балів1
Останні коментарі
k
kmssr08 лютого 2024 р. 18:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 01:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 10:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 08:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 грудня 2023 р. 21:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
IscanderChe
IscanderChe30 квітня 2024 р. 04:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
G
Gar22 квітня 2024 р. 05:46
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil Academics20 квітня 2024 р. 07:45
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasov14 квітня 2024 р. 06:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 квітня 2024 р. 02:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь

Слідкуйте за нами в соціальних мережах