SetWindowsHookExA на функцию член класса

150
17 января 2020, 13:50

Пытаюсь отловить WM_ENTERSIZEMOVE и WM_EXITSIZEMOVE сообщения для окна, дескриптор которого является членом класса. Для этого использую SetWindowsHookExA с параметром WH_GETMESSAGE. Второй параметр функции (HOOKPROC) указывает на функцию, внутри моего класса:

class WindowDisplayHelper : // public ...
{    
public:
    // some other public methods here
    void SetMsgHook();
protected:
    LRESULT CALLBACK GetMsgProcHook(int code, WPARAM wParam, LPARAM lParam);
    static LRESULT CALLBACK MsgPoc(int code, WPARAM wParam, LPARAM lParam);
private:
    // some other private members there
    HWND m_windowHandle;
    bool m_isWindowResizing = false;
    static HHOOK m_msgHook;
    static WindowsDisplayHelperMasterWindow* m_pThis;
};

.cpp файл:

WindowDisplayHelper* WindowDisplayHelper ::m_pThis = nullptr;
HHOOK WindowDisplayHelper ::m_msgHook = NULL;
void WindowDisplayHelper ::SetMsgHook()
{
    m_pThis = this;
    m_msgHook = SetWindowsHookEx(WH_GETMESSAGE, MsgPoc, NULL, 0);
}
LRESULT CALLBACK WindowDisplayHelper::MsgPoc(int code, WPARAM wParam, LPARAM lParam)
{
    if (m_pThis != nullptr)
    {
        return m_pThis->GetMsgProcHook(code, wParam, lParam);
    }
    return CallNextHookEx(0, code, wParam, lParam);
}
LRESULT CALLBACK WindowDisplayHelper::GetMsgProcHook(int code, WPARAM wParam, LPARAM lParam)
{
    DUMPER_INFO("Hooked");
    if (code < 0)
    {
        return CallNextHookEx(0, code, wParam, lParam);
    }
    MSG* lpmsg = (MSG*)lParam;
    DUMPER_INFO("Hooked for HWND: %p. Current window %p", lpmsg->hwnd, m_windowHandle);
    if (lpmsg->hwnd != m_windowHandle)
    {
        return CallNextHookEx(0, code, wParam, lParam);
    }
    if (lpmsg->message == WM_ENTERSIZEMOVE && !m_isWindowResizing)
    {
        DUMPER_INFO("Start window resizing");
        m_isWindowResizing = true;
    }
    else if (lpmsg->message == WM_EXITSIZEMOVE && m_isWindowResizing)
    {
        DUMPER_INFO("Stop window resizing");
        m_isWindowResizing = false;
    }
    return CallNextHookEx(0, code, wParam, lParam);
}

Процес создания объекта WindowDisplayHelper:

auto helper = boost::make_shared<WindowDisplayHelper>(windowHandle);
helper->SetMsgHook();
AddDisplayHelper(displayId, helper);
return true;

И хотя я и вызываю SetMsgHook после создания объекта WindowDisplayHelper, выглядит так, будто хук не устанавливается: m_isWindowResizing все время == false и логов не видно этих. Т.к. обработчик хука находится внутри класса, то пришлось прибегнуть к приемам со статическим указателем на this, но что-то это не сработало. Пробовал указать id потока (результат GetCurrentThreadId), но это тоже не помогло. Подскажите, пожалуйста, почему не срабатывает моя функция обработки оконных сообщений?

Answer 1

Если я правильно понял ваш вопрос, то эта проблема обычно решается немного иначе. В частности, для хранения ссылки на экземпляр класса используется не статическое поле, а само окно.

Адрес экземпляра класса указывается при создании окна как аргумент CreateWindowEx:

m_hwndControl = ::CreateWindowEx(WS_EX_CLIENTEDGE, MY_WINDOW_CLASS, _TEXT(""),
        WS_CHILD | WS_TABSTOP | WS_VISIBLE,
        rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
        m_hwndParent, (HMENU)uID, hInstance, this);

А связывание с экземпляром класса выполняется в оконной процедуре.

// WndProc является статической функцией-членом!
LRESULT CALLBACK MyWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    // Get pointer to self
    MyWindow *pSelf;
    if (uMsg == WM_CREATE) {
        pSelf = (MyWindow*)(((CREATESTRUCT*)lParam)->lpCreateParams);
        ::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pSelf);
    }
    else
        pSelf = (MyWindow*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
    // Здесь вызываются классовые функции-обработчики, например так:
    // Dispatch message
    switch (uMsg) {
        HANDLE_MSG(hWnd, WM_CREATE,         pSelf->OnCreate);
        HANDLE_MSG(hWnd, WM_DESTROY,        pSelf->OnDestroy);
        HANDLE_MSG(hWnd, WM_ERASEBKGND,     pSelf->OnEraseBkgnd);
        HANDLE_MSG(hWnd, WM_PAINT,          pSelf->OnPaint);
    . . .

(HANDLE_MSG - это message cracker из windowx.h. Использовать крекеры, разумеется, не обязательно.)

В таком виде все будет корректно работать и для нескольких окон.

READ ALSO
Изменить шрифт для всего приложения

Изменить шрифт для всего приложения

Пытаюсь подключить свой шрифт для приложенияНо он подключается, только если я поверну экран или уйду на другую активити

142
Чтение файла возле JAR при работе со spring-boot

Чтение файла возле JAR при работе со spring-boot

Задача: В одном каталоге с jar-файлом лежит properties файл, который нужно прочитать

136
Проблема с библиотекой MPAndroidChart v3.1.0

Проблема с библиотекой MPAndroidChart v3.1.0

Имеется класс ScoreCardActivity где описана работа с библиотекой MPAndroidChart а именно работа с PieChart (круглая диаграмма) Студия подсвечивает красным такие...

157
Не получается вызвать onActivityResult во фрагменте

Не получается вызвать onActivityResult во фрагменте

я пытаюсь получить результат сканирования QR-кода во фрагменте, при использовании activity все работает, но после передачи, результат получить...

148