65.9K
CodeProject is changing. Read more.
Home

ColourGrabber2

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (4 votes)

Jun 15, 2013

CPOL

2 min read

viewsIcon

35962

downloadIcon

214

Using Windows hooks to capture mouse action.

Introduction

I was working on another project when I discovered I wanted a particular colour for an HTML style class and I had no immediate means to discover the HTML colour value. I had previously created a colour picker application (ColourGrabber) and it accomplished what I needed. However, I noticed some shortcomings with the application and decided I’d like to resolve those. The primary deficiency was that the program was a dialog-based application and used SetCapture() to capture mouse input. Unfortunately, SetCapture() confines cursor position reporting to the application/parent window and thus I couldn’t provide a preview of the colour under the cursor. After a lot of trial and error, I discovered the hook functions. ColourGrabber2 resolves the problem.

Unfortunately, as soon as I upgraded my OS to Windows 8.1, ColourGrabber2 stopped working. The solution has been a closer examination of the hook functions.

Using the Code

This project begins with a dialog based program. The primary dialog class is called CColourGrabber2Dlg. Set up two global variables...

CColourGrabber2Dlg *pDialog=NULL;
HHOOK handleHook=NULL; 

...at the beginning of CColourGrabber2.cpp.

The next step was to create the hook function immediately following the two global variables. In the previously posted version, I used the hook type WH_JOURNALRECORD. Unfortunately, I discovered that the set hook call failed in Windows 8. However, by changing the call to the Low Level mouse hook, WH_MOUSE_LL, and then changing the mouse message handling function so that wParam and lParam are handled correctly, the problem is solved. wParam contains the specific mouse message such as WM_LBUTTONDOWN and WM_MOUSEMOVE and lParam is a pointer to an MSLLHOOKSTRUCT that provides location information. Therefore, the new code for MouseProc is:

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) { 
    CPoint ptLocation;
    
    MSLLHOOKSTRUCT * Msg = (MSLLHOOKSTRUCT *)lParam; 
    ptLocation = Msg->pt;
    if (wParam == WM_LBUTTONDOWN) 
    { 
        pDialog->GrabColour(ptLocation); 
    } 
    if (wParam == WM_MOUSEMOVE)
    { 
        pDialog->ShowCoordinates(ptLocation); 
    } 
    LRESULT result = CallNextHookEx(handleHook, nCode, wParam, lParam); 
    return result; 
 } 

Notice that this is very simple. The function determines the cursor position and then processes just two messages, WM_LBUTTONDOWN and MW_MOUSEMOVE. For each of these messages, a method belonging to CColourGrabber2Dlg is called.

In CColourGrabber2Dlg::InitDialog, make the dialog accessible to the MouseProc function:

pDialog = this;  

In CColourGrabber2::OnBnClickedPickColourButton, place the statements:

handleHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC) MouseProc, GetModuleHandle(NULL), NULL); 

Now define the functions that will process the mouse information:

void CColourGrabber2Dlg::GrabColour(CPoint point)
{
    if (m_bGetPostion ) // this is a member variable set to true 
            // when the "Get Colour" button is clicked
    {
        CDC dcScreen ;
        dcScreen.CreateDC("DISPLAY", NULL, NULL, NULL);

        COLORREF crPixelColour = GetPixel(dcScreen.GetSafeHdc(), 
            point.x, point.y);
        // Do something with the information such as 
        // set a control’s background colour to the colour just
        // grabbed and report the position 

        ReleaseDC(&dcScreen);
        m_bGetPosition = false;
        UnhookWindowsHookEx(handleHook);
    }

and:

void CColourGrabber2Dlg::ShowCoordinates(CPoint point )
{
    if (m_bGetPostion)
    {
        CDC dcScreen ;
        dcScreen.CreateDC("DISPLAY", NULL, NULL, NULL);
        COLORREF crPixelColour  = GetPixel(dcScreen.GetSafeHdc(), 
            point.x, point.y);
        // Again do something with the information about the
        // position of the cursor as the mouse moves it such as
        // dynamically changing the text and background colour of
        // a static control.

        ReleaseDC(&dcScreen);
    }
}

Everything else in the project is for displaying and using the colour and position information. I hope others find this helpful.

Please also note that the project uses the Ultimate TCP-IP library which is not included with the source files.

Acknowledgements

History

  • Version 2.0 June 14, 2013: The move to using hooks provides a significant improvement to the user interface for a colour grabber.
  • Version 2.2 March 5, 2014: Version 2.0 did not work with Windows 8.1. Changes to the hook type (WH_MOUSE_LL from WH_JOURNALRECORD) and subsequent changes to the function processing the messages from the mouse have solved the problem.