Почему перехватчики Windows не получают определенные сообщения?

Microsoft не рекомендует DirectInput для ввода с клавиатуры и мыши. Поэтому я написал класс диспетчера ввода, который использует SetWindowsHookEx для подключения к WndProc и GetMsg. Я считаю, что крючки установлены правильно, хотя они кажутся причиной различных проблем.

Ни мои перехватчики WndProc, ни GetMsg не получают никаких сообщений, которые получает фактический WndProc. Мой диспетчер ввода никогда не получает необходимые ему сообщения WM_INPUT, WM_BUTTON, WM_MOUSEWHEEL и WM_KEY*.

Что дает?

Частичный заголовок:

namespace InputManager
{
    class CInputManager
    {
        HWND m_Window;
        HHOOK m_WndProcHook;
        HHOOK m_GetMessageHook;
        static LRESULT CALLBACK WindowsProcedureHookProcedure(int Code, WPARAM WParameter, LPARAM LParameter);
        static LRESULT CALLBACK GetMessageHookProcedure(int Code, WPARAM WParameter, LPARAM LParameter);
        static LRESULT CALLBACK MessageHandler(HWND Window, UINT Message, WPARAM wParameter, LPARAM lParameter);
    };
}

Частичный источник:

namespace InputManager
{
    bool CInputManager::Initialize(HWND Window)
    {
        m_Window = Window;

        // Hook into the sent messages of the target window to intercept input messages.
        m_WndProcHook = SetWindowsHookEx(WH_CALLWNDPROC, &(WindowsProcedureHookProcedure), NULL, GetCurrentThreadId());
        // Hook into the posted messages of the target window to intercept input messages.
        m_GetMessageHook = SetWindowsHookEx(WH_GETMESSAGE, &(GetMessageHookProcedure), NULL, GetCurrentThreadId());

        // Register mouse device for raw input.
        RAWINPUTDEVICE RawInputDevice;
        RawInputDevice.usUsagePage = HID_USAGE_PAGE_GENERIC; 
        RawInputDevice.usUsage = HID_USAGE_GENERIC_MOUSE; 
        RawInputDevice.dwFlags = RIDEV_INPUTSINK;   
        RawInputDevice.hwndTarget = m_Window;
        return RegisterRawInputDevices(&(RawInputDevice), 1, sizeof(RawInputDevice));
    }

    void CInputManager::Shutdown()
    {
        // Unhook from the posted messages of the target window.
        UnhookWindowsHookEx(m_GetMessageHook);
        // Unhook from the sent messages of the target window.
        UnhookWindowsHookEx(m_WndProcHook);
    }

    LRESULT CALLBACK CInputManager::WindowsProcedureHookProcedure(int nCode, WPARAM wParameter, LPARAM lParameter)
    {
        if(nCode == HC_ACTION)
        {
            // Forward to message handler.
            CWPSTRUCT* Message = reinterpret_cast<CWPSTRUCT*>(lParameter);
            MessageHandler(Message->hwnd, Message->message, Message->wParam, Message->lParam);
        }
        return CallNextHookEx(NULL, nCode, wParameter, lParameter);
    }

    LRESULT CALLBACK CInputManager::GetMessageHookProcedure(int nCode, WPARAM wParameter, LPARAM lParameter)
    {
        if(nCode == HC_ACTION)
        {
            // Forward to message handler.
            CWPSTRUCT* Message = reinterpret_cast<CWPSTRUCT*>(lParameter);
            MessageHandler(Message->hwnd, Message->message, Message->wParam, Message->lParam);
        }
        return CallNextHookEx(NULL, nCode, wParameter, lParameter);
    }
}

Я не привожу код обработчика сообщений, так как он состоит из 149 строк, большинство из которых являются переключателями для типов сообщений. Значения сообщений, полученные в WndProc, не совпадают со значениями в моих обратных вызовах.


person Sion Sheevok    schedule 27.01.2011    source источник
comment
Находится ли целевое окно в том же процессе?   -  person Joel Lucsy    schedule 27.01.2011
comment
Да да это. Все в одном бинарнике.   -  person Sion Sheevok    schedule 27.01.2011


Ответы (3)


Я довольно опаздываю на вечеринку, но я потратил так много часов на решение одной и той же проблемы, и, надеюсь, кто-то еще найдет это полезным.

Мой эмпирический вывод заключается в том, что DispatchMessage не запускает перехватчики WH_CALLWNDPROC. Другими словами, сообщения, которые опубликованы в очереди сообщений потока и проходят через цикл обработки сообщений (GetMessage -> DispatchMessage), не будут перехватываться WH_CALLWNDPROC. Он перехватывает только сообщения, отправленные непосредственно оконной процедуре с помощью SendMessage и т.д.

Функция обратного вызова, определяемая приложением или библиотекой, используемая с функцией SetWindowsHookEx. Система вызывает эту функцию перед вызовом оконной процедуры для обработки сообщения, отправленного в поток.

И, конечно же, для хука WH_GETMESSAGE верно обратное. Он будет ловить опубликованные сообщения, но не отправленные сообщения. Чтобы получить все сообщения, вам нужно либо использовать оба хука, либо использовать подклассы, чтобы напрямую цеплять оконную процедуру:

WNDPROC realProc;
LRESULT CALLBACK hookProc(HWND h, UINT msg, WPARAM wp, LPARAM lp)
{
  return CallWindowProc(realProc, h, msg, wp, lp);
}
...
realProc = (WNDPROC)SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)hookProc);

Кроме того, причина, по которой хук GetMessage OP не работал, вероятно, заключается в том, что lParameter следует привести к MSG*, а не CWPSTRUCT*.

person Timo    schedule 19.06.2011
comment
В итоге вернулся к этому вопросу и попробовал еще раз. Действительно, кажется, что проблема была в CWPSTRUCT*. - person Sion Sheevok; 08.11.2011

Кажется, я не могу добавить комментарий к вашему исходному вопросу, и я бы предпочел это поставить, но:

Судя по тому, что вы пытаетесь сделать, не будет ли более подходящим хуком WH_KEYBOARD и WH_MOUSE?

person Jerry Joyce    schedule 27.01.2011
comment
Вам нужно 50 представителей, чтобы иметь возможность комментировать вопросы других людей. Этот комментарий на самом деле дает достаточно хороший ответ и поможет вам построить репутацию. - person Ben Voigt; 28.01.2011
comment
Да, это было бы так, но мне нужно было бы использовать версии LL. Кроме того, мне нужно фиксировать измененные события в оконном режиме, чтобы отпускать кнопки, если пользователь покидает окно. Это может быть правильным решением, но я до сих пор не понимаю, почему я не получаю сообщения. Документация, похоже, не предлагает ничего об этих двух ловушках, не получающих входные сообщения. - person Sion Sheevok; 28.01.2011
comment
На самом деле, даже эти хуки не принесут мне никакой пользы, поскольку в них отсутствует какая-либо форма определения того, на какое окно они нацелены, что является неотъемлемой частью моего диспетчера ввода... - person Sion Sheevok; 29.01.2011

У меня когда-то была похожая проблема. Я не совсем уверен, что это было (я думаю, что это было использовано где-то в PreTranslateMessage, но я не уверен), но я знаю, как я это заметил:

Я сам создал одно из этих исчезающих сообщений и отладил его через MFC. Если я правильно помню, я только что где-то вернул неправильный BOOLEAN. Тем не менее, этот подход может дать вам настоящую подсказку.

person rhavin    schedule 24.03.2011