本文由Qwen 3.8 Max辅助写作。
- 原因:这是 Windows 11 26220 这个新构建引入的内核行为变化。系统内核重构了光标管理,把上述 6 个光标角色从经典指针方案流程里挪走、改走辅助功能管线,导致换方案的广播(
SPI_SETCURSORS)对这 6 个槽位完全失效,控制面板点应用的底层调用更是直接报错失败。同一套操作在旧构建 26200 上完全正常。 - 解决:绕开失效的广播,用
按当前 DPI 尺寸加载光标 + 直接注入内核槽位的方式写入,实测在 26200 和 26220 上都稳定生效。文章末尾的 CursorTester 修复工具可以帮到你。
使用ProcMon抓一次 SPI_SETCURSORS,在32000多条事件里可以发现对 Control Panel\Cursors 的注册表读取、对任何 .cur/.ani 文件的打开都是0条。
也就是说内核在做光标刷新时根本没读注册表、没碰文件,它走的是自己的配置解析机制。
用 Windows Kits 里的 CDB 把 win32kfull.sys 反汇编,顺调用链一直检查,发现:
user32!SystemParametersInfoW
→ user32!RealSystemParametersInfoW
→ win32u!NtUserSystemParametersInfo (syscall 0x103D)
→ win32kfull!xxxSystemParametersInfoWorker
→ xxxRestoreMouseCursors
→ xxxUpdateSystemCursorFromRegistry ← 每个槽位的刷新逻辑
xxxUpdateSystemCursorFromRegistry 里有决定性分支:
call FastGetProfileStringFromIDW ; 按“名字ID”从内核配置缓存读注册表值
cmp word ptr [rsp+60h], 0 ; 读到的字符串是不是空?
je +0x243 ; 空 → 走回退分支
; ... ; 非空 → 用户态 LoadImage 加载真实文件
+0x243:
lea eax, [rsi+64h] ; 回退:资源ID = 槽位号 + 100
; → 从 user32.dll 内置资源加载 1-bit 单色光标
在用户态 user32!_ClientLoadImage 下断点,可以看到内核回调用户态时传了什么:
- 失败槽位收到的是纯资源 ID(0x64=100、0x66=102 这种);
- 成功槽位收到的是真实路径字符串(内存里能直接看到
C:\WINDOWS\C...的 Unicode 序列)。
可见 6 个槽位读空 → 回退内置单色
把两组角色的注册表指向故意对调:让 6 个异常槽位的注册表指向系统自带的 Aero 彩色文件,让 8 个正常槽位的注册表指向第三方 BA mini 文件,然后触发刷新,看内核到底听注册表的,还是听阿三的:
如果 6 个槽位按注册表变成 Aero 彩色 → 说明它们还在走经典方案流程;如果 6 个槽位依旧单色 → 说明它们根本不读注册表,被另一条流程接管。
用注册表把 6 个异常槽位写成 Aero 彩色路径、8 个正常槽位写成第三方路径,再触发 SPI_SETCURSORS 刷新,得到如下结果:
Arrow* 注册表=Aero彩色 → 实测 MONO (10,10) 内核无视注册表
Help* 注册表=Aero彩色 → 实测 MONO (10,10) 内核无视注册表
Wait* 注册表=Aero彩色 → 实测 MONO (10,10) 内核无视注册表
Cross* 注册表=Aero彩色 → 实测 MONO (16,16) 内核无视注册表
No* 注册表=Aero彩色 → 实测 MONO (10,10) 内核无视注册表
Hand* 注册表=Aero彩色 → 实测 MONO (10,10) 内核无视注册表
SizeNS 注册表=BA mini → 实测 COLOR (15,15) 正常读注册表
IBeam 注册表=BA mini → 实测 COLOR (15,15) 正常读注册表
这 6 个槽位已经被从经典方案流程里移除了:注册表里哪怕是合法的彩色路径,内核也不读,直接回退内置单色。而剩下 8 个角色照常读注册表。
这 6 个槽位的集合,跟 Windows 11 辅助功能鼠标指针机制在内核里接管的角色集合逐位重合。
测试fWinIni 参数矩阵,可以得到了如下的结果:
| 调用方式 | 返回值 | 错误码 |
|---|---|---|
fWinIni=0 |
TRUE | — |
fWinIni=2(SENDCHANGE) |
TRUE | — |
fWinIni=1(UPDATEINIFILE) |
FALSE | 6(ERROR_INVALID_HANDLE) |
fWinIni=3(main.cpl 实际用法) |
FALSE | 6 |
main.cpl 点“应用”用的正是 fWinIni=3。也就是说本机上控制面板点一下,底层调用直接以 ERROR_INVALID_HANDLE 失败。这就是控制面板点了没反应的直接原因。而且管理员权限下同样复现,跟权限无关。
更新的系统内核应该重构了光标管理,有意让这 6 个角色改走辅助功能那个流程。
至此,ai做的已经够多了,思路也非常清晰:SPI_SETCURSORS 这条广播路径在新构建上对 6 个槽位失效,解决方式自然是不走它:
// 1. 按当前 DPI 基准尺寸加载第三方光标(避免 128px 图层,WorkBuddy 的坑)
int cx = GetSystemMetrics(SM_CXCURSOR);
int cy = GetSystemMetrics(SM_CYCURSOR);
IntPtr h = LoadImage(IntPtr.Zero, path, IMAGE_CURSOR, cx, cy, LR_LOADFROMFILE);
// 2. 直接注入内核活动槽位,绕开辅助功能管线的短路
SetSystemCursor(h, OCR_NORMAL); // 32512,其余角色同理
这条路径完全绕开失效的广播和写回逻辑,在 26200 和 26220 上都能够稳定生效,软件下载如下:
点我下载
这个软件能干什么:
- 14 个光标槽位实时监测:一眼看出你的 6 个槽位是不是也被锁成了 MONO 1bpp 单色,分辨率、热点、句柄全都有;
- 一键修复:内置 DPI 自适应加载 +
SetSystemCursor直接槽位注入,绕开失效的换方案广播,把 6 个被锁死的角色掰回你要的光标,26200 / 26220 都实测有效; - 一键恢复出厂:想回归 Windows Aero 默认方案也行,注册表和活动光标一起恢复;
- 出厂基准比对:注册表、无障碍缓存、云同步、登录模板逐项对照纯净基准,顺带查策略锁定;
- 冲突进程排查:向日葵、ToDesk、远控/虚拟驱动这类可能拦截光标的软件自动扫一遍。
使用方法:解压后双击运行,打开实时监测页确认 6 个槽位是不是 MONO;是的话到基准与复现页用一键恢复/槽位注入即可。如果双击没反应,先装一下 .NET 8 Desktop Runtime再运行即可。
源码本来该一起传 GitHub 的——结果 GitHub 最近毫无征兆地把我的号封了,说我开多个个人号违反ToS了。现在还在走绝赞申诉流程。等解封之后再把 CursorTester 的完整源码传上去吧,到时候会在这里补上仓库地址。
现在的ai真的太方便了。我本身啥也不懂,仅仅提供了一些浅薄的思路,ai便能理解、完成对应的任务。上面的工具、假设、验证全部都是用ai完成的,我也没啥留图片的习惯,随手编写了这篇文章就当我是斯里尼瓦萨·拉马努詹就行。ai太好用了你知道吗。

铸币来了,下载链接:https://blog.cartol.top/fileShare/CursorTester.exe
附件下载Argon原来没有,搞半天还要自己做(