C# WinForms托盘程序:鼠标点击可休眠显示器,快捷键触发后立即唤醒
修复快捷键触发显示器休眠后立即唤醒的问题
我开发了一个C# WinForms系统托盘应用,目标是通过点击托盘图标或Ctrl + Alt + S快捷键让所有显示器休眠。点击托盘图标时功能正常,但使用快捷键触发时,显示器会进入休眠状态后立即唤醒,即便没有触碰键盘或鼠标。
现有代码展示
显示器休眠核心实现
private int SC_MONITORPOWER = 0xF170; private uint WM_SYSCOMMAND = 0x0112; [DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private void SchnozOff() { SendMessage(this.Handle, WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)2); }
托盘图标点击事件(工作正常)
private void sysTrayIcon_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { SchnozOff(); } else if (e.Button == MouseButtons.Middle) { Application.Exit(); } }
快捷键相关代码(存在问题)
[DllImport("user32.dll")] public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc); [DllImport("user32.dll")] public static extern bool UnregisterHotKey(IntPtr hWnd, int id); private const int MYACTION_HOTKEY_ID = 1; private void RegisterHotkeys() { int Ctrl = 1; int Alt = 2; RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, Ctrl + Alt, (int)Keys.S); } protected override void WndProc(ref Message m) { if (m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID) { SchnozOff(); } base.WndProc(ref m); }
问题原因及修复方案
核心原因
快捷键触发后,系统会保留按键的按下状态,这些未处理的按键消息会在显示器休眠后被系统识别为输入操作,从而立即唤醒显示器。另外,原代码中修饰符常量的定义存在逻辑混淆(把Ctrl设为1、Alt设为2,虽然组合值正确,但不符合系统定义的常量规范)。
修复步骤及代码
- 修正修饰符常量定义
使用系统标准的修饰符常量,避免逻辑混淆:
private const int MOD_ALT = 0x0001; private const int MOD_CONTROL = 0x0002; private void RegisterHotkeys() { RegisterHotKey(this.Handle, MYACTION_HOTKEY_ID, MOD_ALT | MOD_CONTROL, (int)Keys.S); }
- 拦截并终止快捷键消息传递
在WndProc中处理完快捷键后,直接返回不调用基类方法,避免系统继续处理该按键消息:
protected override void WndProc(ref Message m) { if (m.Msg == 0x0312 && m.WParam.ToInt32() == MYACTION_HOTKEY_ID) { SchnozOff(); // 终止消息传递,避免后续处理触发唤醒 return; } base.WndProc(ref m); }
- 优化休眠指令发送逻辑
将休眠消息发送到所有顶级窗口(用IntPtr.Zero作为窗口句柄),确保所有显示器都能接收到休眠指令,同时增加短暂延迟让系统完成休眠操作:
private void SchnozOff() { // 发送消息到所有顶级窗口,覆盖所有显示器 SendMessage(IntPtr.Zero, WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)2); // 短暂延迟确保系统完成休眠 System.Threading.Thread.Sleep(100); }
通过以上修改,快捷键触发显示器休眠后就不会再出现立即唤醒的问题了。
内容的提问来源于stack exchange,提问作者Alex Dobson-Pleming
相关产品推荐
相关产品推荐

