Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C++

Your Desktop and Microsoft's SetWindowsHookEx()

Rate me:
Please Sign up or sign in to vote.
4.80/5 (23 votes)
31 Mar 2014CPOL3 min read 62.4K   56   21
How to detect Desktop mouse (double) clicks using MH_MOUSE_LL

Introduction

Since one of my applications I wrote recently needed a way to detect left mouse button double clicks on the desktop, I accepted this challenge and started to investigate. What I found on the internet and by reading the usual docs was a heap of information that I finally got sorted out, and so I'm now able to present an easy step-by-step guide for those who are about to set out on the same quest. Fear no more!

Background

Installing and using a hook to monitor messages from all around the system sounded quite complicated to me, but in the end, it (naturally) turned out to be plain simple. It was all a matter of putting the right pieces together, so to help you spare your valuable time, I present all those pieces you'll need to accomplish your task.

Please be aware, I'll present some code fragments you'll probably won't need in the first place; I included them anyway to show certain techniques. The majority of the code came out of my head, but some snippets presented here I found on the internet (or elsewhere). It's not my intention to pass them along as my own ideas, so, if you see anything that came out of your head, drop me a line and I'll throw in appropriate remarks.

Using the Code

Since I wanted to avoid code injection using a DLL, I decided to install a system wide hook that would not only deliver Desktop messages, but events from other applications too. This may seem like an overkill, but getting the Desktop sorted out is easy, once you have the HWND to its corresponding ListView. And here's how to obtain this valuable handle:

C++
// Handle to Desktop ListView, global declaration
HWND  g_hFolderView;
 ////////////////////////////////
// Find Desktop ListView, Part 1
////////////////////////////////

BOOL CALLBACK FindDLV(HWND hWndPM, LPARAM lParam)
{
   HWND hWnd = FindWindowEx(hWndPM, NULL, _T("SHELLDLL_DefView"), NULL);

   if(hWnd)
   {
      // Gotcha!
      HWND *phWnd = (HWND *)lParam;
      *phWnd      = hWnd;

      return false;
   }

   return true;
}

////////////////////////////////
// Find Desktop ListView, Part 2
////////////////////////////////

HWND FindDesktopListView()
{
    HWND hWndPM = FindWindowEx(NULL, NULL, _T("Progman"), NULL);

    if(!hWndPM)
        return NULL;

   HWND hWnd = FindWindowEx(hWndPM, NULL, _T("SHELLDLL_DefView"), NULL);

   if(!hWnd)
   {
       EnumWindows(FindDLV, LPARAM((HWND *)&hWnd));
       
       // Strange, no Desktop ListView found!?
       if(!hWnd)
           return NULL;
   }

   HWND hWndLV = FindWindowEx(hWnd, NULL, _T("SysListView32"), NULL);

   return hWndLV;
}

If all goes well, a call to FindDesktopListView() at the beginning of your application should return the HWND corresponding to the Desktop's ListView. It's likely you'll use this handle more than once, so just declare a global variable (like g_hFolderView in my case) representing the HWND.

The next thing to do is to install our hook, which is simple:

C++
HHOOK g_hDesktopHook; // Also declared global
if((g_hDesktopHook = SetWindowsHookEx(WH_MOUSE_LL, OnDTMouseEvent, NULL, 0)) == NULL)
{
    // Sorry, no hook for you...
}

We're using WH_MOUSE_LL, so we don't have to use an additional DLL for the callback function, etc. But there's a disadvantage: a (left) mouse button double click will never be reported at this low level, simply because the system doesn't even know what a double-click is down here. But we'll deal with that in our callback function:

C++
DWORD g_tcLastLeftButtonClickTime = 0; // Global declaration
/////////////////////////////////////
// Callback-Function for desktop hook
/////////////////////////////////////

LRESULT CALLBACK OnDTMouseEvent(int nCode, WPARAM wParam, LPARAM lParam)
{
    // Doesn't concern us
    if(nCode < 0)
        return CallNextHookEx(g_hDesktopHook, nCode, wParam, lParam);

    if(nCode == HC_ACTION)
    {
        // Left button pressed somewhere
        if(wParam == WM_LBUTTONDOWN)
        {
            // Check for left mouse button double click
            if(GetTickCount() < g_tcLastLeftButtonClickTime + GetDoubleClickTime())
            {
                // Event occurred on desktop
                if(WindowFromPoint(((MSLLHOOKSTRUCT *)lParam)->pt) == g_hFolderView)
                {
                    // Do something here
                }
            }
            // Save timestamp of this mouse click (maybe another one will occur)
            else
                g_tcLastLeftButtonClickTime = GetTickCount();
        }
    }    

    return CallNextHookEx(g_hDesktopHook, nCode, wParam, lParam);
}

And that's all there is to it! What the callback function does is check for (mouse) events and filter out left mouse button presses. As I mentioned above, we will never receive WM_LMBUTTONDBLCLK style events at this low level, so we have to roll our own.

As you can see, the function time stamps any left mouse button press and checks if the next click occurs within the system wide double click time, which can be adjusted using the mouse control panel. This is a reliable and convenient way to check for a double click, without using any additional timer functions, etc.

Finally, if the callback function decides a double click has occurred, it uses our g_hFolderView handle to check whether the event occurred on the desktop. (In my application, I also ensure the mouse does not hover above any desktop icon before I further process the event. This is easy when you use the ListView_GetHotItem() macro.)

And since we do things well, do not forget to unhook everything when your application exits; you can add the following to your OnDestroy() handler:

C++
if(g_hDesktopHook)
        UnhookWindowsHookEx(g_hDesktopHook);

This will release the hook and free all used resources, etc.

Final Words

I spent quite some time to gather all the information and putting the code together since I couldn't find any article presenting an example like this. So I hope it'll save you some time!

History

  • 28th April, 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Germany Germany
The first thing I did when I was born in 1966 was to start crying. For years, this was my favorite hobby, until I got my first computer in 1981. I then continued crying until I finally mastered 65xx assembler. I learned to smile then, and that's what I've been doing ever since.

Comments and Discussions

 
QuestionAnd My Vote of 5 Pin
Ron Anders8-Jan-15 17:50
Ron Anders8-Jan-15 17:50 
QuestionThank you. Pin
Ron Anders1-Jan-15 10:46
Ron Anders1-Jan-15 10:46 
QuestionThank you! Pin
FrankRiese31-Dec-14 1:42
FrankRiese31-Dec-14 1:42 
Questiondoesn't work Pin
sb13626-Jul-14 16:58
sb13626-Jul-14 16:58 
AnswerRe: doesn't work Pin
Jörg Anslik6-Jul-14 23:56
Jörg Anslik6-Jul-14 23:56 
GeneralRe: doesn't work Pin
sb13627-Jul-14 8:30
sb13627-Jul-14 8:30 
GeneralRe: doesn't work Pin
Jörg Anslik8-Jul-14 1:42
Jörg Anslik8-Jul-14 1:42 
GeneralRe: doesn't work Pin
sb13628-Jul-14 8:35
sb13628-Jul-14 8:35 
GeneralRe: doesn't work Pin
Jörg Anslik9-Jul-14 3:14
Jörg Anslik9-Jul-14 3:14 
GeneralRe: doesn't work Pin
Member 1118137429-Oct-14 7:01
Member 1118137429-Oct-14 7:01 
QuestionDesktop clicks Pin
sb13624-Jun-14 10:40
sb13624-Jun-14 10:40 
Questionerrata Pin
constm31-Mar-14 12:04
constm31-Mar-14 12:04 
AnswerRe: errata Pin
Jörg Anslik31-Mar-14 13:16
Jörg Anslik31-Mar-14 13:16 
Generaluseful Pin
Jinhuang Dai21-Nov-13 3:59
Jinhuang Dai21-Nov-13 3:59 
GeneralMy vote of 5 Pin
Dharmateja Challa10-Dec-12 21:23
Dharmateja Challa10-Dec-12 21:23 
GeneralMy vote of 5 Pin
boychester30-Aug-12 23:36
boychester30-Aug-12 23:36 
QuestionNice! Pin
boychester30-Aug-12 23:34
boychester30-Aug-12 23:34 
GeneralMy vote of 5 Pin
Bharat Chandak 1005-Jun-12 5:16
Bharat Chandak 1005-Jun-12 5:16 
GeneralReally useful API... Superb ! Pin
Bharat Chandak 1005-Jun-12 5:15
Bharat Chandak 1005-Jun-12 5:15 
GeneralMy vote of 5 Pin
prashu10012-May-11 20:40
prashu10012-May-11 20:40 
GeneralRe: My vote of 5 Pin
Jörg Anslik15-May-11 12:44
Jörg Anslik15-May-11 12:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.