none
Windows Service won't stop when RegisterPowerSettingNotification is used to detect Connected Standby RRS feed

  • Question

  • I'm trying to implement a way to detect Connected Standby (now Modern Standby) from a Windows Service using this MSDN blog reference: How to get notified when going in and out of connected standby from a Windows Service

    My windows service is running as LOCAL_SYSTEM, and I have no problems starting/stopping it from a command line using "net stop <myservicename>" or "net start <myservicename" after I install it using "installutil".

    Also, I'm seeing that the blog reference guidance is correct and I am able to properly detect when going in/out of connected standby.

    However, whenever I register the service to the power capabilities using RegisterPowerSettingNotification, I am no longer able to stop the service.  My guess is that the registration is not being properly disposed by the the service controller, and it fails to stop the service properly anymore.  When I do "net stop <myservicename>", I get the error "The <myservicename> service could not be stopped.".  The only way for me to kill the service is from the command line using: taskkill /f /fi "imagename eq <myservicename>.exe"

    I just want the service to be able to stop cleanly and successfully.  Please help.

    Here is my sample code below.

    Windows Service (NotifyService):

    NotifySvc.cs:

    using System.ServiceProcess;
    
    namespace NotifyService
    {
        public partial class NotifySvc : ServiceBase
        {
            public NotifySvc()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                Logging.Initialize(@"c:\tmp\service.log");
                NativeMethods.RegisterServiceForPowerNotifications(this.ServiceName);
                Logging.LogMessage("OnStart completed successfully.");
            }
    
            protected override void OnStop()
            {
                Logging.LogMessage("OnStop completed successfully.");
            }
        }
    }
    

    NotifySvc.Designer.cs:

    namespace NotifyService
    {
        partial class NotifySvc
        {
            /// <summary> 
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Component Designer generated code
    
            /// <summary> 
            /// Required method for Designer support - do not modify 
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                components = new System.ComponentModel.Container();
                this.ServiceName = "Service1";
            }
    
            #endregion
        }
    }
    

    NativeMethods.cs:

    using System;
    using System.Runtime.InteropServices;
    
    namespace NotifyService
    {
        public static class NativeMethods
        {
            private delegate void HandlerEx(int control, int eventType, IntPtr eventData, IntPtr context);
    
            [DllImport(@"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification",
                     CallingConvention = CallingConvention.StdCall)]
            private static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);
    
            [DllImport(@"User32", EntryPoint = "UnregisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
            private static extern bool UnregisterPowerSettingNotification(IntPtr handle);
    
            [DllImport("powrprof.dll", SetLastError = true)]
            private static extern UInt32 CallNtPowerInformation(Int32 InformationLevel, IntPtr lpInputBuffer, UInt32 nInputBufferSize, out SYSTEM_POWER_CAPABILITIES lpOutputBuffer, UInt32 nOutputBufferSize);
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern IntPtr RegisterServiceCtrlHandlerEx(string lpServiceName, HandlerEx cbex, IntPtr context);
    
            // 02731015-4510-4526-99E6-E5A17EBD1AEA
            private static Guid GUID_MONITOR_POWER_ON = new Guid(0x02731015, 0x4510, 0x4526, 0x99, 0xE6, 0xE5, 0xA1, 0x7E, 0xBD, 0x1A, 0xEA);
    
            private const int WM_POWERBROADCAST = 0x0218;
    
            private const int SYSTEM_POWER_CAPABILITIES_LEVEL = 0x0004;
    
            private const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
    
            private const int DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001;
    
            private const int PBT_POWERSETTINGCHANGE = 0x8013; // DPPE
    
            // This structure is sent when the PBT_POWERSETTINGSCHANGE message is sent.
            // It describes the power setting that has changed and contains data about the change
            [StructLayout(LayoutKind.Sequential, Pack = 4)]
            internal struct POWERBROADCAST_SETTING
            {
                public Guid PowerSetting;
                public uint DataLength;
                public byte Data;
            }
    
            internal struct SYSTEM_POWER_CAPABILITIES
            {
                [MarshalAs(UnmanagedType.U1)]
                public bool PowerButtonPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool SleepButtonPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool LidPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS1;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS2;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS3;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS4;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS5;
                [MarshalAs(UnmanagedType.U1)]
                public bool HiberFilePresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool FullWake;
                [MarshalAs(UnmanagedType.U1)]
                public bool VideoDimPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool ApmPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool UpsPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool ThermalControl;
                [MarshalAs(UnmanagedType.U1)]
                public bool ProcessorThrottle;
                public byte ProcessorMinThrottle;
                public byte ProcessorMaxThrottle;    // Also known as ProcessorThrottleScale before Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool FastSystemS4;   // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool Hiberboot;  // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool WakeAlarmPresent;   // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool AoAc;   // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool DiskSpinDown;
                public byte HiberFileType;  // Ignore if earlier than Windows 10 (10.0.10240.0)
                [MarshalAs(UnmanagedType.U1)]
                public bool AoAcConnectivitySupported;  // Ignore if earlier than Windows 10 (10.0.10240.0)
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
                private readonly byte[] spare3;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemBatteriesPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool BatteriesAreShortTerm;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
                public BATTERY_REPORTING_SCALE[] BatteryScale;
                public SYSTEM_POWER_STATE AcOnLineWake;
                public SYSTEM_POWER_STATE SoftLidWake;
                public SYSTEM_POWER_STATE RtcWake;
                public SYSTEM_POWER_STATE MinDeviceWakeState;
                public SYSTEM_POWER_STATE DefaultLowLatencyWake;
            }
    
            internal struct BATTERY_REPORTING_SCALE
            {
                public ulong Granularity;
                public ulong Capacity;
            }
    
            internal enum SYSTEM_POWER_STATE
            {
                PowerSystemUnspecified = 0,
                PowerSystemWorking = 1,
                PowerSystemSleeping1 = 2,
                PowerSystemSleeping2 = 3,
                PowerSystemSleeping3 = 4,
                PowerSystemHibernate = 5,
                PowerSystemShutdown = 6,
                PowerSystemMaximum = 7
            }
    
            private static IntPtr hMonitorOn;
    
            public static bool ConnectedStandby { get; private set; }
    
            public static bool SupportsConnectedStandby { get; private set; }
    
            public static void RegisterWindowForPowerNotifications(IntPtr hwnd)
            {
                hMonitorOn = RegisterPowerSettingNotification(hwnd, ref GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE);
    
                GetAlwaysOnAlwaysConnectedCapability();
            }
    
            public static void UnregisterWindowFromPowerNotifications()
            {
                Logging.LogMessage($"Unregistering from Power Notifications");
                var retVal = UnregisterPowerSettingNotification(hMonitorOn);
            }
    
            public static void RegisterServiceForPowerNotifications(string serviceName)
            {
                Logging.LogMessage($"Registering Service for Power Notifications: {serviceName}");
                var serviceStatusHandle = RegisterServiceCtrlHandlerEx(serviceName, DoHandlerCallback, IntPtr.Zero);
    
                hMonitorOn = RegisterPowerSettingNotification(serviceStatusHandle, ref GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_SERVICE_HANDLE);
    
                GetAlwaysOnAlwaysConnectedCapability();
            }
    
            private static void DoHandlerCallback(int control, int eventType, IntPtr eventData, IntPtr context)
            {
                Logging.LogMessage($"DoHandlerCallback(control:{control}, eventType:{eventType}, eventData:{eventData}, context: {context})");
    
                if (eventData != null)
                {
                    if (eventType == PBT_POWERSETTINGCHANGE)
                    {
                        var ps = Marshal.PtrToStructure(eventData, typeof(POWERBROADCAST_SETTING));
    
                        if (ps != null)
                        {
                            UpdateConnectedStandbyState((POWERBROADCAST_SETTING)ps);
                        }
                        else
                        {
                            Logging.LogMessage("ERROR: ps == null");
                        }
                    }
                }
            }
    
            private static void GetAlwaysOnAlwaysConnectedCapability()
            {
                SYSTEM_POWER_CAPABILITIES cap;
                var retVal = CallNtPowerInformation(SYSTEM_POWER_CAPABILITIES_LEVEL, IntPtr.Zero, 0, out cap, (uint)Marshal.SizeOf(typeof(SYSTEM_POWER_CAPABILITIES)));
    
                if (retVal == 0)
                {
                    // Get the connected standby support value
                    if (cap.AoAc)
                    {
                        Logging.LogMessage("This system supports Connected Standby.");
                        SupportsConnectedStandby = true;
                    }
                    else
                    {
                        Logging.LogMessage("WARNING: This system does not support Connected Standby.");
                    }
                }
            }
    
            public static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                if (msg == WM_POWERBROADCAST && wParam.ToInt32() == PBT_POWERSETTINGCHANGE)
                {
                    // Extract data from message
                    POWERBROADCAST_SETTING ps =
                     (POWERBROADCAST_SETTING)Marshal.PtrToStructure(
                         lParam, typeof(POWERBROADCAST_SETTING));
                    IntPtr pData = (IntPtr)(lParam.ToInt32() + Marshal.SizeOf(ps));  // (*1)
    
                    UpdateConnectedStandbyState(ps);
                }
    
                return IntPtr.Zero;
            }
    
            private static void UpdateConnectedStandbyState(POWERBROADCAST_SETTING ps)
            {
                if (SupportsConnectedStandby)
                {
                    // Examine notification
                    Logging.LogMessage($"PowerSetting GUID: {ps.PowerSetting}");
                    Logging.LogMessage($"ps.DataLength: {ps.DataLength}");
                    Logging.LogMessage($"ps.Data: {ps.Data}");
    
                    if (ps.Data == 0)
                    {
                        // When the display is off, then you are entering connected standby
                        Logging.LogMessage("Entering Connected Standby");
                        ConnectedStandby = true;
                    }
                    else
                    {
                        // When the display is on, then you are exiting connected standby
                        Logging.LogMessage("Exiting Connected Standby");
                        ConnectedStandby = false;
                    }
                }
            }
        }
    }
    

    Logging.cs:

    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Threading;
    
    namespace NotifyService
    {
        public static class Logging
        {
            public static void Initialize(string fileName)
            {
                Trace.AutoFlush = true;
    
                var fi = new FileInfo(fileName);
    
                if (!Directory.Exists(fi.DirectoryName))
                {
                    Directory.CreateDirectory(fi.DirectoryName);
                }
    
                var logWriter = new TextWriterTraceListener(fi.FullName);
                if (logWriter != null)
                {
                    Trace.Listeners.Add(logWriter);
                }
    
                var executingAssembly = System.Reflection.Assembly.GetExecutingAssembly();
                var configAttribute = executingAssembly.GetCustomAttributes(false).SingleOrDefault(v => v.GetType() == typeof(System.Reflection.AssemblyConfigurationAttribute)) as System.Reflection.AssemblyConfigurationAttribute;
                var configurationString = string.Empty;
                if (configAttribute != null)
                {
                    configurationString = configAttribute.Configuration;
                }
    
                var startMessage = $"Log File Started - {executingAssembly.GetName().Version} - {configurationString} - {System.Environment.MachineName}";
    
                Trace.WriteLine(new string('=', startMessage.Length));
                Trace.Flush();
    
                // Will always be printed verbose.
                LogMessage(startMessage);
            }
    
            public static void LogMessage(string message, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = -1)
            {
                var fi = filePath == null ? null : new FileInfo(filePath);
    
                if (string.IsNullOrEmpty(message))
                {
                    message = "BEGIN";
                }
    
                var currentThread = Thread.CurrentThread;
                var threadInfoString = string.Format(CultureInfo.InvariantCulture, "T{0:D3}", currentThread.ManagedThreadId);
                var callerInfo = string.Format("F:{0},M:{1},L:{2}", fi == null ? "NULL" : fi.Name, memberName, lineNumber);
                var logString = string.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2}|{3}", DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss.ffffff"), threadInfoString, callerInfo, message);
                Trace.WriteLine(logString);
                Trace.Flush();
            }
        }
    }
    

    ProjectInstaller.Designer.cs:

    namespace NotifyService
    {
        partial class ProjectInstaller
        {
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.IContainer components = null;
    
            /// <summary> 
            /// Clean up any resources being used.
            /// </summary>
            /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
            protected override void Dispose(bool disposing)
            {
                if (disposing && (components != null))
                {
                    components.Dispose();
                }
                base.Dispose(disposing);
            }
    
            #region Component Designer generated code
    
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
                this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
                // 
                // serviceProcessInstaller1
                // 
                this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
                this.serviceProcessInstaller1.Password = null;
                this.serviceProcessInstaller1.Username = null;
                // 
                // serviceInstaller1
                // 
                this.serviceInstaller1.ServiceName = "NotifySvc";
                // 
                // ProjectInstaller
                // 
                this.Installers.AddRange(new System.Configuration.Install.Installer[] {
                this.serviceProcessInstaller1,
                this.serviceInstaller1});
    
            }
    
            #endregion
    
            private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
            private System.ServiceProcess.ServiceInstaller serviceInstaller1;
        }
    }

    All other files were not modified from their default template creations, so I didn't include them.  This is a standard Windows Service application project.

    Again, the request here is to simply get the windows service to stop successfully instead of having to kill the process using taskkill.

    Regards,

    Tam Bui 

    Friday, April 28, 2017 9:18 PM

Answers

  • I found a solution to my problem.  I discovered that I could detect that the service is being stopped in the DoHandlerCallback method of the NativeMethods.cs file if I monitor the 'control' variable and look for the SERVICE_CONTROL_STOP (0x00000001) value.  When detected, I can use this opportunity to stop the service.

    Here are the modifications that I made to the above code.

    New NativeMethods.cs file:

    using System;
    using System.Runtime.InteropServices;
    using System.ServiceProcess;
    
    namespace NotifyService
    {
        public static class NativeMethods
        {
            private delegate void HandlerEx(int control, int eventType, IntPtr eventData, IntPtr context);
    
            [DllImport(@"User32", SetLastError = true, EntryPoint = "RegisterPowerSettingNotification",
                     CallingConvention = CallingConvention.StdCall)]
            private static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);
    
            [DllImport(@"User32", EntryPoint = "UnregisterPowerSettingNotification", CallingConvention = CallingConvention.StdCall)]
            private static extern bool UnregisterPowerSettingNotification(IntPtr handle);
    
            [DllImport("powrprof.dll", SetLastError = true)]
            private static extern UInt32 CallNtPowerInformation(Int32 InformationLevel, IntPtr lpInputBuffer, UInt32 nInputBufferSize, out SYSTEM_POWER_CAPABILITIES lpOutputBuffer, UInt32 nOutputBufferSize);
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern IntPtr RegisterServiceCtrlHandlerEx(string lpServiceName, HandlerEx cbex, IntPtr context);
    
            // 02731015-4510-4526-99E6-E5A17EBD1AEA
            private static Guid GUID_MONITOR_POWER_ON = new Guid(0x02731015, 0x4510, 0x4526, 0x99, 0xE6, 0xE5, 0xA1, 0x7E, 0xBD, 0x1A, 0xEA);
    
            private const int WM_POWERBROADCAST = 0x0218;
    
            private const int SYSTEM_POWER_CAPABILITIES_LEVEL = 0x0004;
    
            private const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;
    
            private const int DEVICE_NOTIFY_SERVICE_HANDLE = 0x00000001;
    
            private const int PBT_POWERSETTINGCHANGE = 0x8013; // DPPE
    
            private const int SERVICE_CONTROL_POWEREVENT = 0x0000000D;
    
            private const int SERVICE_CONTROL_STOP = 0x00000001;
    
            private static ServiceBase registeredService;
    
            // This structure is sent when the PBT_POWERSETTINGSCHANGE message is sent.
            // It describes the power setting that has changed and contains data about the change
            [StructLayout(LayoutKind.Sequential, Pack = 4)]
            internal struct POWERBROADCAST_SETTING
            {
                public Guid PowerSetting;
                public uint DataLength;
                public byte Data;
            }
    
            internal struct SYSTEM_POWER_CAPABILITIES
            {
                [MarshalAs(UnmanagedType.U1)]
                public bool PowerButtonPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool SleepButtonPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool LidPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS1;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS2;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS3;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS4;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemS5;
                [MarshalAs(UnmanagedType.U1)]
                public bool HiberFilePresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool FullWake;
                [MarshalAs(UnmanagedType.U1)]
                public bool VideoDimPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool ApmPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool UpsPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool ThermalControl;
                [MarshalAs(UnmanagedType.U1)]
                public bool ProcessorThrottle;
                public byte ProcessorMinThrottle;
                public byte ProcessorMaxThrottle;    // Also known as ProcessorThrottleScale before Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool FastSystemS4;   // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool Hiberboot;  // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool WakeAlarmPresent;   // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool AoAc;   // Ignore if earlier than Windows XP
                [MarshalAs(UnmanagedType.U1)]
                public bool DiskSpinDown;
                public byte HiberFileType;  // Ignore if earlier than Windows 10 (10.0.10240.0)
                [MarshalAs(UnmanagedType.U1)]
                public bool AoAcConnectivitySupported;  // Ignore if earlier than Windows 10 (10.0.10240.0)
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
                private readonly byte[] spare3;
                [MarshalAs(UnmanagedType.U1)]
                public bool SystemBatteriesPresent;
                [MarshalAs(UnmanagedType.U1)]
                public bool BatteriesAreShortTerm;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
                public BATTERY_REPORTING_SCALE[] BatteryScale;
                public SYSTEM_POWER_STATE AcOnLineWake;
                public SYSTEM_POWER_STATE SoftLidWake;
                public SYSTEM_POWER_STATE RtcWake;
                public SYSTEM_POWER_STATE MinDeviceWakeState;
                public SYSTEM_POWER_STATE DefaultLowLatencyWake;
            }
    
            internal struct BATTERY_REPORTING_SCALE
            {
                public ulong Granularity;
                public ulong Capacity;
            }
    
            internal enum SYSTEM_POWER_STATE
            {
                PowerSystemUnspecified = 0,
                PowerSystemWorking = 1,
                PowerSystemSleeping1 = 2,
                PowerSystemSleeping2 = 3,
                PowerSystemSleeping3 = 4,
                PowerSystemHibernate = 5,
                PowerSystemShutdown = 6,
                PowerSystemMaximum = 7
            }
    
            private static IntPtr hMonitorOn;
    
            public static bool ConnectedStandby { get; private set; }
    
            public static bool SupportsConnectedStandby { get; private set; }
    
            public static void RegisterWindowForPowerNotifications(IntPtr hwnd)
            {
                hMonitorOn = RegisterPowerSettingNotification(hwnd, ref GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE);
    
                GetAlwaysOnAlwaysConnectedCapability();
            }
    
            public static void UnregisterFromPowerNotifications()
            {
                Logging.LogMessage($"Unregistering from Power Notifications");
                var retVal = UnregisterPowerSettingNotification(hMonitorOn);
            }
    
            public static void RegisterServiceForPowerNotifications(ServiceBase serviceBase)
            {
                registeredService = serviceBase;
                Logging.LogMessage($"Registering Service for Power Notifications: {registeredService.ServiceName}");
                var serviceStatusHandle = RegisterServiceCtrlHandlerEx(registeredService.ServiceName, DoHandlerCallback, IntPtr.Zero);
    
                hMonitorOn = RegisterPowerSettingNotification(serviceStatusHandle, ref GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_SERVICE_HANDLE);
    
                GetAlwaysOnAlwaysConnectedCapability();
            }
    
            private static void DoHandlerCallback(int control, int eventType, IntPtr eventData, IntPtr context)
            {
                Logging.LogMessage($"DoHandlerCallback(control:{control}, eventType:{eventType}, eventData:{eventData}, context: {context})");
    
    
                if (control == SERVICE_CONTROL_POWEREVENT)
                {
                    Logging.LogMessage("Handler Callback: SERVICE_CONTROL_POWEREVENT");
                    if (eventData != null)
                    {
                        if (eventType == PBT_POWERSETTINGCHANGE)
                        {
                            var ps = Marshal.PtrToStructure(eventData, typeof(POWERBROADCAST_SETTING));
    
                            if (ps != null)
                            {
                                UpdateConnectedStandbyState((POWERBROADCAST_SETTING)ps);
                            }
                            else
                            {
                                Logging.LogMessage("ERROR: ps == null");
                            }
                        }
                    }
                }
                else if (control == SERVICE_CONTROL_STOP)
                {
                    Logging.LogMessage("Handler Callback: SERVICE_CONTROL_STOP");
                    UnregisterFromPowerNotifications();
                    registeredService.Stop();
                }
            }
    
            private static void GetAlwaysOnAlwaysConnectedCapability()
            {
                SYSTEM_POWER_CAPABILITIES cap;
                var retVal = CallNtPowerInformation(SYSTEM_POWER_CAPABILITIES_LEVEL, IntPtr.Zero, 0, out cap, (uint)Marshal.SizeOf(typeof(SYSTEM_POWER_CAPABILITIES)));
    
                if (retVal == 0)
                {
                    // Get the connected standby support value
                    if (cap.AoAc)
                    {
                        Logging.LogMessage("This system supports Connected Standby.");
                        SupportsConnectedStandby = true;
                    }
                    else
                    {
                        Logging.LogMessage("WARNING: This system does not support Connected Standby.");
                    }
                }
            }
    
            public static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                if (msg == WM_POWERBROADCAST && wParam.ToInt32() == PBT_POWERSETTINGCHANGE)
                {
                    // Extract data from message
                    POWERBROADCAST_SETTING ps =
                     (POWERBROADCAST_SETTING)Marshal.PtrToStructure(
                         lParam, typeof(POWERBROADCAST_SETTING));
                    IntPtr pData = (IntPtr)(lParam.ToInt32() + Marshal.SizeOf(ps));  // (*1)
    
                    UpdateConnectedStandbyState(ps);
                }
    
                return IntPtr.Zero;
            }
    
            private static void UpdateConnectedStandbyState(POWERBROADCAST_SETTING ps)
            {
                if (SupportsConnectedStandby)
                {
                    // Examine notification
                    Logging.LogMessage($"PowerSetting GUID: {ps.PowerSetting}");
                    Logging.LogMessage($"ps.DataLength: {ps.DataLength}");
                    Logging.LogMessage($"ps.Data: {ps.Data}");
    
                    if (ps.Data == 0)
                    {
                        // When the display is off, then you are entering connected standby
                        Logging.LogMessage("Entering Connected Standby");
                        ConnectedStandby = true;
                    }
                    else
                    {
                        // When the display is on, then you are exiting connected standby
                        Logging.LogMessage("Exiting Connected Standby");
                        ConnectedStandby = false;
                    }
                }
            }
        }
    }
    

    New NotifySvc.cs file:

    using System.ServiceProcess;
    
    namespace NotifyService
    {
        public partial class NotifySvc : ServiceBase
        {
            public NotifySvc()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                Logging.Initialize(@"c:\tmp\service.log");
                NativeMethods.RegisterServiceForPowerNotifications(this);
                Logging.LogMessage("OnStart completed successfully.");
            }
    
            protected override void OnStop()
            {
                Logging.LogMessage("OnStop completed successfully.");
            }
        }
    }
    

    When you open a cmd prompt with Admin priveleges to install the service, then call "net start notifysvc", followed by "net stop notifysvc", you will now see it successfully stop.  The service.log file output will look similar to this:

    ===========================================
    05/01/2017 20:14:59.699894|T004|F:Logging.cs,M:Initialize,L:44|Log File Started - 1.0.0.0 -  - TAMBUI-W540
    05/01/2017 20:14:59.699894|T004|F:NativeMethods.cs,M:RegisterServiceForPowerNotifications,L:155|Registering Service for Power Notifications: NotifySvc
    05/01/2017 20:14:59.699894|T001|F:NativeMethods.cs,M:DoHandlerCallback,L:165|DoHandlerCallback(control:13, eventType:32787, eventData:5920724, context: 0)
    05/01/2017 20:14:59.699894|T001|F:NativeMethods.cs,M:DoHandlerCallback,L:170|Handler Callback: SERVICE_CONTROL_POWEREVENT
    05/01/2017 20:14:59.699894|T004|F:NativeMethods.cs,M:GetAlwaysOnAlwaysConnectedCapability,L:211|WARNING: This system does not support Connected Standby.
    05/01/2017 20:14:59.699894|T004|F:NotifySvc.cs,M:OnStart,L:24|OnStart completed successfully.
    05/01/2017 20:15:01.708884|T001|F:NativeMethods.cs,M:DoHandlerCallback,L:165|DoHandlerCallback(control:4, eventType:0, eventData:0, context: 0)
    05/01/2017 20:15:03.520977|T001|F:NativeMethods.cs,M:DoHandlerCallback,L:165|DoHandlerCallback(control:4, eventType:0, eventData:0, context: 0)
    05/01/2017 20:15:03.536542|T001|F:NativeMethods.cs,M:DoHandlerCallback,L:165|DoHandlerCallback(control:1, eventType:0, eventData:0, context: 0)
    05/01/2017 20:15:03.536542|T001|F:NativeMethods.cs,M:DoHandlerCallback,L:190|Handler Callback: SERVICE_CONTROL_STOP
    05/01/2017 20:15:03.536542|T001|F:NativeMethods.cs,M:UnregisterFromPowerNotifications,L:148|Unregistering from Power Notifications
    05/01/2017 20:15:03.536542|T001|F:NotifySvc.cs,M:OnStop,L:29|OnStop completed successfully.

    Regards,

    Tam

    • Marked as answer by Tam Bui Tuesday, May 2, 2017 3:24 AM
    Tuesday, May 2, 2017 3:22 AM