Home > other >  How can I catch an event when the computer resume from sleep/hibernate mode in .net 4.5?
How can I catch an event when the computer resume from sleep/hibernate mode in .net 4.5?

Time:12-10

I have a console application running on .net 4.5 (only). I am trying to detect when the computer return from sleep/hibernate mode. I tried using Win32.SystemEvents.PowerModeChanged but for some reason it doesn't work... I am using ThinkPad laptop running windows 10, When I unplug the charging cable it does fire the event with the argument 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");
        }
    }

Tried printing to the screen and writing to a file, couldn't managed to make it work...

Please if someone have an idea or a different approach, in the end I need to catch when the computer returns from sleep or hibernate.

CodePudding user response:

SOLVED:

From windows 10, microsoft added Modern Standby that expands the Windows 8.1 Connected Standby power model. SystemEvents.PowerModeChanged in .net 4.5 only supports Traditional Sleep and Hibernate (S1-4).

From windows 10, version 2004 Modern Standby is forced and cannot be disabled, renders SystemEvents.PowerModeChanged useless in my case.

The new Win32 API for handling Modern Standby power mode changes is referenced here: PowerRegisterSuspendResumeNotification function MSDN

Unfortunately I didn't managed to find a complete C# implementation for the new API.

Soo I made one myself using C# wrappers for User32.dll and PowrPorf.dll from Vanara Project By dahall (GitHub):

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

Full source code: SystemPowerModeNotification-dotnet4.5 (GitHub)

EDIT:

When using it inside a Windows Service, the callback function registered in PowerRegisterSuspendResumeNotification will fire only when going to Hibernate mode, and not in Modern Standby Sleep/Monitor Off.

There for you need to register to a different notification called RegisterPowerSettingNotification referenced here: Registering for Power Events MSDN and when a PowerEvent check the monitor state. Take in mind that it will happen even if the computer will enter Monitor Off/On state without entering sleep mode.

Example of registration:

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

Example of the callback:

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;
        }
  • Related