Introduction
ColorCursor 2.2 is a invisible tray-only application that pops up RGB values for all visible objects found on your desktop - pixel by pixel under the nose of your cursor (also known as its hot spot).
Starting
cc.exe gives you a Woodstock or a Woodstock with a red line on top. This signals that ColorCursor is running/sleeping (1) - a normal icon means 'not-paused' and one with a red line means 'paused'. Interacting with it goes on from the Woodstock ctx. menu (2). Most items here are self explanatory I think.
On the menu you have several sections. The first one controls the way you log "color-pixels". ColorCursor enables you to track down mouse moves. This I refer to as recording. Nothing surprising about that (I'll come back to how it works in a moment). Next section sets up the format of the RBG-info. Third section controls the
behavior of the ballooning.
It's all about hooking into other message queues of other processes. I've made a function for that in this dual workspace (one part for the app. and one for the hook-dll) called -
const bool HookInOtherMsgQueue();
And implementation like this -
const bool CCcFrameWnd::HookInOtherMsgQueue()
{
bool bNewHookNeeded = false;
m_hWndMouseHoover_New = GetHWND_MouseHoover();
if(m_hWndMouseHoover_Old)
{
bNewHookNeeded = (m_hWndMouseHoover_New != m_hWndMouseHoover_Old);
}
else
{
bNewHookNeeded = true;
FNSetHook_Owner *SetHook_Owner = (FNSetHook_Owner*)::GetProcAddress(m_hInstanceDll, _T("SetHook_Owner"));
SetHook_Owner(m_hWnd);
}
if(bNewHookNeeded)
{
DWORD dwThreadId = 0,
dwProcessId = 0;
dwThreadId = ::GetWindowThreadProcessId(m_hWndMouseHoover_New, &dwProcessId);
if(dwThreadId)
{
HOOKPROC hHookProcAdr = (HOOKPROC)::GetProcAddress(m_hInstanceDll, _T("MouseTrap"));
if(m_hHook)
::UnhookWindowsHookEx(m_hHook);
m_hHook = ::SetWindowsHookEx(WH_MOUSE, hHookProcAdr, m_hInstanceDll, dwThreadId);
if(m_hHook)
{
FNSetHook *SetHook = (FNSetHook*)::GetProcAddress(m_hInstanceDll, _T("SetHook"));
SetHook(m_hHook);
return true;
}
}
m_hWndMouseHoover_Old = m_hWndMouseHoover_New;
}
return false;
}
That's the far most interesting one in my (MFC) project. I don't say that this is indeed perfect but it works.
The point is that ColorCursor sets up a 100ms-timer when executed and not paused. On WM_TIMER
events
it asks if the mouse now hoovers a new window and, if so, it tries to hook into its msg. queue - tapping mouse events
from it. What I'm interested here is only when L- and R- client and NonClient events go in for mouse-buttons. Now back
to the timer -
void CCcFrameWnd::OnTimer(UINT uIdEvent)
{
CFrameWnd::OnTimer(uIdEvent);
if(uIdEvent == sm_uIdTimer_Pop)
{
HookInOtherMsgQueue();
TTShow();
}
}
TTShow(...)
is called (TT == ToolTip). It prepares the balloon info for
display and the overridden TTShow(const CString &cstrTTT)
(TTT == ToolTipText) is called
from it and pops the new TT only if (iff) it's now moved to a new pixel. To be able to show fast TT on
demand I've made a by-proxy TT-class imitating a "real" TT (and 'proxy' because not based on native TT
classes but on frame wnd's, in fact from ground up). This class along with other util. classes and functions
(see lib. 'extra'), you get in addition to this project. I'll not go into details about how that works - you'll
have to check that out yourself (or ask me).
Picture #3 shows you a balloon from the by-proxy class. Picture #4 is part of a RGB-log. To avoid stupid problems
I let the app. go into a forced pause state on showing the log. Perhaps you can do better - forgive...
I've made the CCcFrameWnd::MouseTrap(...)
event handler catch and filter the events I'll like to
respond. A counterpart to it initiates it all inside the hook-dll -
LRESULT CALLBACK MouseTrap(int iCode, WPARAM wParam, LPARAM lParam)
{
if(iCode == HC_ACTION)
{
HWND hWnd = ::FindWindow(NULL, I_Constant_Title);
if(hWnd && ::IsWindow(hWnd))
{
::PostMessage(hWnd, WM_MGSTRAP, wParam, lParam);
}
}
return ::CallNextHookEx(g_hHook, iCode, wParam, lParam);
}
And naturally it all takes place because of the -
m_hHook = ::SetWindowsHookEx(WH_MOUSE, hHookProcAdr, m_hInstanceDll, dwThreadId);
In the CCcFrameWnd::HookInOtherMsgQueue()
function.
One problem that I dit not solve was why the hWnd
of the owner window did not match up the
HWND
of calling ::FindWindow(NULL, I_Constant_Title);
in the hook-dll. This is
not a huge problem though - but I can not understand why I can't tell my hook-dll about the owner hwnd,
the hWnd
to get the msg. next - cc in other words, from inside the hook-into-fct. (security obstruction?)
I try to do that - but without much luck I'm afraid. Instead I have to use the title
depended ::FindWindow(...)
and that's a poor alternative I feel.
Enjoy - I'm looking forward to see what you all have to say about this.