none
Windows Service start-up parameters RRS feed

  • Question

  • Hi, 

    I have a windows service performing log folder monitoring. The service at start-up has to perform slightly different job, depending on whether it's started by Windows during computer/OS boot process, or by a user from the SCM. 

    So my question if there is a way to somehow distinguish the Windows automatic boot from user interaction?  

    Thanks a lot!


     

    Thursday, August 4, 2016 4:44 AM

Answers

  • On Windows 8 and later, this information may be available from QueryServiceDynamicInformation(ServiceBase.ServiceHandle, SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON, ppDynamicInfo). The documentation does not say anything about the format of the data whose address this function stores in *ppDynamicInfo, but <winsvc.h> defines the SERVICE_START_REASON structure and macros like SERVICE_START_REASON_DEMAND, so that's probably it.

    I tested on Windows 10 build 14393 that the following correctly recognizes the service start reasons Demand, Auto, and DelayedAuto:

    using System;
    using System.Runtime.InteropServices;
    using System.ServiceProcess;
    using System.ComponentModel;
    
    namespace ServiceStartReason
    {
        public partial class Service1 : ServiceBase
        {
            public Service1()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                IntPtr pDynamicInfo;
                if (NativeMethods.QueryServiceDynamicInformation(
                    this.ServiceHandle,
                    NativeMethods.SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON,
                    out pDynamicInfo))
                {
                    try
                    {
                        // Unsafe code could instead use *(NativeMethods.SERVICE_START_REASON*)pDynamicInfo.
                        var reason = (NativeMethods.SERVICE_START_REASON)Marshal.PtrToStructure(
                            pDynamicInfo,
                            typeof(NativeMethods.SERVICE_START_REASON));
    
                        string message = string.Format("Start reason {0}", (ServiceStartReasons)reason.dwReason);
                        this.EventLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.Information);
                    }
                    finally
                    {
                        NativeMethods.LocalFree(pDynamicInfo);
                    }
                }
                else
                {
                    var exception = new Win32Exception();
                    string message = string.Format("Cannot get start reason. {0}", exception.Message);
                    this.EventLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.Error);
                }
            }
    
            protected override void OnStop()
            {
            }
        }
    
        [Flags]
        internal enum ServiceStartReasons
        {
            None = 0,
            Demand = (int)NativeMethods.SERVICE_START_REASON_DEMAND,
            Auto = (int)NativeMethods.SERVICE_START_REASON_AUTO,
            Trigger = (int)NativeMethods.SERVICE_START_REASON_TRIGGER,
            RestartOnFailure = (int)NativeMethods.SERVICE_START_REASON_RESTART_ON_FAILURE,
            DelayedAuto = (int)NativeMethods.SERVICE_START_REASON_DELAYEDAUTO,
        }
    
        internal static class NativeMethods
        {
            public const uint SERVICE_START_REASON_DEMAND = 1 << 0;
            public const uint SERVICE_START_REASON_AUTO = 1 << 1;
            public const uint SERVICE_START_REASON_TRIGGER = 1 << 2;
            public const uint SERVICE_START_REASON_RESTART_ON_FAILURE = 1 << 3;
            public const uint SERVICE_START_REASON_DELAYEDAUTO = 1 << 4;
    
            public const uint SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON = 1;
    
            public struct SERVICE_START_REASON
            {
    #pragma warning disable 649 // CS0649: field is never assigned.  Marshal.PtrToStructure assigns it.
                public uint dwReason;
    #pragma warning restore 649
            }
    
            [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool QueryServiceDynamicInformation(
                IntPtr hServiceStatus,
                uint dwInfoLevel,
                out IntPtr ppDynamicInfo);
    
            // There is Marshal.FreeHGlobal but no Marshal.FreeHLocal.
            [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
            public static extern IntPtr LocalFree(
                IntPtr hMem);
        }
    }
    However, the SERVICE_START_REASON structure is undocumented, so I cannot be sure whether the service start reasons need to be compared as bit masks or integers. Also, this code will get an exception on Windows 7 because QueryServiceDynamicInformation does not exist in that version.

    Friday, August 5, 2016 12:56 PM

All replies

  • Hi fly2,

    >>So my question if there is a way to somehow distinguish the Windows automatic boot from user interaction?

    According to your description, do you mean that you want to retrieve StartType of window service? if it the case, we could get StartType information by using ServiceController which has a Property named StartType.

    For more information, please refer to:

    https://msdn.microsoft.com/library/system.serviceprocess.servicecontroller

    https://msdn.microsoft.com/en-us/library/system.serviceprocess.servicestartmode

    Best regards,

    Cole Wu


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.


    Thursday, August 4, 2016 7:47 AM
    Moderator
  • Thank you Cole!

     I want to know how the service is being started. 

    Whether it's OS Boot, or user goes to SCM and starts it manually. I will try to instantiate ServiceController in my overriden ServiceBase.OnStart(). Will let you know. 

    Thanks a lot!

    Thursday, August 4, 2016 1:57 PM
  • On Windows 8 and later, this information may be available from QueryServiceDynamicInformation(ServiceBase.ServiceHandle, SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON, ppDynamicInfo). The documentation does not say anything about the format of the data whose address this function stores in *ppDynamicInfo, but <winsvc.h> defines the SERVICE_START_REASON structure and macros like SERVICE_START_REASON_DEMAND, so that's probably it.

    I tested on Windows 10 build 14393 that the following correctly recognizes the service start reasons Demand, Auto, and DelayedAuto:

    using System;
    using System.Runtime.InteropServices;
    using System.ServiceProcess;
    using System.ComponentModel;
    
    namespace ServiceStartReason
    {
        public partial class Service1 : ServiceBase
        {
            public Service1()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                IntPtr pDynamicInfo;
                if (NativeMethods.QueryServiceDynamicInformation(
                    this.ServiceHandle,
                    NativeMethods.SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON,
                    out pDynamicInfo))
                {
                    try
                    {
                        // Unsafe code could instead use *(NativeMethods.SERVICE_START_REASON*)pDynamicInfo.
                        var reason = (NativeMethods.SERVICE_START_REASON)Marshal.PtrToStructure(
                            pDynamicInfo,
                            typeof(NativeMethods.SERVICE_START_REASON));
    
                        string message = string.Format("Start reason {0}", (ServiceStartReasons)reason.dwReason);
                        this.EventLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.Information);
                    }
                    finally
                    {
                        NativeMethods.LocalFree(pDynamicInfo);
                    }
                }
                else
                {
                    var exception = new Win32Exception();
                    string message = string.Format("Cannot get start reason. {0}", exception.Message);
                    this.EventLog.WriteEntry(message, System.Diagnostics.EventLogEntryType.Error);
                }
            }
    
            protected override void OnStop()
            {
            }
        }
    
        [Flags]
        internal enum ServiceStartReasons
        {
            None = 0,
            Demand = (int)NativeMethods.SERVICE_START_REASON_DEMAND,
            Auto = (int)NativeMethods.SERVICE_START_REASON_AUTO,
            Trigger = (int)NativeMethods.SERVICE_START_REASON_TRIGGER,
            RestartOnFailure = (int)NativeMethods.SERVICE_START_REASON_RESTART_ON_FAILURE,
            DelayedAuto = (int)NativeMethods.SERVICE_START_REASON_DELAYEDAUTO,
        }
    
        internal static class NativeMethods
        {
            public const uint SERVICE_START_REASON_DEMAND = 1 << 0;
            public const uint SERVICE_START_REASON_AUTO = 1 << 1;
            public const uint SERVICE_START_REASON_TRIGGER = 1 << 2;
            public const uint SERVICE_START_REASON_RESTART_ON_FAILURE = 1 << 3;
            public const uint SERVICE_START_REASON_DELAYEDAUTO = 1 << 4;
    
            public const uint SERVICE_DYNAMIC_INFORMATION_LEVEL_START_REASON = 1;
    
            public struct SERVICE_START_REASON
            {
    #pragma warning disable 649 // CS0649: field is never assigned.  Marshal.PtrToStructure assigns it.
                public uint dwReason;
    #pragma warning restore 649
            }
    
            [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool QueryServiceDynamicInformation(
                IntPtr hServiceStatus,
                uint dwInfoLevel,
                out IntPtr ppDynamicInfo);
    
            // There is Marshal.FreeHGlobal but no Marshal.FreeHLocal.
            [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
            public static extern IntPtr LocalFree(
                IntPtr hMem);
        }
    }
    However, the SERVICE_START_REASON structure is undocumented, so I cannot be sure whether the service start reasons need to be compared as bit masks or integers. Also, this code will get an exception on Windows 7 because QueryServiceDynamicInformation does not exist in that version.

    Friday, August 5, 2016 12:56 PM