Thread: SetWindowsHookEx - LowLevelMouseProc

  1. #1
    Registered User
    Join Date
    Sep 2009
    Posts
    6

    SetWindowsHookEx - LowLevelMouseProc

    Hello there,

    first of all: My english is not as good as it should be. So be patient if there is any confusing stuff I'm writing ;-)


    As mentioned in title bar I`ve got some problems implementing a LowLevelMouseProc callback function with SetWindowsHookEx.

    The LowLevelMouseProc function is implemented in a DLL-file which I'm loading with LoadLibrary in my main cpp-file.

    The Source code is mentioned below. The Problem is: Every time I call the function GetFrame the hook is installed successfully, but the mouse pointer is hanging (about 10 seconds) and after that my program crashes.

    Relating to MSDN documentation there is nothing I missed / typed in wrong.
    Any Idea?

    Code:
    // DLL
    
    LOWLEVELMI_API
    LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    	if( nCode == HC_ACTION )
    	{
    		MSLLData = *(reinterpret_cast<MSLLHOOKSTRUCT*>( lParam ));
    
    		switch( wParam )
    		{
    			case WM_LBUTTONDOWN:
    			{
    				*(reinterpret_cast<PULONGLONG>(&MSFrame)) = 
    					(static_cast<ULONGLONG>(MSLLData.pt.x) << 32) + MSLLData.pt.y;
    
    				MSStat = MSStat | bFlagDown;
    			}
    			break;
    
    			case WM_LBUTTONUP:
    			{
    				*(reinterpret_cast<PULONGLONG>(reinterpret_cast<PBYTE>(&MSFrame) + 8)) = 
    					(static_cast<ULONGLONG>(MSLLData.pt.x) << 32) + MSLLData.pt.y;
    
    				MSStat = MSStat | bFlagUp;
    			}
    			break;
    
    			default:
    				return CallNextHookEx(miHook, nCode, wParam, lParam);
    		}
    	}
    
    	return CallNextHookEx(miHook, nCode, wParam, lParam);
    }
    
    
    // main.cpp
    
    MSFRAME __cdecl GetFrame(void)
    {
    	MSFRAME MSFrame = {0};
    
    	LPCSTR  lpDLLPath = "..\\LowLevelMI\\Release\\LowLevelMI.dll";
    	HMODULE hDLL      = LoadLibrary(lpDLLPath);
    
    	if( hDLL != NULL )
    	{
    		typedef LRESULT (*LPLOWLEVELMOUSEPROC)(int, WPARAM, LPARAM);
    		typedef MSFRAME (*LPGETMOUSEINPUT)    (void);
    
    		LPGETMOUSEINPUT lpGetMouseInput = reinterpret_cast<LPGETMOUSEINPUT>(GetProcAddress(hDLL, "GetMouseInput"));
    		
    		HHOOK miHook = SetWindowsHookEx( 
    			WH_MOUSE_LL,
    			reinterpret_cast<HOOKPROC>(GetProcAddress(hDLL, "LowLevelMouseProc")),
    			hDLL,
    			0
    		);
    
    
    		if( miHook != NULL )
    		{
    			lpGetMouseInput();
    			
    		}
    		else
    		{
    			CHAR szError[50] = {0};
    			sprintf_s<50>( szError, "unable to set windows hook: %d", GetLastError() );
    
    			throw std::exception(szError);
    		}
    
    		UnhookWindowsHookEx(miHook);
    		FreeLibrary(hDLL);
    	}
    	else
    		throw std::exception("LowLevelMI.dll was not found");  
    
    	return MSFrame;
    }

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    I would first add error checking to all system calls (GetProcAddress). Report "GetLastError()" if anything fails.

    >> lpGetMouseInput();
    This is being called with cdecl calling convention - is it defined that way in the Dll?

    Also, WH_MOUSE_LL doesn't need to be in a DLL. It's one of those "global" hooks that always gets called within the context of containing process. Which also means that the thread which installs the hook must also provide a message loop in order for the hook to receive anything.

    LowLevelMouseProc Function ()

    gg

  3. #3
    Registered User
    Join Date
    Sep 2009
    Posts
    6
    Quote Originally Posted by Codeplug View Post
    I would first add error checking to all system calls (GetProcAddress). Report "GetLastError()" if anything fails.

    >> lpGetMouseInput();
    This is being called with cdecl calling convention - is it defined that way in the Dll?

    Also, WH_MOUSE_LL doesn't need to be in a DLL. It's one of those "global" hooks that always gets called within the context of containing process. Which also means that the thread which installs the hook must also provide a message loop in order for the hook to receive anything.

    LowLevelMouseProc Function ()

    gg
    1. I think the main part is already in good shape. I tried to call MessageBox function in both dll-functions and that works. So the functions should be called.

    2. Yes I know that there is a way to define WH_MOUSE_DLL directly in my main program but I need to call GetMouseInput from different Processes, so it might be the best choice to put into a dll (that were my thoughts)

    3. The part including the message loop is the only thing I don't understand. The message loop himself is truly not the problem. But which message I need to get?
    In due to getting the mouse input already in LowLevelMouseProc function there is no reason to get a message in message loop of my main program. This makes absolutely no sense for me.


    Hope for further help

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> ... but I need to call GetMouseInput from different Processes ...
    Quote Originally Posted by MSDN
    However, the WH_MOUSE_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event.
    DLL or no DLL - doesn't matter - it'll work the same. Not using a DLL is a lot easier to manage. However, it may be nice to have in case you switch to hook which does need to be in a DLL.
    Quote Originally Posted by MSDN
    This hook is called in the context of the thread that installed it. The call is made by sending a message to the thread that installed the hook. Therefore, the thread that installed the hook must have a message loop.
    So "lpGetMouseInput()" must implement a message loop, or your hook function will never receive any calls.

    >> But which message I need to get?
    You just need a "message pump" or "message loop". And it has to be in the thread that called SetWindowsHookEx().

    Console App Sample:
    Code:
    #define _CRT_SECURE_NO_WARNINGS
    #include <windows.h>
    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    HHOOK g_Hook;
    HANDLE g_evExit;
    
    LRESULT CALLBACK LowLevelMouseProc (int code, WPARAM wParam, LPARAM lParam)
    {
        if (code == HC_ACTION)
        {
            const char *msg;
            char msg_buff[128];
            switch (wParam)
            {
                case WM_LBUTTONDOWN: msg = "WM_LBUTTONDOWN"; break;
                case WM_LBUTTONUP: msg = "WM_LBUTTONUP"; break;
                case WM_MOUSEMOVE: msg = "WM_MOUSEMOVE"; break;
                case WM_MOUSEWHEEL: msg = "WM_MOUSEWHEEL"; break;
                case WM_MOUSEHWHEEL: msg = "WM_MOUSEHWHEEL"; break;
                case WM_RBUTTONDOWN: msg = "WM_RBUTTONDOWN"; break;
                case WM_RBUTTONUP: msg = "WM_RBUTTONUP"; break;
                default: 
                    sprintf(msg_buff, "Unknown msg: %u", wParam); 
                    msg = msg_buff;
                    break;
            }//switch
    
            const MSLLHOOKSTRUCT *p = 
                reinterpret_cast<const MSLLHOOKSTRUCT*>(lParam);
            cout << msg << " - [" << p->pt.x << ',' << p->pt.y << ']' << endl;
    
            static bool left_down = false;
            static bool right_down = false;
            switch (wParam)
            {
                case WM_LBUTTONDOWN: left_down = true; break;
                case WM_LBUTTONUP:   left_down = false; break;
                case WM_RBUTTONDOWN: right_down = true; break;
                case WM_RBUTTONUP:   right_down = false; break;
            }//switch
            
            if (left_down && right_down)
                SetEvent(g_evExit);
        }//if
    
        return CallNextHookEx(g_Hook, code, wParam, lParam); 
    }//LowLevelMouseProc
    
    
    int main()
    {
        g_evExit = CreateEvent(0, TRUE, FALSE, 0);
        if (!g_evExit)
        {
            cerr << "CreateEvent failed, le = " << GetLastError() << endl;
            return 1;
        }//if
    
        g_Hook = SetWindowsHookEx(WH_MOUSE_LL, &LowLevelMouseProc, 
                                  GetModuleHandle(0), 0);
        if (!g_Hook)
        {
            cerr << "SetWindowsHookEx() failed, le = " << GetLastError() << endl;
            return 1;
        }//if
    
        cout << "Press both left and right mouse buttons to exit..." << endl;
    
        MSG msg;
        DWORD status;
        while (1)
        {
            while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
                DispatchMessage(&msg);
    
            status = MsgWaitForMultipleObjects(1, &g_evExit, FALSE, 
                                               INFINITE, QS_ALLINPUT);
            if (status == (WAIT_OBJECT_0 + 1))
            {
                // there are messages to process, eat em up
                continue;
            }//if
            else
            {
                // assume g_evExit is signaled
                break;
            }//else
        }//while
        
        cout << "Exiting..." << endl;
        UnhookWindowsHookEx(g_Hook);
        CloseHandle(g_evExit);
        return 0;
    }//main
    gg

  5. #5
    Registered User
    Join Date
    Sep 2009
    Posts
    6
    Quote Originally Posted by Codeplug View Post
    You just need a "message pump" or "message loop". And it has to be in the thread that called SetWindowsHookEx().
    Again, excuse my non perfect english.

    First: Thank you for your very fast advice. But there is still someting I don't understand.

    So, I need to have a message loop with GetMeesage or PeekMessag. It's propably due to my absent knowledge of how this hook works in low level layer. But why I have to got another message loop in the thread which installed the hook, when I'm already getting the message by the callback function itself? My thoughts are: I'm getting the mouse input messages from the LowLevelProc function. So what kind of message I need to "eat" in the message loop? Maybe I'm slow on the uptake but this makes no sense for me. It would be very nice from you if you could explain it for me ;-)

  6. #6
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Here is how your callback is called:
    [Mouse Moves]
    1) OS sends a custom MSG to the message queue of the thread that called SetWindowsHookEx()
    2) Your message loop extracts the MSG and calls DispatchMessage()
    3) The OS processes the MSG by calling your LowLevelMouseProc()

    The only time your callback is called, is when you call DispatchMessage().

    The OS is using a message queue as a means to call your LowLevelMouseProc(). So if you don't have a message loop (and don't call DispatchMessage) then your callback will never be called.

    gg

  7. #7
    Registered User
    Join Date
    Sep 2009
    Posts
    6
    Quote Originally Posted by Codeplug View Post
    So if you don't have a message loop (and don't call DispatchMessage) then your callback will never be called.
    So this makes definitely sense to me but can't be right. Why? In due to I've already checked that my callback function was actually called from OS.

    I've tested this by implementing the MessageBox function into the begin of my callback function. And what happend? After I've installed the hook I've received my MessageBox message. So the function seems to be called.

    Understand me: I don't want to say that your explanation is wrong or something. I'm just wondering about the reason of getting this message, although I schould not.


    This is besides the reason why I didn't understand your first post.

  8. #8
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Use your debugger to set a break point in LowLevelMouseProc(). Examine the call stack. You will see that the call to your callback originated from message loop in your thread.

    Calling MessageBox() will also execute a message loop in your thread.

    gg

  9. #9
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> The only time your callback is called, is when you call DispatchMessage().
    Following my own advice, I found that PeekMessage() was actually making the call. GetMessage() probably behaves the same way.

    gg

  10. #10
    Ex scientia vera
    Join Date
    Sep 2007
    Posts
    477
    Quote Originally Posted by Codeplug View Post
    >> The only time your callback is called, is when you call DispatchMessage().
    Following my own advice, I found that PeekMessage() was actually making the call. GetMessage() probably behaves the same way.

    gg
    May I ask which Visual Studio plugins you're using?
    "What's up, Doc?"
    "'Up' is a relative concept. It has no intrinsic value."

  11. #11
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Visual Assist. I highly recommend it. Have been using it for over 10 years now.

    gg

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. SetWindowsHookEx in Win 9x/ME/NT4
    By underline_bruce in forum Windows Programming
    Replies: 7
    Last Post: 03-17-2008, 04:00 PM
  2. SetWindowsHookEx and WM_KEYBOARD_LL
    By IceDane in forum Windows Programming
    Replies: 0
    Last Post: 02-28-2008, 10:04 AM
  3. SetWindowsHookEx local WH_CALLWNDPROCRET not getting called
    By dbrien in forum Windows Programming
    Replies: 0
    Last Post: 09-13-2006, 09:57 AM
  4. can't see console win activation using setwindowshookex
    By otimist in forum Windows Programming
    Replies: 0
    Last Post: 12-16-2005, 11:54 AM
  5. mouse detecting
    By canine in forum Windows Programming
    Replies: 4
    Last Post: 07-24-2002, 02:32 AM