Why would Windows hooks not receive certain messages?
Asked Answered
O

3

7

Microsoft does not recommend DirectInput for keyboard and mouse input. As such, I've written an input manager class that uses SetWindowsHookEx to hook into WndProc and GetMsg. I believe the hooks are set appropriately, though they look to be the cause of various issues.

Neither my WndProc nor GetMsg hooks receive any of the messages that the actual WndProc is receiving. My input manager never receives the WM_INPUT, WM_BUTTON, WM_MOUSEWHEEL, and WM_KEY* messages that it needs.

What gives?

Partial header:

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);
    };
}

Partial source:

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);
    }
}

I don't include the code for the message handler as it consists of 149 lines, most of which are the switches for the message types. The message values received in the WndProc are not the same as the ones in my callbacks.

Overtire answered 27/1, 2011 at 16:56 Comment(2)
Is the target window within the same process?Laurenelaurens
Yes, yes it is. Everything is in a single binary.Overtire
J
6

I'm quite late to the party here, but I just spent so many hours figuring out the same problem, and hopefully someone else will find this useful.

My empirical conclusion is that DispatchMessage does not trigger WH_CALLWNDPROC hooks. In other words, messages that are posted in the thread's message queue and go through the message loop (GetMessage -> DispatchMessage) will not be caught by WH_CALLWNDPROC. It only catches messages sent directly to the window procedure with SendMessage etc.. And when you look at the documentation, that's kinda what it says:

An application-defined or library-defined callback function used with the SetWindowsHookEx function. The system calls this function before calling the window procedure to process a message sent to the thread.

And of course the opposite is true for WH_GETMESSAGE hook. It will catch posted messages but not sent messages. To get all messages you either have to use both hooks, or use subclassing to hook the window procedure directly:

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);

Also, the reason the OP's GetMessage hook wasn't working is probably because lParameter should be casted to MSG* and not CWPSTRUCT*.

Jetta answered 19/6, 2011 at 15:59 Comment(1)
Eventually returned to this issue and tried it again. Indeed, it seems as though the CWPSTRUCT* was the issue.Overtire
S
6

I can't seem to add a comment under your original question which is where I would prefer to put this but:

From what it looks like you are trying to do, wouldn't a WH_KEYBOARD and WH_MOUSE hook be more appropriate?

Scintillate answered 27/1, 2011 at 21:59 Comment(3)
You need 50 rep to be able to comment on other peoples' questions. This comment actually makes a reasonably good answer, and will get you on your way building rep.Leguminous
Aye, that would be the case, but I'd need to use the LL versions. Additionally, I need capture changed events while in a windowed mode in order to release buttons if the user leaves the window. This may be a valid work around, but I still don't understand why I'm not receiving the messages. Documentation doesn't seem to suggest anything about those two hooks not receiving input messages.Overtire
Actually, even those hooks won't do me any good, as they lack any form of identifying which window they're targeted towards, which is integral to my input manager...Overtire
J
6

I'm quite late to the party here, but I just spent so many hours figuring out the same problem, and hopefully someone else will find this useful.

My empirical conclusion is that DispatchMessage does not trigger WH_CALLWNDPROC hooks. In other words, messages that are posted in the thread's message queue and go through the message loop (GetMessage -> DispatchMessage) will not be caught by WH_CALLWNDPROC. It only catches messages sent directly to the window procedure with SendMessage etc.. And when you look at the documentation, that's kinda what it says:

An application-defined or library-defined callback function used with the SetWindowsHookEx function. The system calls this function before calling the window procedure to process a message sent to the thread.

And of course the opposite is true for WH_GETMESSAGE hook. It will catch posted messages but not sent messages. To get all messages you either have to use both hooks, or use subclassing to hook the window procedure directly:

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);

Also, the reason the OP's GetMessage hook wasn't working is probably because lParameter should be casted to MSG* and not CWPSTRUCT*.

Jetta answered 19/6, 2011 at 15:59 Comment(1)
Eventually returned to this issue and tried it again. Indeed, it seems as though the CWPSTRUCT* was the issue.Overtire
N
0

I once had a similar problem. Im not quite shure what it was (i think it was consumed somewhere in PreTranslateMessage but i'm not shure) but i know how i spotted it:

I created one of those vanishing messages myself and debugged its way thru MFC. If i remeber correctly, i just returned somewhere the wrong BOOLEAN. However, this approach might give you the actual clue.

Njord answered 24/3, 2011 at 21:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.