【问题标题】:Events fired when the display power is switched On/Off显示器电源打开/关闭时触发的事件
【发布时间】:2019-07-07 09:18:07
【问题描述】:

我搜索一个事件,或者如果不存在,一个知道屏幕是否关闭的方法(电源选项 - 控制面板 - 关闭显示设置)。

这些解决方案都不适合我。
所以要么我在某个地方错了,要么就是不合适。

How to get the events when the screen/display goes to power OFF or ON?

我期待一些跟踪或解决方案。
问题是我不知道自己在做什么,如果你能再帮我一点,那就太好了。

我做了这个,但它不起作用:

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);
}

【问题讨论】:

    标签: c# .net winforms winapi screen


    【解决方案1】:

    声明大部分是正确的,您只需要在收到通知时处理消息即可。

    覆盖OnHandleCreated,以确保将窗口句柄传递给函数时它是有效的。
    覆盖WndProc,以接收和处理WM_POWERBROADCAST 事件。 请注意,Windows 8+ 中使用的 Guid 与 Window 7 中使用的不同。 不多,在 Windows 8+ 中也可以使用 0x02POWERBROADCAST_SETTING.Data 值,包括 Monitor Dimmed 状态;无论如何,建议您改用此 Guid。
    您可以在致电RegisterPowerSettingNotification 之前查看OSVersion
    该函数返回一个句柄(IntPtr),用于之后调用UnregisterPowerSettingNotification

    第一个通知会在您的应用程序开始处理消息后立即发送(您应该会收到一条消息,通知您 Monitor 已开启 :)。
    请注意,系统打开/关闭或调暗显示器电源时会通知这些事件,而不是在您打开/关闭显示器的电源按钮时通知。

    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://docs.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");
        }
    }
    

    【讨论】:

    • 成功了,非常感谢。我正在分析代码,但我认为仅获取屏幕电源事件很复杂^^
    • 当您需要过滤 Windows 消息时,这是标准程序。当然,NativeMethods 类(通常是部分类)包含在链接可重用类文件的项目中。表单的处理程序通常由模型/管理器类连接/未连接。 WndProcm.Msg 也是如此,它在其他地方处理(另一个专门的类)。因此,在实践中,您的表单将保持不变。如果你经常做这种事情,那么在创建项目时你已经拥有了所有这些 manager 类,所以除了一些用于新消息类型的代码之外,没有什么可添加的。
    • IIRC,还有另一种获取相同通知的方法,使用 WMI 事件。不过,不确定您是否认为它不那么复杂。由于不涉及 PInvoking,因此代码肯定更少。
    • 是的,我明白了。我会寻找 WMI。
    【解决方案2】:

    您必须先调用 RegisterPowerSettingNotification 你会收到WM_POWERBROADCAST消息

    【讨论】:

      猜你喜欢
      • 2011-01-13
      • 1970-01-01
      • 2021-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-02
      • 1970-01-01
      • 2018-05-12
      相关资源
      最近更新 更多