DisableWinShortcuts 的诞生
起因很简单:打游戏的时候,左手小指总会碰到 Win 键。屏幕一闪,开始菜单弹出来,游戏被切到后台,人已经没了。
网上能找到的方案大致有三种:改注册表禁用整个 Win 键、用 AutoHotkey 写脚本、装一个体积几十 MB 的「游戏模式」软件。第一种太粗暴——我还想用 Win 键打开开始菜单;后两种又太重。
于是我写了 DisableWinShortcuts:屏蔽所有 Win + 组合键,但保留单独按 Win 打开开始菜单。
思路:低级键盘钩子
Windows 提供了 SetWindowsHookEx,其中 WH_KEYBOARD_LL 可以在按键消息分发给任何程序之前拦截它。只要在回调里判断「Win 键处于按下状态,且又按下了另一个键」,就把这个按键吞掉。
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
var info = Marshal.PtrToStructure<KBDLLHOOKSTRUCT>(lParam);
bool winDown = (GetAsyncKeyState(VK_LWIN) & 0x8000) != 0
|| (GetAsyncKeyState(VK_RWIN) & 0x8000) != 0;
if (winDown && info.vkCode != VK_LWIN && info.vkCode != VK_RWIN)
return (IntPtr)1; // 吞掉这个键
}
return CallNextHookEx(_hookId, nCode, wParam, lParam);
}单独按 Win 的问题
难点在于:如果用户按下 Win、再按 D、再松开 Win,系统会认为「Win 被单独按下并松开了」,于是弹出开始菜单。解决办法是在吞掉组合键之后,补发一个无害的按键(例如 VK_NONAME),打断系统对「单独按 Win」的判断。
系统拦不住的组合
有几个组合是低级钩子管不到的:
| 组合键 | 由谁处理 | 能否屏蔽 |
|---|---|---|
| Win + L | winlogon | 否(只能用策略禁用锁屏) |
| Ctrl + Alt + Del | 安全注意序列 | 否 |
| Win + D / E / R | 资源管理器 | 可以 |
| Win + Tab | 资源管理器 | 可以 |
托盘与开机自启
程序常驻托盘,右键菜单只有三项:暂停、开机自启、退出。开机自启写在 HKCU\Software\Microsoft\Windows\CurrentVersion\Run,不需要管理员权限。
体积
便携版 16 KB,安装包 36 KB。没有运行时依赖——目标框架是 Windows 自带的 .NET Framework 4.8。关于怎么把安装包压到这么小,我另写了一篇。
补发一个无害按键这个思路太巧了,我之前一直卡在单独按 Win 会弹开始菜单的问题上。
用了一年多了,打游戏再也没被切出去过,感谢!
@小满 谢谢支持,2.1 会加白名单。
请问 Win + L 真的没办法拦吗?
低级钩子拦不住,只能通过策略禁用锁屏,表格里写了。