65.9K
CodeProject 正在变化。 阅读更多。
Home

ColorCursor v. 2.2

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.40/5 (4投票s)

2001年10月23日

3分钟阅读

viewsIcon

100649

downloadIcon

844

ColorCursor v. 2.1 弹出显示桌面上所有可见对象的RGB值。

Sample Image - ColorCursor.jpg

引言

ColorCursor 2.2 是一款仅在托盘区运行的隐形应用程序,它会弹出显示桌面所有可见对象的RGB值——逐像素显示光标热区下的颜色。

运行 cc.exe 后,你会看到一个伍德斯托克图标,或者顶部带有红线的伍德斯托克图标。这表示 ColorCursor 正在运行/暂停 (1)——普通图标表示“未暂停”,带红线的图标表示“暂停”。你可以通过伍德斯托克图标的右键菜单 (2) 与其交互。我认为菜单中的大多数选项都一目了然。

菜单包含几个部分。第一部分控制你记录“颜色像素”的方式。ColorCursor 允许你追踪鼠标移动,我称之为记录。这没什么特别的(我稍后会解释其工作原理)。第二部分设置 RGB 信息的格式。第三部分控制气泡提示的行为。

这主要涉及到连接其他进程的消息队列。我在这个双工作区(一部分用于应用程序,一部分用于 hook-dll)中为此编写了一个函数——

const bool HookInOtherMsgQueue();

以及这样的实现——

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;
}

这是我 (MFC) 项目中最有趣的部分。我并不认为这完美无缺,但它确实有效。

关键在于,ColorCursor 在执行并处于未暂停状态时会设置一个 100ms 的计时器。在 `WM_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(...)` 被调用 (TT == ToolTip)。它准备要显示的气泡信息,并从中调用重写的 `TTShow(const CString &cstrTTT)` (TTT == ToolTipText),并且仅当 (iff) 鼠标移动到新的像素时才会弹出新的 TT。为了能够按需快速显示 TT,我创建了一个通过代理的 TT 类,它模拟了“真实”的 TT(并且是“代理”,因为它不是基于原生 TT 类,而是基于框架窗口,事实上是从头开始构建的)。除了这个项目之外,你还可以获得这个类以及其他实用程序类和函数(参见库“extra”)。我不会详细介绍它的工作原理——你必须自己检查(或者问我)。

图片 #3 显示了通过代理类创建的气泡。图片 #4 是 RGB 日志的一部分。为了避免愚蠢的问题,我在显示日志时让应用程序进入强制暂停状态。也许你可以做得更好——请原谅……

我创建了 `CCcFrameWnd::MouseTrap(...)` 事件处理程序来捕获和过滤我想响应的事件。它的一个对应部分在 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);
}

当然,这一切都是因为——

m_hHook = ::SetWindowsHookEx(WH_MOUSE, hHookProcAdr, m_hInstanceDll, dwThreadId);

在 `CCcFrameWnd::HookInOtherMsgQueue()` 函数中。

我未解决的一个问题是,为什么拥有窗口的 `hWnd` 与在 hook-dll 中调用 `::FindWindow(NULL, I_Constant_Title);` 的 `HWND` 不匹配。但这并不是一个大问题——但我无法理解为什么我无法在我的 hook-dll 中告知拥有窗口的 hwnd,即获取下一个消息的 `hWnd`——换句话说,在 hook-into-fct 内部获取 cc(安全障碍?)。我尝试这样做——但我恐怕没有取得多大成功。相反,我必须使用依赖于标题的 `::FindWindow(...)`,我觉得这是一个糟糕的替代方案。

希望你喜欢——我期待着看到你们对此的评价。

© . All rights reserved.