WPF(C#)检测显示器开关:SC_MONITORPOWER返回lParam始终相同
解决WPF中监听SC_MONITORPOWER消息时lParam始终为2的问题
我之前在WPF项目里也碰到过一模一样的坑!你遇到的问题大概率是因为WPF的窗口消息处理逻辑和传统WinForms不一样,直接在主视图里挂WndProc钩子的方式可能没正确捕获到完整的消息参数。下面给你几个可行的解决思路:
1. 用HwndSource正确拦截窗口消息
WPF窗口本身不是原生HWND,得通过HwndSource来获取并处理原生Windows消息。一定要在窗口的OnSourceInitialized方法里注册钩子,因为这时候WPF窗口的原生句柄才完全创建好,早了可能拿不到正确的消息:
protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); HwndSource source = PresentationSource.FromVisual(this) as HwndSource; source.AddHook(WndProc); } private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { const int WM_SYSCOMMAND = 0x0112; const int SC_MONITORPOWER = 0xF170; const int MONITOR_ON = -1; const int MONITOR_OFF = 2; const int MONITOR_STANDBY = 1; if (msg == WM_SYSCOMMAND) { int command = wParam.ToInt32() & 0xFFF0; if (command == SC_MONITORPOWER) { int powerState = lParam.ToInt32(); switch (powerState) { case MONITOR_ON: // 这里处理显示器开启的逻辑 break; case MONITOR_OFF: // 这里处理显示器关闭的逻辑 break; case MONITOR_STANDBY: // 这里处理显示器待机的逻辑 break; } handled = true; } } return IntPtr.Zero; }
2. 排查外部干扰因素
有时候第三方电源管理软件、系统组策略或者某些后台进程会拦截SC_MONITORPOWER消息,导致你只能收到关闭状态的通知。可以试试暂时禁用这类软件,或者在干净的系统环境里测试,排除外部干扰。
3. 结合WM_POWERBROADCAST消息辅助判断
如果SC_MONITORPOWER还是不太靠谱,可以同时监听WM_POWERBROADCAST消息(0x0218),它会在系统电源状态变化时触发,比如显示器从关闭切换到开启时,往往会伴随电源状态的变更,结合两个消息能更准确地判断显示器状态:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { const int WM_POWERBROADCAST = 0x0218; const int PBT_POWERSETTINGCHANGE = 0x8013; if (msg == WM_POWERBROADCAST && wParam.ToInt32() == PBT_POWERSETTINGCHANGE) { POWERBROADCAST_SETTING setting = Marshal.PtrToStructure<POWERBROADCAST_SETTING>(lParam); // 替换为显示器状态对应的PowerSetting GUID if (setting.PowerSetting == new Guid("{28636AA6-953D-4F34-B77E-64BD8BC90761}")) { byte[] data = new byte[setting.DataLength]; Marshal.Copy(setting.Data, data, 0, data.Length); int displayState = BitConverter.ToInt32(data, 0); // displayState为1表示开启,0表示关闭 } } // 保留之前的SC_MONITORPOWER处理逻辑... return IntPtr.Zero; } // 需要定义的互操作结构体 [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct POWERBROADCAST_SETTING { public Guid PowerSetting; public uint DataLength; public IntPtr Data; }
另外,也可以试试以管理员身份运行程序,权限不足有时候也会导致消息拦截不完整。
内容的提问来源于stack exchange,提问作者OvidiuS
相关产品推荐
相关产品推荐

