You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:55:30