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 ) {
CDC dcScreen ;
dcScreen.CreateDC("DISPLAY", NULL, NULL, NULL);
COLORREF crPixelColour = GetPixel(dcScreen.GetSafeHdc(),
point.x, point.y);
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);
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.