FEATURED · 精选文章

PowerToys Mouse Utilities:四款鼠标工具的运行架构、启用链路与调试实战

发布时间 / 2026/9/7 17:28:43
来源 / 创域科博编辑部
栏目 / 资讯中心
PowerToys Mouse Utilities:四款鼠标工具的运行架构、启用链路与调试实战 PowerToys Mouse Utilities四款鼠标工具的运行架构、启用链路与调试实战【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToysMouse Utilities 是 PowerToys 中集中增强鼠标与光标体验的模块包含 Find My Mouse、Mouse Highlighter、Mouse Jump、Mouse Pointer Crosshairs 四个子工具。本文基于 Mouse Utilities 开发文档 及其四篇子工具文档结合src/modules/MouseUtils下的真实源码讲清该模块“三线程内嵌 一独立进程”的混合架构、各工具从设置 JSON 到窗口消息的完整启用链路、跨进程事件通信机制以及针对每类工具的调试方法帮助你在修改或排查鼠标工具行为时能快速定位到对应代码。一、模块总览四个子工具各解决什么问题Mouse Utilities 是 Windows 上鼠标/光标功能增强工具集四个子模块定位互不重叠子工具功能定位文档核心实现目录Find My Mouse通过聚光灯spotlight特效帮助定位鼠标指针findmymouse.mdFindMyMouseMouse Highlighter在鼠标点击瞬间绘制可定制的高亮圈mousehighlighter.mdMouseHighlighterMouse Jump通过网格覆盖层快速将光标移动到屏幕任意位置mousejump.mdMouseJumpMouse Pointer Crosshairs显示跟随光标的水平/垂直十字线mousepointer.mdMousePointerCrosshairs二、整体架构两种进程模型并存开发文档对架构的划分非常明确Find My Mouse、Mouse Highlighter、Mouse Pointer Crosshairs 三个工具作为独立线程运行在 PowerToys Runner 进程内Mouse Jump 则作为独立进程运行通过共享事件与 Runner 通信。这两种模型在源码中都有直接对应。2.1 内嵌线程模型三个轻量工具都以 PowerToy 模块 DLL 形式被 Runner 加载启用时各自detach()一个后台线程运行消息循环。例如 Mouse Pointer Crosshairsstd::thread([]() { InclusiveCrosshairsMain(hInstance, settings); }).detach();这种方式的优点是随 Runner 统一启停、无额外进程开销代价是调试时需要直接 attach 到 Runner 进程见第七节。2.2 Mouse Jump 的独立进程模型与共享事件Mouse Jump 之所以单独成进程是因为它需要全屏 WinUI3 覆盖层窗口来承载网格 UI。Runner 侧的模块实现位于 MouseJump/dllmain.cpp构造时创建两个命名事件m_hInvokeEvent CreateDefaultEvent(CommonSharedConstants::MOUSE_JUMP_SHOW_PREVIEW_EVENT); m_hTerminateEvent CreateDefaultEvent(CommonSharedConstants::TERMINATE_MOUSE_JUMP_SHARED_EVENT);这两个事件名是跨进程通信的“契约”定义在 shared_constants.h 中带有全局唯一的 GUID避免与其他应用的事件重名const wchar_t MOUSE_JUMP_SHOW_PREVIEW_EVENT[] LLocal\\MouseJumpEvent-aa0be051-3396-4976-b7ba-1a9cc7d236a5; const wchar_t TERMINATE_MOUSE_JUMP_SHARED_EVENT[] LLocal\\TerminateMouseJumpEvent-252fa337-317f-4c37-a61f-99464c3f9728;事件语义为MOUSE_JUMP_SHOW_PREVIEW_EVENT由 Runner 置位以“通知 UI 进程显示覆盖层”TERMINATE_MOUSE_JUMP_SHARED_EVENT由 Runner 置位以“通知 UI 进程清理并退出”。从当前仓库源码结构看Mouse Jump 的 UI 进程已从文档早期记载的PowerToys.MouseJumpUI.exeWinFormsMainForm.cs迁移为 WinUI3 应用dllmain.cpp 中 launch_process() 实际启动的是WinUI3Apps\PowerToys.MouseJump.WinUI3.exe对应 MouseJump.WinUI3 目录下的 PreviewWindow.xaml 覆盖层窗口。启动时仍以命令行参数传入 Runner 的 PIDGetCurrentProcessId()转宽字符串UI 进程据此反查 Runner这也是“直接调试 MouseJumpUI 很困难”的根因。启动、热键与退出链路如下enable()时预热启动 UI 进程保证首次热键按下时窗口已就绪dllmain.cpp#L259-L269按下激活快捷键时on_hotkey()被 Runner 调用若 UI 进程已死则重新拉起再用EnumWindows PID 找到 UI 窗口并SetForegroundWindow()最后SetEvent(m_hInvokeEvent)通知覆盖层显示dllmain.cpp#L287-L330源码注释特别说明此时 Runner 的热键钩子线程拥有“最后输入”状态因此SetForegroundWindow能可靠成功用户在覆盖层上选定目标点UI 进程将光标移动到该位置disable()或 PowerToys 退出时Runner 置位TERMINATE_MOUSE_JUMP_SHARED_EVENT等待进程自行退出等待WaitForSingleObject超时后TerminateProcess()强制结束dllmain.cpp#L272-L285。Mouse Jump 的默认激活快捷键为WinShiftD当设置 JSON 中没有有效的activation_shortcut时parse_hotkey() 会回退到该默认组合。三、代码结构地图设置 UI 与模块实现开发文档给出了 Mouse Utilities 的代码布局按“设置 UI”与“Runner 侧模块实现”两层组织以下路径均以仓库根目录为基准并已对照当前仓库核实设置 UISettings.UIMouseUtilsPage.xaml —— 鼠标工具设置页入口MouseJumpPanel.xaml / MouseJumpPanel.xaml.cs —— Mouse Jump 的快捷键录制面板MouseUtilsViewModel.cs / MouseUtilsViewModel_MouseJump.cs —— 设置页数据绑定Runner 侧模块实现src/modules/MouseUtilsFindMyMouseFindMyMouse.cpp 键盘钩子事件定义WinHookEventIDs.cppMouseHighlighterMouseHighlighter.cppMousePointerCrosshairsInclusiveCrosshairs.cppMouseJumpRunner 侧接口上述dllmain.cppMouseJump.CommonRunner 侧与 UI 侧共享的 C# 代码含 DPI/绘制/屏幕布局等 Helpers 与成像服务从当前源码结构看模块目录还包含 MouseJump.Models视图模型、MouseJump.HotKeys按键模型及各自的 UnitTests 工程此外还有 CursorWrap 的 C 源码与测试目录属于文档未列出的新增组件本文不展开四、Find My Mouse聚光灯定位光标Find My Mouse 基于 Raymond Chen 的 SuperSonar 工具思路通过键盘快捷键典型用法是双击 Ctrl触发时在光标位置显示一个聚光灯/涟漪动画。4.1 启用链路模块启用时创建后台线程异步运行主逻辑virtual void enable() { m_enabled true; Trace::EnableFindMyMouse(true); std::thread([]() { FindMyMouseMain(m_hModule, m_findMyMouseSettings); }).detach(); }CompositionSpotlight实例用用户设置初始化失败则记录错误并返回CompositionSpotlight sonar; sonar.ApplySettings(settings, false); if (!sonar.Initialize(hinst)) { Logger::error(Couldnt initialize a sonar instance.); return 0; } m_sonar sonar;工具通过WM_INPUT原始输入事件监听比标准鼠标事件更精确、响应更快。4.2 激活流程从钩子到动画激活过程是一条“键盘钩子 → 自定义窗口消息 → 消息处理器 → 动画”的调用链键盘钩子检测快捷键初始化时注册全局低级键盘钩子匹配双击 Ctrl 等模式后向 sonar 窗口发送WM_PRIV_SHORTCUTvirtual void OnHotkeyEx() override { Logger::trace(OnHotkeyEx()); HWND hwnd GetSonarHwnd(); if (hwnd ! nullptr) PostMessageW(hwnd, WM_PRIV_SHORTCUT, NULL, NULL); }消息处理器切换状态WM_PRIV_SHORTCUT经BaseWndProc()路由后在开/关动画间切换if (message WM_PRIV_SHORTCUT) { if (m_sonarStart NoSonar) StartSonar(); // 触发 sonar 动画 else StopSonar(); // 已在运行则取消 }聚光灯动画StartSonar()借助CompositionSpotlight在鼠标指针中心绘制涟漪动画会自动淡出也可被用户输入打断。4.3 可配置项从 FindMyMouse/dllmain.cpp 读取的设置键可以看出该工具的配置面const wchar_t JSON_KEY_ACTIVATION_METHOD[] Lactivation_method; const wchar_t JSON_KEY_INCLUDE_WIN_KEY[] Linclude_win_key; const wchar_t JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE[] Ldo_not_activate_on_game_mode; const wchar_t JSON_KEY_BACKGROUND_COLOR[] Lbackground_color; const wchar_t JSON_KEY_SPOTLIGHT_COLOR[] Lspotlight_color; const wchar_t JSON_KEY_OVERLAY_OPACITY[] Loverlay_opacity; // legacy only (migrated into color alpha) const wchar_t JSON_KEY_SPOTLIGHT_RADIUS[] Lspotlight_radius; const wchar_t JSON_KEY_ANIMATION_DURATION_MS[] Lanimation_duration_ms; const wchar_t JSON_KEY_SPOTLIGHT_INITIAL_ZOOM[] Lspotlight_initial_zoom; const wchar_t JSON_KEY_EXCLUDED_APPS[] Lexcluded_apps; const wchar_t JSON_KEY_SHAKING_MINIMUM_DISTANCE[] Lshaking_minimum_distance; const wchar_t JSON_KEY_SHAKING_INTERVAL_MS[] Lshaking_interval_ms; const wchar_t JSON_KEY_SHAKING_FACTOR[] Lshaking_factor; const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] Lactivation_shortcut;除了快捷键activation_method/activation_shortcut/include_win_key与游戏模式豁免还有“甩动shaking”触发的三个参数最小距离、间隔、方向变化因子以及聚光灯颜色、半径、动画时长、初始缩放等视觉参数overlay_opacity被标注为遗留项其功能已并入颜色 alpha 通道。事件处理方面鼠标事件可触发 sonar 动画如甩动或快捷键后键盘事件可取消/切换效果主窗口收到WM_DESTROY关闭或禁用时会清理 sonar 实例并优雅结束消息循环。五、Mouse Highlighter点击高亮Mouse Highlighter 运行在 Runner 进程内用 Windows Composition API 渲染点击时在光标周围绘制圈形指示。5.1 启用链路后台线程异步启动std::thread([]() { MouseHighlighterMain(m_hModule, m_highlightSettings); }).detach();单例Highlighter实例化并应用设置、注册窗口类Highlighter highlighter; Highlighter::instance highlighter; highlighter.ApplySettings(settings); highlighter.MyRegisterClass(hInstance);创建透明高亮窗口instance-CreateHighlighter()窗口在WM_CREATE中初始化 Composition APICompositor、visuals、target。5.2 激活流程WM_SWITCH_ACTIVATION_MODE 双态切换快捷键按下后一条自定义消息WM_SWITCH_ACTIVATION_MODE被投递到高亮窗口MouseHighlighter.cpp 的WndProc据此在开/关绘制间切换源码中该消息出现 3 处case WM_SWITCH_ACTIVATION_MODE: if (instance-m_visible) instance-StopDrawing(); else instance-StartDrawing();开启StartDrawing()将窗口置为最前、略微调整窗口尺寸以规避透明渲染 bug、显示透明绘制窗口、挂接全局鼠标钩子并开始绘制关闭StopDrawing()隐藏窗口、移除鼠标钩子、停止渲染。高亮生效后的绘制过程低级鼠标钩子捕获按钮事件 → 在光标位置绘制圆或配置的其他视觉样式→ 按用户设置随时间淡出 → 不同鼠标按键可配置不同颜色。已知问题开发文档记录了“透明度调为 0 后高亮颜色仍然滞留”的缺陷且该问题存在已久、在较新发布版中仍可复现见 mousehighlighter.md。六、Mouse Pointer Crosshairs十字线跟随光标十字线工具与高亮器同属 Runner 内嵌线程模型核心实现是 InclusiveCrosshairs.cpp。6.1 启用链路std::thread([]() { InclusiveCrosshairsMain(hInstance, settings); }).detach();InclusiveCrosshairs crosshairs; InclusiveCrosshairs::instance crosshairs; crosshairs.ApplySettings(settings, false); crosshairs.MyRegisterClass(hInstance);它通过CreateInclusiveCrosshairs()使用 Composition API 创建十字线视觉对象在WM_CREATE中初始化 Compositor并创建带WS_EX_LAYERED、WS_EX_TRANSPARENT等扩展样式的透明分层窗口用于绘制。6.2 激活流程与 StartDrawing 全貌同样由WndProc处理WM_SWITCH_ACTIVATION_MODE在m_drawing状态下切换StartDrawing()/StopDrawing()。mousepointer.md 给出的StartDrawing()实现值得完整理解——它同时处理了自动隐藏游标的状态判断void InclusiveCrosshairs::StartDrawing() { Logger::info(Start drawing crosshairs.); UpdateCrosshairsPosition(); m_hiddenCursor false; if (m_crosshairs_auto_hide) { CURSORINFO cursorInfo{}; cursorInfo.cbSize sizeof(cursorInfo); if (GetCursorInfo(cursorInfo)) { m_hiddenCursor !(cursorInfo.flags CURSOR_SHOWING); } SetAutoHideTimer(); } if (!m_hiddenCursor) { ShowWindow(m_hwnd, SW_SHOWNOACTIVATE); } m_drawing true; m_mouseHook SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, m_hinstance, 0); }要点先更新十字线位置若启用了自动隐藏用GetCursorInfo判断当前系统光标是否已被其他程序隐藏隐藏时不额外显示窗口并设置自动隐藏定时器光标可见则以SW_SHOWNOACTIVATE显示窗口不抢焦点最后挂接WH_MOUSE_LL低级鼠标钩子异步跟踪移动。StopDrawing()则移除钩子、销毁定时器、隐藏窗口并记录日志。激活期间钩子在每次WM_MOUSEMOVE上实时更新十字线位置光标无操作达到设定时长后支持自动隐藏。七、调试实战按工具分头处理开发文档的调试章节给出了针对性建议核心原则是内嵌三件套直接 attach Runner 进程Mouse Jump 需要双进程 attach。Find My Mouse / Mouse Highlighter / Mouse Pointer Crosshairs直接 attach 到 PowerToys Runner 进程调试在对应源文件设断点FindMyMouse.cpp、MouseHighlighter.cpp、InclusiveCrosshairs.cpp用激活快捷键如 Find My Mouse 的双击 Ctrl触发目标代码注意调试器开销会导致视觉特效出现卡顿/异常表现属预期现象。十字线工具尤其如此——MouseHookProc在每次WM_MOUSEMOVE都更新位置断点叠加高频更新会造成明显卡顿。Mouse Jump先调试 Runner 进程UI 进程启动后再把调试器 attach 到 MouseJumpUI/WinUI3 进程直接独立调试 UI 进程较困难它启动时必须携带 Runner 的 PID 作为参数见 dllmain.cpp 的 launch_process()。八、UI 测试自动化迁移Mouse Utilities 正在进行 UI 测试迁移以提升自动化测试覆盖。当前仓库中 MouseUtils.UITests 工程已包含四个子工具各自的测试类FindMyMouseTests.cs、MouseHighlighterTests.cs、MouseJumpTests.cs、MousePointerCrosshairsTests.cs与配置数据模型util/下的各*Settings.cs迁移进度清单见 Release-Test-Checklist-Migration-Progress.md。九、社区渊源Michael Claytonmikeclayton贡献了 Mouse Jump 的初始版本及基于其 FancyMouse 工具的多次更新Raymond ChenoldnewthingFind My Mouse 的思路源于他的 SuperSonar 工具。十、小结一张表看懂修改入口想改什么去哪里双击 Ctrl 等复杂快捷键检测FindMyMouseWinHookEventIDs.cpp定义事件点击高亮的形状/颜色/淡出MouseHighlighter.cpp十字线位置更新与自动隐藏InclusiveCrosshairs.cpp 的StartDrawing/MouseHookProcMouse Jump 跨进程事件名shared_constants.hMouse Jump 进程拉起/前台切换MouseJump/dllmain.cpp 的launch_process()/on_hotkey()覆盖层网格 UIMouseJump.WinUI3 的PreviewWindow.xaml设置页交互MouseUtilsPage.xaml 与两个 ViewModel理解这套架构的关键在于把握两条主线一是“模块 DLL 被 Runner 加载 → 后台线程 透明窗口 钩子”的内嵌模型二是“共享命名事件 独立 UI 进程”的跨进程模型。沿着WM_PRIV_SHORTCUT、WM_SWITCH_ACTIVATION_MODE这些自定义消息和Local\MouseJumpEvent-*事件名两个“契约”入手就能快速还原任意一个子工具的完整调用链。【免费下载链接】PowerToysMicrosoft PowerToys is a collection of utilities that supercharge productivity and customization on Windows项目地址: https://gitcode.com/GitHub_Trending/po/PowerToys创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻