none
How to correctly capture global mouse clicks

    Question

  • Hello:

    I have been trying to insert the ability to wait for a global mouse clicks into my program, I have been seeing many suggestions but nothing has worked for me thus far. I am very confused at this point. I do not know where to go from here. Any Ideas? I have the following code at this point:

    HHOOK hMouseHook;
    MSG message;

    __declspec(dllexport) LRESULT CALLBACK MouseHookWndProc (int nCode, WPARAM wParam, LPARAM lParam)
    {
    if (nCode < 0) // do not process the message
    return CallNextHookEx(hMouseHook, nCode, wParam, lParam);

    MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
    if (pMouseStruct != NULL)
    {
    if(wParam == WM_LBUTTONDOWN)
    {
    AfxMessageBox("Left");
    }
    if(wParam == WM_RBUTTONDOWN)
    {
    AfxMessageBox("Right");
    }
    }
    return CallNextHookEx(hMouseHook,nCode,wParam,lParam);
    }

    DWORD WINAPI MyMouseLogger(LPVOID lpParm)
    {
    HINSTANCE hInstance = = GetModuleHandle(NULL);
    if (!hInstance) hInstance = LoadLibrary((LPCSTR) lpParm);
    if (!hInstance) return 1;

    hMouseHook = SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseHookWndProc,hInstance,NULL);

    while(GetMessage(&message,NULL,0,0))
    {
    TranslateMessage( &message );
    DispatchMessage( &message );
    }

    UnhookWindowsHookEx(hMouseHook);
    return 0;
    }

    int CCSToolDlg::mouse()
    {
    HANDLE hThread;
    DWORD dwThread;

    hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)MyMouseLogger, (LPVOID) NULL, NULL, &dwThread);//MyMouseLogger, (LPVOID) argv[0], NULL, &dwThread);
    if (hThread)
    return WaitForSingleObject(hThread,INFINITE);
    else return 1;
    }


    Thank you so much for your time and assistance.
    Thursday, March 25, 2010 2:45 PM

Answers

  • Try my sample.

    It contains dll with hook procedure that monitors right button clicks and logs info to a file. Also, it contains dll starter.


    Nikita Leontiev
    • Marked as answer by Nancy Shao Monday, April 05, 2010 7:37 AM
    Thursday, March 25, 2010 3:49 PM

All replies

  • Try my sample.

    It contains dll with hook procedure that monitors right button clicks and logs info to a file. Also, it contains dll starter.


    Nikita Leontiev
    • Marked as answer by Nancy Shao Monday, April 05, 2010 7:37 AM
    Thursday, March 25, 2010 3:49 PM
  • Thanks for the response, but I am still not able to capture the global mouse clicks. I do not understand what I am doing wrong at this point.
    Thank you so much for your time and assistance.
    Thursday, March 25, 2010 4:54 PM
  • Have you checked my sample?
    Nikita Leontiev
    Thursday, March 25, 2010 5:36 PM
  • Yes, I took a look at your sample and tried to use it to accomplish my task, but I was not successful.

    I am trying to do the following:

    • press a button
    • wait until the next Right or Left mouse click anywhere in the window
    • after capturing the single click, continue program execution within my MFC app
    • uses click to determine pass of fail status of app
    Am I missing something? Sorry, but I am just not getting it. I am very confused at this point. I have banging my head up against the wall on this one for like a week.
    Thank you so much for your time and assistance.
    Thursday, March 25, 2010 6:13 PM
  • Nikita's sample worked fine for me. What error are you getting?

    Also, you have one too many '=' signs here (though, that may be some formatting bug):

    DWORD WINAPI MyMouseLogger(LPVOID lpParm) 
    { 
        HINSTANCE hInstance = = GetModuleHandle(NULL);

    -PaulH

    Thursday, March 25, 2010 8:17 PM
  • I was trying to take the pieces out of this sample and insert them into my program with no luck. There were no errors, but the program did not perform as expected (described above). When I click outside of the application, I am not able to identify the left and right mouse button clicks. They are only identified when the clicks occur on the console application form. Any ideas? Are you able to click outside of the form window, on the desktop for example, and see the left and right clicks?
    Thank you so much for your time and assistance.
    Friday, March 26, 2010 11:01 AM
  • Mouse Hook proc in my dll monitors right mouse button clicks anywhere. You can change this dll for your needs and use in your app to monitor mouse actions.
    Nikita Leontiev
    Friday, March 26, 2010 12:43 PM
  • 
    InfamousVJ wrote:
    > I have been trying to insert the ability to wait for a global mouse clicks into my program, I have been seeing many suggestions
    > but nothing has worked for me thus far. I am very confused at this point. I do not know where to go from here. Any Ideas? I have
    > the following code at this point: 
    >
    > DWORD WINAPI MyMouseLogger(LPVOID lpParm)
    > {
    >     HINSTANCE hInstance = = GetModuleHandle(NULL);
    >     if (!hInstance) hInstance = LoadLibrary((LPCSTR) lpParm);
    >     if (!hInstance) return 1;
    >
    >  hMouseHook = SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseHookWndProc,hInstance,NULL);
     
    A global hook must be in a DLL. The third parameter of SetWindowsHookEx must the the HINSTANCE of that DLL. You obtain yours with GetModuleHandle(NULL) - that gives you the HINSTANCE of the running EXE, not the DLL (even if called from inside a DLL). Thus, you are actually installing a local hook.
     
    See also
     
     
    the part that talks about #pragma data_seg. You need to share HHOOK handle this way: it's initialized in the EXE, but the DLL loaded into another process needs it to call CallNextHookEx.
    --
    Igor Tandetnik
    Friday, March 26, 2010 12:55 PM
  • If you consider another hook – WH_MOUSE_LL – then you do not have to put the hook procedure in a DLL [http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/d3e60bb8-aa67-46b6-a6a6-3f1ad6611f50], but your executable must have a message loop [http://msdn.microsoft.com/en-us/library/ms644986(VS.85).aspx].

     

    Friday, March 26, 2010 2:19 PM
  • This application will generally be running on an old PC...its running Windows98...i don't think WH_MOUSE_LL applies to this OS...is that correct?...i remember seeing that somewhere.
    Thank you so much for your time and assistance.
    Friday, March 26, 2010 3:03 PM
  • This application will generally be running on an old PC...its running Windows98...i don't think WH_MOUSE_LL applies to this OS...is that correct?...i remember seeing that somewhere.

    According to MSDN documentation, WH_MOUSE_LL does not seem to be available in Windows 98.

    Friday, March 26, 2010 3:08 PM
  • According to MSDN none of the hook procedures will not work on Windows 98.
    Nikita Leontiev
    Friday, March 26, 2010 3:58 PM
  • Wow...thanks for the help!

    If anyone knows of another way to achieve the desired operation, please let me know.

    Once again, here is what I was attempting to do:

    • press a button on the MFC app
    • program will wait until the operator makes a pass or fail decision by clicking either the left or right mouse button
    • a single mouse click on anywhere on the desktop would then allow to program to continue execution
    It seems SO simple to say, but yet SO complex to achieve....especially using this Win98 PC that I have to work with.
    Thank you so much for your time and assistance.
    Friday, March 26, 2010 6:01 PM
  • Wow...thanks for the help!

    If anyone knows of another way to achieve the desired operation, please let me know.

    Once again, here is what I was attempting to do:

    • press a button on the MFC app
    • program will wait until the operator makes a pass or fail decision by clicking either the left or right mouse button
    • a single mouse click on anywhere on the desktop would then allow to program to continue execution
    It seems SO simple to say, but yet SO complex to achieve....especially using this Win98 PC that I have to work with.
    Thank you so much for your time and assistance.
    Friday, March 26, 2010 6:01 PM
  • 
    Nikita Leontiev wrote:
    > According to MSDN none of the hook procedures will not work on Windows 98.
    The double negation makes it rather difficult to understand what you are trying to say. But, if you are suggesting that Windows 98 does not implement SetWindowsHookEx, you are mistaken.
     
    You can't use MSDN Online to determine availability for platforms below Win2K (the oldest platform still officially supported). MSDN Online claims CreateWindow requires Win2K - but of course it's been around since at least Windows 3.0 and probably earlier. If you need to program for Win98, you'll need a copy of MSDN dated to about that time.
     
    My October 2001 copy of MSDN says SetWindowsHookEx is supported in Win95 and up. However, certain hooks, in particular WH_MOUSE_LL, are only supported on WinNT family (WinNT, Win2000, WinXP and up).
    --
    Igor Tandetnik
    Saturday, March 27, 2010 1:08 AM
  • Igor, I was just trying to say that we can not determine minimal supported version by msdn correctly. You said the same thing, but in correct words :-)

    And I don't understand why the minimum supported versions change in online msdn. Strange politics.

    2001 MSDN says that WH_MOUSE_LL can't be used on Win 9x?


    Nikita Leontiev
    Saturday, March 27, 2010 8:49 AM
  • 
    Nikita Leontiev wrote:
    > 2001 MSDN says that WH_MOUSE_LL can't be used on Win 9x?
    Yes it does.
    --
    Igor Tandetnik
    Sunday, March 28, 2010 2:03 PM
  • So, InfamousVJ can successfully use WH_MOUSE.
    Nikita Leontiev
    Sunday, March 28, 2010 5:00 PM
  • To make sure I understand the concept, I wanted to break down the code into pieces.

    First are the declarations:

    #pragma data_seg("shared")
    #pragma comment(linker, "/section:shared,rws")
    #pragma data_seg()
    
    HHOOK hMouseHook;
    MSG message;
    HINSTANCE hInstance;
    

    One piece would be a Mouse Hook Callback function such as:

    LRESULT CALLBACK MouseHookWndProc (int nCode, WPARAM wParam, LPARAM lParam)
    {
    	if (nCode < 0)  // do not process the message 
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam); 
    
        MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
        if (pMouseStruct != NULL)
        {
    	if(wParam == WM_LBUTTONDOWN)
            {
    		AfxMessageBox("Left");
    	}
    	if(wParam == WM_RBUTTONDOWN)
            {
    		AfxMessageBox("Right");
    	}
        }
        return CallNextHookEx(hMouseHook,nCode,wParam,lParam);
    }

    Another piece would have to be added for the SetWindowsHookEx:

    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
    {
    	switch (fdwReason)
    	{ 
    	case DLL_PROCESS_ATTACH:
    		hInstance = hinstDLL;
    		break;
    	case DLL_THREAD_ATTACH:
    		break;
    	case DLL_THREAD_DETACH:
    		break;
    	case DLL_PROCESS_DETACH:
    		break;
    	}
    	return TRUE;
    }

    Then another piece placed inside the button itself would be required along the lines of:

    void CToolDlg::OnWaitForMousClick()
    {
    	hMouseHook = SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseHookWndProc,hInstance,NULL);
        
    	while(GetMessage(&message,NULL,0,0)) 
    	{
                   TranslateMessage( &message );
                   DispatchMessage( &message );
            }
    
        UnhookWindowsHookEx(hMouseHook);
    }

    I know that this is not correct or working properly, but by breaking the task into smaller pieces, I can have a better understanding of which pieces are correct and which pieces still need work when receiving any responses for help.

     

     


    Thank you so much for your time and assistance.
    Monday, March 29, 2010 1:45 PM
  • To make sure I understand the concept, I wanted to break down the code into pieces.

    First are the declarations:

    #pragma data_seg("shared")
    #pragma comment(linker, "/section:shared,rws")
    #pragma data_seg()
    
    HHOOK hMouseHook;
    MSG message;
    HINSTANCE hInstance;
    

    One piece would be a Mouse Hook Callback function such as:

    LRESULT CALLBACK MouseHookWndProc (int nCode, WPARAM wParam, LPARAM lParam)
    {
    	if (nCode < 0)  // do not process the message 
            return CallNextHookEx(hMouseHook, nCode, wParam, lParam); 
    
        MOUSEHOOKSTRUCT * pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
        if (pMouseStruct != NULL)
        {
    	if(wParam == WM_LBUTTONDOWN)
            {
    		AfxMessageBox("Left");
    	}
    	if(wParam == WM_RBUTTONDOWN)
            {
    		AfxMessageBox("Right");
    	}
        }
        return CallNextHookEx(hMouseHook,nCode,wParam,lParam);
    }

    Another piece would have to be added for the SetWindowsHookEx:

    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
    {
    	switch (fdwReason)
    	{ 
    	case DLL_PROCESS_ATTACH:
    		hInstance = hinstDLL;
    		break;
    	case DLL_THREAD_ATTACH:
    		break;
    	case DLL_THREAD_DETACH:
    		break;
    	case DLL_PROCESS_DETACH:
    		break;
    	}
    	return TRUE;
    }

    Then another piece placed inside the button itself would be required along the lines of:

    void CToolDlg::OnWaitForMousClick()
    {
    	hMouseHook = SetWindowsHookEx(WH_MOUSE,(HOOKPROC)MouseHookWndProc,hInstance,NULL);
        
    	while(GetMessage(&message,NULL,0,0)) 
    	{
                   TranslateMessage( &message );
                   DispatchMessage( &message );
            }
    
        UnhookWindowsHookEx(hMouseHook);
    }

    I know that this is not correct or working properly, but by breaking the task into smaller pieces, I can have a better understanding of which pieces are correct and which pieces still need work when receiving any responses for help.

     

     


    Thank you so much for your time and assistance.
    Monday, March 29, 2010 1:46 PM
  • In dll:

    entry point

     

    HINSTANCE hInstDLL = NULL;
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
    {
    	switch (fdwReason)
    	{ 
    	case DLL_PROCESS_ATTACH:
    		hInstDLL = hinstDLL;
    		break;
    	case DLL_THREAD_ATTACH:
    		break;
    	case DLL_THREAD_DETACH:
    		break;
    	case DLL_PROCESS_DETACH:
    		break;
    	}
    	return TRUE;
    }

     

     

    hook setting procedure

    HHOOK hMouseHook = NULL;
    
    __declspec(dllexport) void SetHook(void)
    {
    	hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseHookProc, hInstDLL, 0);
    }

    MouseHook procedure

    LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
    	if (nCode >= 0)
    	{
    		switch (wParam) 
    		{
    		case WM_LBUTTONDOWN:
    			break;
    		case WM_RBUTTONDOWN:
    			break;
    		}
    	}
    	return CallNextHookEx(hMouseHook, nCode, wParam, lParam); 
    }

     

    In application something like this:

    HINSTANCE hLib = NULL;
    typedef void (*MYPROC)();
    
    void ...::SetHook()
    {
    	hLib = LoadLibrary(L"HookDll.dll");
    	if (hLib)
    	{
    		MYPROC myproc = (MYPROC)GetProcAddress(hLib, "SetHook");
    		if (myproc)
    			myproc();
    	}
    }


    Nikita Leontiev
    Monday, March 29, 2010 2:20 PM
  • Thank you for the response!

    When I attempted to the build the code, I received the following error:

    error C2664: 'LoadLibraryA' : cannot convert parameter 1 from 'unsigned short [12]' to 'const char *'
            Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

    It pertains to the following line of code:

    hLib = LoadLibrary(L"HookDll.dll");  // replaced with hLib = LoadLibrary("HookDll.dll");

    However, when I ran the code and pressed the button, nothing happened. The only changes are the one shown above and adding message boxes to the case statements for when the mouse buttons are clicked.


    Thank you so much for your time and assistance.
    Monday, March 29, 2010 2:39 PM
  • 1. Your app uses MultiByte char set, so removing "L" is correct.

    2. Have you checked return value of LoadLibrary and GetProcAddress? Maybe they return NULL: library not found, function not found.

    What proc name are you passing to GetProcAddress? Use Dependency Walker and check SetHook function name in your dll. It can be for example "SetHook@@YAXXZ".


    Nikita Leontiev
    Monday, March 29, 2010 3:58 PM
  • I see now that the LoadLibrary is command is returning a 0 value. Using Dependency Walker displays this when the button is pusheed:

    LoadLibraryA("Hookdll.dll") called from "CS TOOL.EXE" at address 0x00404E9F.
    LoadLibraryA("Hookdll.dll") returned NULL. Error: The specified module could not be found (126).

    Where does this file come from because it does not seem to exist on my PC?


    Thank you so much for your time and assistance.
    Monday, March 29, 2010 6:39 PM
  • 1. You need to provide path to your DLL in LoadLibrary param.

    2. Open your DLL with Dependency Walker and look function name in the table. Use it for GetProcAddress.


    Nikita Leontiev
    Monday, March 29, 2010 9:47 PM