当计算机从睡眠/休眠模式恢复时,如何捕获事件?

Eth*_*n.S 3 .net c# winapi .net-4.5 windows-10

我有一个在 .net 4.5(仅)上运行的控制台应用程序。我试图检测计算机何时从睡眠/休眠模式返回。我尝试使用,Win32.SystemEvents.PowerModeChanged但由于某种原因它不起作用...我使用运行 Windows 10 的 ThinkPad 笔记本电脑,当我拔下充电线时,它确实会触发带有参数 Mode = 的事件PowerModes.StatusChange

class Program
{
    static void Main(string[] args)
    {
        SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(SystemEvents_PowerModeChanged);
        Console.WriteLine("This application is waiting for system events.");
        Console.WriteLine("Press <Enter> to terminate this application.");
        Console.ReadLine();
    }

    private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
    {
        Console.WriteLine(Enum.GetName(typeof(PowerModes), e.Mode));
        File.WriteAllText("test.txt", "test");
    }
}
Run Code Online (Sandbox Code Playgroud)

尝试打印到屏幕并写入文件,无法使其工作......

如果有人有想法或不同的方法,最后我需要捕捉计算机何时从睡眠或休眠状态返回。

Eth*_*n.S 7

解决了:

从 Windows 10 开始,微软添加了现代待机功能,扩展了 Windows 8.1 连接待机电源模型。 SystemEvents.PowerModeChanged.net 4.5 中仅支持传统睡眠和休眠 (S1-4)。

从 Windows 10 版本 2004 开始,现代待机被强制且无法禁用,SystemEvents.PowerModeChanged在我的情况下毫无用处。

此处引用了用于处理现代待机电源模式更改的新 Win32 API: PowerRegisterSuspendResumeNotification 函数 MSDN

不幸的是,我没有找到新 API 的完整 C# 实现。

User32.dllSoo 我自己使用Dahall (GitHub) 的 Vanara 项目PowrPorf.dll的C# 包装器制作了一个:

public static class SystemPowerNotifications
    {
        public static event SystemPowerNotificationEventHandler PowerModeChanged 
        { 
            add
            {                           
                _powerModeChanged += value;
                if (_eventHandler == null)
                {
                    var result = PowrProf.PowerRegisterSuspendResumeNotification(PowrProf.RegisterSuspendResumeNotificationFlags.DEVICE_NOTIFY_CALLBACK,
                    _dnsp, out _eventHandler);
                    if (result != Win32Error.ERROR_SUCCESS)
                        throw new Exception();
                }
            } 
            remove 
            {
                _powerModeChanged -= value;
                if(_powerModeChanged.GetInvocationList().Length == 0)
                {
                    if (PowrProf.PowerUnregisterSuspendResumeNotification(_eventHandler) != Win32Error.NO_ERROR)
                        throw new Exception();
                    _eventHandler.Dispose();
                    _eventHandler = null;
                }       
            }
        }

        private static PowrProf.SafeHPOWERNOTIFY _eventHandler;
        private static PowrProf.DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS _dnsp = new PowrProf.DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
        {
            Callback = OnDeviceNotify,
            Context = IntPtr.Zero
        };
        private static Win32Error OnDeviceNotify(IntPtr context, uint type, IntPtr setting)
        {
            _powerModeChanged?.Invoke(null,new PowerNotificationArgs((PowerBroadcastType)type));
            return 0;
        }
        private static SystemPowerNotificationEventHandler _powerModeChanged;
    }
Run Code Online (Sandbox Code Playgroud)

完整源代码: SystemPowerModeNotification-dotnet4.5 (GitHub)

编辑:

在 Windows 服务中使用它时,注册的回调函数PowerRegisterSuspendResumeNotification仅在进入休眠模式时才会触发,而在现代待机睡眠/监视器关闭时不会触发。

您需要注册到RegisterPowerSettingNotification此处引用的不同通知:注册电源事件 MSDN 以及检查PowerEvent监视器状态。请记住,即使计算机进入显示器关闭/打开状态而不进入睡眠模式,也会发生这种情况。

注册示例:

public static event SystemPowerNotificationEventHandler PowerModeChanged
        {
            add
            {
                _powerModeChanged += value;
                if (_powerEventHandler == null)
                {
                    if (!string.IsNullOrEmpty(ServiceName))
                    {
                        if (_ssh.IsNull)
                            _ssh = AdvApi32.RegisterServiceCtrlHandlerEx(ServiceName, OnDisplayNotify);
                        if (_ssh.IsNull)
                            throw new Exception("Failed To Register ServiceCtrlHandlerEx");
                        _displayEventHandler = User32.RegisterPowerSettingNotification(((IntPtr)_ssh), PowrProf.GUID_MONITOR_POWER_ON, User32.DEVICE_NOTIFY.DEVICE_NOTIFY_SERVICE_HANDLE);
                        if (_displayEventHandler.IsNull)
                            throw new Exception("Failed To Register PowerSettingNotification");
                    }

                    var result = PowrProf.PowerRegisterSuspendResumeNotification(PowrProf.RegisterSuspendResumeNotificationFlags.DEVICE_NOTIFY_CALLBACK,
                    _dnsp, out _powerEventHandler);
                    if (result != Win32Error.ERROR_SUCCESS)
                        throw new Exception("Failed To Register PowerSuspendResumeNotification");
                }

            }
            remove
            {
                _powerModeChanged -= value;
                if (_powerModeChanged == null)
                {
                    if (!string.IsNullOrEmpty(ServiceName))
                    {
                        if (!User32.UnregisterPowerSettingNotification(_displayEventHandler))
                            throw new Exception("Failed To Unregister PowerSettingNotification");
                        _displayEventHandler.Dispose();
                        _displayEventHandler = null;
                    }

                    if (PowrProf.PowerUnregisterSuspendResumeNotification(_powerEventHandler) != Win32Error.NO_ERROR)
                        throw new Exception("Failed To Unregister PowerSuspendResumeNotification");
                    _powerEventHandler.Dispose();
                    _powerEventHandler = null;
                }
            }
        }
Run Code Online (Sandbox Code Playgroud)

回调示例:

private static Win32Error OnDisplayNotify(AdvApi32.ServiceControl control,uint eventType,IntPtr eventData,IntPtr context)
        {
            var dataHandle = new HANDLE(eventData);
            var contextHandle = new HANDLE(context);
            if(control == AdvApi32.ServiceControl.SERVICE_CONTROL_POWEREVENT)
            {
                POWERBRODCAST_SETTING settings = (POWERBRODCAST_SETTING)Marshal.PtrToStructure(eventData, typeof(POWERBRODCAST_SETTING));
                _powerModeChanged?.Invoke(null, new PowerNotificationArgs((PowerBroadcastType)eventType,settings.Data));
            }
            
            return 0;
        }
Run Code Online (Sandbox Code Playgroud)