Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ColorCursor v. 2.2

0.00/5 (No votes)
22 Oct 2001 1  
ColorCursor v. 2.1 pops up RGB values for all visible objects found on your desktop

Sample Image - ColorCursor.jpg

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()
// Try to place a msg-hook under the queue of the window hoovered by the hotspot of the mouse to trap mouse events. It only happens if the mouse hoovers a new window.

{
	bool bNewHookNeeded = false;
	m_hWndMouseHoover_New = GetHWND_MouseHoover();
	if(m_hWndMouseHoover_Old)
	{
		// Hooked some window before.

		bNewHookNeeded = (m_hWndMouseHoover_New != m_hWndMouseHoover_Old);
	}
	else
	{
		// First hook.

		bNewHookNeeded = true;
		// Tell hook-dll who we are.

		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"));
			// Change hook proc.

			if(m_hHook)
				::UnhookWindowsHookEx(m_hHook);
			m_hHook = ::SetWindowsHookEx(WH_MOUSE, hHookProcAdr, m_hInstanceDll, dwThreadId);
			if(m_hHook)
			{
				// Tell hook-dll about new hook handle.

				FNSetHook *SetHook = (FNSetHook*)::GetProcAddress(m_hInstanceDll, _T("SetHook"));
				SetHook(m_hHook);
				// New hook set.

				return true;
			}
		}
		m_hWndMouseHoover_Old = m_hWndMouseHoover_New;
	}
	// No new hook set (old is reused (if any)).

	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) 
// The framework calls this member function after each interval specified in the SetTimer member function used to install a timer.

{
	CFrameWnd::OnTimer(uIdEvent);
	if(uIdEvent == sm_uIdTimer_Pop)
	{
		// If mouse hoovers a new window we try to hook into it now.

		HookInOtherMsgQueue();		
		// Show popup baloon.

		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)
// Call owner on interesting msg.

{
	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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here