当显示器电源开/关时触发的事件

4
我正在寻找一个事件或者方法来判断屏幕是否关闭(电源选项-控制面板-关闭显示器设置)。但是这些解决方案都对我无效。所以要么我哪里错了,要么这些解决方案并不适合我。 如何获取当屏幕/显示器开启或关闭时的事件? 我期望能够得到一些线索或解决方案。问题在于我不知道该怎么做,如果你能再帮我一点就太好了。
我尝试了以下代码,但它没有起作用:
internal static class NativeMethods
{
    public static Guid GUID_MONITOR_POWER_ON = new Guid(0x02731015, 0x4510, 0x4526, 0x99, 0xE6, 0xE5, 0xA1, 0x7E, 0xBD, 0x1A, 0xEA);
    public const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
    public const int WM_POWERBROADCAST = 0x0218;
    public const int PBT_POWERSETTINGCHANGE = 0x8013;

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    public struct POWERBROADCAST_SETTING
    {
        public Guid PowerSetting;
        public uint DataLength;
        public byte Data;
    }

    [DllImport(@"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);

    [DllImport(@"User32", SetLastError = true, EntryPoint = "UnregisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
    public static extern bool UnregisterPowerSettingNotification(IntPtr handle);
}

private void WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    Debug.WriteLine("EVENT", "DEBUG");
}

public form1()
{
    NativeMethods.RegisterPowerSettingNotification(this.Handle, ref NativeMethods.GUID_MONITOR_POWER_ON, NativeMethods.DEVICE_NOTIFY_WINDOW_HANDLE);
}
2个回答

6
声明大部分是正确的,你只需要在接收到通知时处理消息。
要确保窗口句柄在传递给函数时有效,请重写 OnHandleCreated。 重写 WndProc,以接收和处理 WM_POWERBROADCAST 事件。 请注意,在 Windows 8+ 中使用的 GUID 与 Windows 7 中使用的 GUID 不同。在 Windows 8+ 中,还可使用值为 0x02POWERBROADCAST_SETTING.Data,包括监视器变暗状态; 无论如何,建议您改用此 GUID。 在调用 RegisterPowerSettingNotification 之前,可以检查 OSVersion。 此函数返回一个句柄(IntPtr),用于在之后调用 UnregisterPowerSettingNotification
第一条通知将在您的应用程序开始处理消息时发送(您应该收到一条消息,告诉您显示器已经开启:)。 请注意,这些事件是在系统开/关或调暗显示器电源时通知的,而不是在您开/关显示器电源按钮时通知的。
public partial class Form1 : Form
{
    private IntPtr unRegPowerNotify = IntPtr.Zero;

    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        var settingGuid = new NativeMethods.PowerSettingGuid();
        Guid powerGuid = IsWindows8Plus()
                       ? settingGuid.ConsoleDisplayState 
                       : settingGuid.MonitorPowerGuid;

        unRegPowerNotify = NativeMethods.RegisterPowerSettingNotification(
            this.Handle, powerGuid, NativeMethods.DEVICE_NOTIFY_WINDOW_HANDLE);
    }

    private bool IsWindows8Plus()
    {
        var version = Environment.OSVersion.Version;
        if (version.Major > 6) return true; // Windows 10+
        if (version.Major == 6 && version.Minor > 1) return true; // Windows 8+
        return false;  // Windows 7 or less
    }

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg) {
            case NativeMethods.WM_POWERBROADCAST:
                if (m.WParam == (IntPtr)NativeMethods.PBT_POWERSETTINGCHANGE)
                {
                    var settings = (NativeMethods.POWERBROADCAST_SETTING)m.GetLParam(
                        typeof(NativeMethods.POWERBROADCAST_SETTING));
                    switch (settings.Data) {
                        case 0:
                            Console.WriteLine("Monitor Power Off");
                            break;
                        case 1:
                            Console.WriteLine("Monitor Power On");
                            break;
                        case 2:
                            Console.WriteLine("Monitor Dimmed");
                            break;
                    }
                }
                m.Result = (IntPtr)1;
                break;
        }
        base.WndProc(ref m);
    }

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        NativeMethods.UnregisterPowerSettingNotification(unRegPowerNotify);
        base.OnFormClosing(e);
    }
}

NativeMethods声明

using System.Runtime.InteropServices;

public class NativeMethods
{
    internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x0;
    internal const uint DEVICE_NOTIFY_SERVICE_HANDLE = 0x1;
    internal const int WM_POWERBROADCAST = 0x0218;
    internal const int PBT_POWERSETTINGCHANGE = 0x8013;

    [DllImport("User32.dll", SetLastError = true)]
    internal static extern IntPtr RegisterPowerSettingNotification(IntPtr hWnd, [In] Guid PowerSettingGuid, uint Flags);

    [DllImport("User32.dll", SetLastError = true)]
    internal static extern bool UnregisterPowerSettingNotification(IntPtr hWnd);

    [StructLayout(LayoutKind.Sequential, Pack = 4)]
    internal struct POWERBROADCAST_SETTING
    {
        public Guid PowerSetting;
        public uint DataLength;
        public byte Data;
    }

    // https://learn.microsoft.com/en-us/windows/win32/power/power-setting-guids
    public class PowerSettingGuid
    {
        // 0=Powered by AC, 1=Powered by Battery, 2=Powered by short-term source (UPC)
        public Guid AcdcPowerSource { get; } = new Guid("5d3e9a59-e9D5-4b00-a6bd-ff34ff516548");
        // POWERBROADCAST_SETTING.Data = 1-100
        public Guid BatteryPercentageRemaining { get; } = new Guid("a7ad8041-b45a-4cae-87a3-eecbb468a9e1");
        // Windows 8+: 0=Monitor Off, 1=Monitor On, 2=Monitor Dimmed
        public Guid ConsoleDisplayState { get; } = new Guid("6fe69556-704a-47a0-8f24-c28d936fda47");
        // Windows 8+, Session 0 enabled: 0=User providing Input, 2=User Idle
        public Guid GlobalUserPresence { get; } = new Guid("786E8A1D-B427-4344-9207-09E70BDCBEA9");
        // 0=Monitor Off, 1=Monitor On.
        public Guid MonitorPowerGuid { get; } = new Guid("02731015-4510-4526-99e6-e5a17ebd1aea");
        // 0=Battery Saver Off, 1=Battery Saver On.
        public Guid PowerSavingStatus { get; } = new Guid("E00958C0-C213-4ACE-AC77-FECCED2EEEA5");

        // Windows 8+: 0=Off, 1=On, 2=Dimmed
        public Guid SessionDisplayStatus { get; } = new Guid("2B84C20E-AD23-4ddf-93DB-05FFBD7EFCA5");

        // Windows 8+, no Session 0: 0=User providing Input, 2=User Idle
        public Guid SessionUserPresence { get; } = new Guid("3C0F4548-C03F-4c4d-B9F2-237EDE686376");
        // 0=Exiting away mode 1=Entering away mode
        public Guid SystemAwaymode { get; } = new Guid("98a7f580-01f7-48aa-9c0f-44352c29e5C0");

        /* Windows 8+ */
        // POWERBROADCAST_SETTING.Data not used
        public Guid IdleBackgroundTask { get; } = new Guid(0x515C31D8, 0xF734, 0x163D, 0xA0, 0xFD, 0x11, 0xA0, 0x8C, 0x91, 0xE8, 0xF1);

        public Guid PowerSchemePersonality { get; } = new Guid(0x245D8541, 0x3943, 0x4422, 0xB0, 0x25, 0x13, 0xA7, 0x84, 0xF6, 0x79, 0xB7);

        // The Following 3 Guids are the POWERBROADCAST_SETTING.Data result of PowerSchemePersonality
        public Guid MinPowerSavings { get; } = new Guid("8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c");
        public Guid MaxPowerSavings { get; } = new Guid("a1841308-3541-4fab-bc81-f71556f20b4a");
        public Guid TypicalPowerSavings { get; } = new Guid("381b4222-f694-41f0-9685-ff5bb260df2e");
    }
}

它有效了,非常感谢。 我正在分析代码,但我认为仅获取屏幕电源事件很复杂^^ - Nomanoclass
这是在需要过滤Windows消息时的标准操作流程。当然,NativeMethods类(通常是部分类)包含在链接可重用类文件的项目中。表单处理程序通常由模型/管理器类连接/断开连接。对于WndProcm.Msg也是如此,它在其他地方(另一个专门的类)进行处理。因此,在实践中,您的表单将保持不变。如果您经常执行此类操作,则在创建项目时已经有了所有这些管理器类,因此除了一些新消息类型的代码外,没有太多要添加。 - Jimi
如果我没记错的话,有另一种方法可以使用WMI事件获得相同的通知。虽然不确定您是否认为它较不复杂。肯定有更少的代码,因为没有涉及PInvoking。 - Jimi
是的,我明白了。 我会寻找 WMI。 - Nomanoclass

1

您必须先调用 RegisterPowerSettingNotification 函数,然后才能接收到 WM_POWERBROADCAST 消息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接