Qt WinAPI - Урок 009. SetWindowsHookEx - Логирование событий мыши через WinAPI

Hook, mouse, Qt, SetWindowsHookEx, WinAPI

Функционал WinAPI позволяет на низком уровне с помощью хуков отслеживать события системы, такие как движение и клики мыши. Данный функционал работает на основе функций callback, поэтому если хочется использовать ООП и систему сигналов и слотов , то нужно будет один из методов передавать в качестве функции callback в функцию по регистрации callback в системе Windows. Но метод должен быть статическим, поэтому требуется разрабатывать класс в качестве Синглтона.

SetWindowsHookEx

Данная функция используется для регистрации функции-обработчика событий в цепочке hook-обработчиков для отслеживания некоторых событий в системе Windows

HHOOK WINAPI SetWindowsHookEx(
  _In_ int       idHook,
  _In_ HOOKPROC  lpfn,
  _In_ HINSTANCE hMod,
  _In_ DWORD     dwThreadId
);

Параметры

idHook [in]

Тип: 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 [in]

Тип: HOOKPROC

Указатель на hook процедуру. Если dwThreadId параметр равен нулю или определяет идентификатор треда, созданного другим процессом, параметр lpfn должен указывать на подключаемую процедуру в DLL. В противном случае lpfn может указывать на подключаемую процедуру в коде, связанном с текущим процессом.

hMod [in]

Тип: HINSTANCE

Дескриптор библиотеки DLL, содержащей процедуру hook, на который указывает параметр lpfn. Параметр hMod должен быть установлен в NULL, если dwThreadId параметр определяет поток, созданный текущим процессом и если hook процедура находится в пределах кода, связанного с текущим процессом.

dwThreadId [in]

Тип: 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 хостинг.
Поддержать автора Donate

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
TT
13 июня 2019 г. 19:01
Taimoor Tanweer

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

  • Результат:66баллов,
  • Очки рейтинга-1
TT
13 июня 2019 г. 18:51
Taimoor Tanweer

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

  • Результат:75баллов,
  • Очки рейтинга2
ВМ
13 июня 2019 г. 12:30
Ваня Мороз

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

  • Результат:100баллов,
  • Очки рейтинга10
Последние комментарии
i
17 июня 2019 г. 6:10
ingenfly

Только по осям xAxis2, уAxis2 значения начинаются с 0. Почему-то xAxis2 и xAxis не синхронизированы по данным. Ну и QCustomPlot последний.
16 июня 2019 г. 20:21
Евгений Легоцкой

Добрый день. Ну точно также добавляете ту же самую информацию на ось xAxis2, только добавляете другое форматирование customPlot->xAxis2->setDateTimeFormat("hh:mm"); если я ...
EF
14 июня 2019 г. 13:56
Egor Fomin

Спасибо за ваш ответ, у меня получилось реализовать это. Тем не менее появилась другая проблема, поэтому опять надеюсь на вашу помощь. Скажем, я уже выставил точки и они соеденены. Когда я нач...
d
13 июня 2019 г. 14:47
damix

Можно классу, который описывает точку, добавить сигнал, который подавать (emit), когда точка перемещается (переопределить mouseMoveEvent или mouseReleaseEvent). Так вот эти сигналы у каждой из...
i
13 июня 2019 г. 14:09
ingenfly

Здравствайте! Подскажите, пожалуйста: customPlot->xAxis2->setTickLabels(true); //Здесь включается отображение данных на оси xAxis2. а можно как-то продублировать информацию cus...
Сейчас обсуждают на форуме
I
19 июня 2019 г. 13:41
Intruder

Всем добрый день. При разборе XML файла наткнулся на тег вот такого плана: <TagName attribute1="value1" attribute2="value2" /> При попытке проверить на наличие такого элеме...
19 июня 2019 г. 12:55
Михаиллл

Скажите пожалуйста, как его в таком случае перемещать и удалять?
18 июня 2019 г. 19:50
Дмитрий

Большое спасибо! SDK заработал.К сожалению удалось продвинутся только на один шаг. При сборке чистого проекта NDK выдаёт следующие ошибки C:\Android\ndk-bundle/toolchains/arm-linux-andr...
18 июня 2019 г. 16:59
Михаиллл

Добрый день.В этом учебнике представлен код INSTALLED_APPS = ( ... 'rest_framework', 'snippets.apps.SnippetsConfig',) На строчке 'snippets.apps.SnippetsConf...
18 июня 2019 г. 14:24
Михаиллл

Спасибо, работает.Послушаю вашего совета.
Ищу работу?
25,000.00 руб. - 30,000.00 руб.
Разработчик Qt/C++
Barnaul, Altai Krai, Russia

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы

EVILEG
О нас
Услуги
Присоединяйтесь к нам
© EVILEG 2015-2019
Рекомендует хостинг TIMEWEB