ColorCursor v. 2.2






4.40/5 (4投票s)
2001年10月23日
3分钟阅读

100649

844
ColorCursor v. 2.1 弹出显示桌面上所有可见对象的RGB值。
引言
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(...)`,我觉得这是一个糟糕的替代方案。
希望你喜欢——我期待着看到你们对此的评价。