5

I'm trying to set an already installed windows service to automatic delayed start in C#. How do I set a windows service to

Automatic (Delayed Start) 

Can't find that value in the ServiceStartMode enum.

Edit:1

public class ServiceAutoStartInfo
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct SERVICE_DELAYED_AUTO_START_INFO
    {

        public bool fDelayedAutostart;
    }

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool ChangeServiceConfig2(IntPtr hService, int dwInfoLevel, IntPtr lpInfo);

    // Service configuration parameter 
    const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;

    public bool ChangeDelayedAutoStart(IntPtr hService, bool delayed)
    {


        // Validate service handle
        if (hService != IntPtr.Zero)
        {


            // Create 
            SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();

            // Set the DelayedAutostart property
            info.fDelayedAutostart = delayed;

            // Allocate necessary memory
            IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(

            typeof(SERVICE_DELAYED_AUTO_START_INFO)));

            // Convert structure to pointer
            Marshal.StructureToPtr(info, hInfo, true);

            // Change the configuration
            bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);

            // Release memory
            Marshal.FreeHGlobal(hInfo);

            return result;
        }

        return false;
    }
}

This is how I call it:

var controller = new ServiceController(s.ServiceName);
var autoDelay = new ServiceAutoStartInfo();
autoDelay.ChangeDelayedAutoStart(controller.ServiceHandle.DangerousGetHandle(), true);

But with no result.

mghie
  • 32,028
  • 6
  • 87
  • 129
Tan
  • 2,148
  • 3
  • 33
  • 54

5 Answers5

11

Look into calling the Windows ChangeServiceConfig2 function, with dwInfoLevel of SERVICE_CONFIG_DELAYED_AUTO_START_INFO and a SERVICE_DELAYED_AUTO_START_INFO struct with fDelayedAutostart set to TRUE.

Or, you can do this with the command line:

sc.exe config <servicename> start= delayed-auto
lc.
  • 113,939
  • 20
  • 158
  • 187
  • I have tried the ChangeServiceConfig2 approach but with no result i can edit my post with the code. – Tan Apr 24 '14 at 14:07
  • @Tan Is it returning a non-zero value (indicating success), or if not what is the error code? Is your process elevated or do you otherwise have permission to modify the service's configuration? – lc. Apr 24 '14 at 14:08
  • I have permissions im using a admin account. It doesnt return any errors. But the start up type jsut wont update from manual to auto delayed. – Tan Apr 24 '14 at 14:11
  • @Tan I'm not sure if this is still relevant, but [this answer](http://stackoverflow.com/a/18476058/44853) suggests you might need to restart before it appears updated. – lc. Apr 24 '14 at 14:13
  • Did I read the docu wrong or isn't `dwInfoLevel` a `DWORD` which maps to `uint`? Because in that case he should simply pass `3` IMO... – Christoph Fink Apr 24 '14 at 14:13
  • @chrfin If you look at the OP's code (which looks correct), they are just passing 3. – lc. Apr 24 '14 at 14:14
  • Sorry, my bad. Did get `SERVICE_DELAYED_AUTO_START_INFO` and `SERVICE_CONFIG_DELAYED_AUTO_START_INFO` mixed up... – Christoph Fink Apr 24 '14 at 14:17
  • bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo); returns true so its seems that everything is correct but no updated start up type – Tan Apr 24 '14 at 14:18
  • Maybe i have to use the sc.exe after all. – Tan Apr 24 '14 at 14:28
  • @Tan Hmm...Interestingly enough check the very bottom of http://msdn.microsoft.com/en-us/library/windows/desktop/ms685155%28v=vs.85%29.aspx - you could try sending an int 1 instead of a bool? – lc. Apr 24 '14 at 14:52
  • The REBOOT of Windows (Win 7 anyway) seems to be required to actually see it in the SERVICES control panel. I'm guessing for some reason the control panel doesn't update properly. Though when you use SC.exe to set it, it DOES update without a reboot. – Toby Dec 11 '20 at 23:29
4

I'm using the following, which works for me on Windows 7 (when run as admin):

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.ServiceProcess;

namespace ServiceManager
{
    /// <summary>
    /// Extensions to the ServiceController class.
    /// </summary>
    public static class ServiceControlerExtensions
    {
        /// <summary>
        /// Set the start mode for the service.
        /// </summary>
        /// <param name="serviceController">The service controller.</param>
        /// <param name="mode">The desired start mode.</param>
        public static void SetStartMode(this ServiceController serviceController, ServiceStartModeEx mode)
        {
            IntPtr serviceManagerHandle = OpenServiceManagerHandle();
            IntPtr serviceHandle = OpenServiceHandle(serviceController, serviceManagerHandle);

            try
            {
                if (mode == ServiceStartModeEx.DelayedAutomatic)
                {
                    ChangeServiceStartType(serviceHandle, ServiceStartModeEx.Automatic);
                    ChangeDelayedAutoStart(serviceHandle, true);
                }
                else
                {
                    // Delayed auto-start overrides other settings, so it must be set first.
                    ChangeDelayedAutoStart(serviceHandle, false);
                    ChangeServiceStartType(serviceHandle, mode);
                }
            }
            finally
            {
                if (serviceHandle != IntPtr.Zero)
                {
                    CloseServiceHandle(serviceHandle);
                }
                if (serviceHandle != IntPtr.Zero)
                {
                    CloseServiceHandle(serviceManagerHandle);
                }
            }
        }

        private static IntPtr OpenServiceHandle(ServiceController serviceController, IntPtr serviceManagerHandle)
        {
            var serviceHandle = OpenService(
                                            serviceManagerHandle,
                                            serviceController.ServiceName,
                                            SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);

            if (serviceHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Error");
            }
            return serviceHandle;
        }

        private static IntPtr OpenServiceManagerHandle()
        {
            IntPtr serviceManagerHandle = OpenSCManager(null, null, SC_MANAGER_ALL_ACCESS);
            if (serviceManagerHandle == IntPtr.Zero)
            {
                throw new ExternalException("Open Service Manager Error");
            }
            return serviceManagerHandle;
        }

        private static void ChangeServiceStartType(IntPtr serviceHandle, ServiceStartModeEx mode)
        {
            bool result = ChangeServiceConfig(
                                             serviceHandle,
                                             SERVICE_NO_CHANGE,
                                             (uint)mode,
                                             SERVICE_NO_CHANGE,
                                             null,
                                             null,
                                             IntPtr.Zero,
                                             null,
                                             null,
                                             null,
                                             null);

            if (result == false)
            {
                ThrowLastWin32Error("Could not change service start type");
            }
        }

        private static void ChangeDelayedAutoStart(IntPtr hService, bool delayed)
        {
            // Create structure that contains DelayedAutoStart property.
            SERVICE_DELAYED_AUTO_START_INFO info = new SERVICE_DELAYED_AUTO_START_INFO();

            // Set the DelayedAutostart property in that structure.
            info.fDelayedAutostart = delayed;

            // Allocate necessary memory.
            IntPtr hInfo = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SERVICE_DELAYED_AUTO_START_INFO)));

            // Convert structure to pointer.
            Marshal.StructureToPtr(info, hInfo, true);

            // Change the configuration.
            bool result = ChangeServiceConfig2(hService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, hInfo);

            // Release memory.
            Marshal.FreeHGlobal(hInfo);

            if (result == false)
            {
                ThrowLastWin32Error("Could not set service to delayed automatic");
            }
        }

        private static void ThrowLastWin32Error(string messagePrefix)
        {
            int nError = Marshal.GetLastWin32Error();
            var win32Exception = new Win32Exception(nError);
            string message = string.Format("{0}: {1}", messagePrefix, win32Exception.Message);
            throw new ExternalException(message);
        }

        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr OpenService(
            IntPtr hSCManager,
            string lpServiceName,
            uint dwDesiredAccess);

        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", ExactSpelling = true, CharSet = CharSet.Unicode,
            SetLastError = true)]
        private static extern IntPtr OpenSCManager(
            string machineName,
            string databaseName,
            uint dwAccess);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern Boolean ChangeServiceConfig(
            IntPtr hService,
            UInt32 nServiceType,
            UInt32 nStartType,
            UInt32 nErrorControl,
            String lpBinaryPathName,
            String lpLoadOrderGroup,
            IntPtr lpdwTagId,
            [In] char[] lpDependencies,
            String lpServiceStartName,
            String lpPassword,
            String lpDisplayName);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool ChangeServiceConfig2(
            IntPtr hService,
            int dwInfoLevel,
            IntPtr lpInfo);

        [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle")]
        private static extern int CloseServiceHandle(IntPtr hSCObject);

        private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;
        private const uint SERVICE_QUERY_CONFIG = 0x00000001;
        private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
        private const uint SC_MANAGER_ALL_ACCESS = 0x000F003F;

        private const int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct SERVICE_DELAYED_AUTO_START_INFO
        {
            public bool fDelayedAutostart;
        }
    }
}

namespace ServiceManager
{
    public enum ServiceStartModeEx
    {
        Automatic = 2,
        Manual = 3,
        Disabled = 4,
        DelayedAutomatic = 99
    }
}

You call it like this:

var serviceController = new ServiceController("Windows Update");
try
{
    serviceController.SetStartMode(ServiceStartModeEx.DelayedAutomatic);
}
finally
{
    serviceController.Close();
}
Kramii
  • 8,379
  • 4
  • 32
  • 38
0

Update: This only works for setting up new services and is not what the OP asked for:

You can use the DelayedAutoStart property of the ServiceInstaller.

installer.DelayedAutoStart = true;

Christoph Fink
  • 22,727
  • 9
  • 68
  • 113
0

I believe you need to combine both methods ChangeServiceConfig and ChangeServiceConfig2.

pseudo-code follows:

public static void ChangeServiceStartupType(ServiceStartupType type, ...)
{
    if (type == AutomaticDelayed)
    {
        if (ChangeServiceConfig2(.., DelayedAutoStart, ..))
        {
            ChangeServiceConfig(.., Automatic, ..);
        }
    }
    else
    {
        ChangeServiceConfig2(.., !DelayedAutoStart, ..)
        ChangeServiceConfig(.., type, ..)
    }
}

edit: you also need to remove "delayed-automatic" when requesting non-delayed startup-type. Otherwise it won't be possible to set "automatic" type. ("automatic-delayed" overrides "automatic")

grim
  • 1
  • 1
0

SERVICE_DELAYED_AUTO_START_INFO structure documentation states:

fDelayedAutostart

If this member is TRUE, the service is started after other auto-start services are started plus a short delay. Otherwise, the service is started during system boot.

This setting is ignored unless the service is an auto-start service.

https://learn.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_delayed_auto_start_info?redirectedfrom=MSDN#members

So I think if the service is not auto-start it won't change it to auto delayed, you would have to use sc.exe