Enable windows service programmatically

独自空忆成欢 提交于 2019-11-29 14:51:53

There are several ways you can change the startup type of a windows service (see this question). If I remember correctly the WMI method worked when I tested it but was not totally reliable, so I used the Windows API function ChangeServiceConfig. I never tried the registry method. I would think that would be the most flaky of the three options.

Note that if you want the 'Automatic (Delayed Start)' mode then you need to call ChangeServiceConfig2.

public void ChangeServiceStartType(string serviceName, ServiceStartupType startType)
{
    //Obtain a handle to the service control manager database
    IntPtr scmHandle = OpenSCManager(null, null, SC_MANAGER_CONNECT);
    if (scmHandle == IntPtr.Zero)
    {
        throw new Exception("Failed to obtain a handle to the service control manager database.");
    }

    //Obtain a handle to the specified windows service
    IntPtr serviceHandle = OpenService(scmHandle, serviceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG);
    if (serviceHandle == IntPtr.Zero)
    {
        throw new Exception($"Failed to obtain a handle to service '{serviceName}'.");
    }

    //Change the start mode
    bool changeServiceSuccess = ChangeServiceConfig(serviceHandle, SERVICE_NO_CHANGE, (uint)startType, SERVICE_NO_CHANGE, null, null, IntPtr.Zero, null, null, null, null);
    if (!changeServiceSuccess)
    {
        string msg = $"Failed to update service configuration for service '{serviceName}'. ChangeServiceConfig returned error {Marshal.GetLastWin32Error()}.";
        throw new Exception(msg);
    }

    //Clean up
    if (scmHandle != IntPtr.Zero)
        CloseServiceHandle(scmHandle);
    if (serviceHandle != IntPtr.Zero)
        CloseServiceHandle(serviceHandle);
}

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

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

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

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

private const uint SC_MANAGER_CONNECT = 0x0001;
private const uint SERVICE_QUERY_CONFIG = 0x00000001;
private const uint SERVICE_CHANGE_CONFIG = 0x00000002;
private const uint SERVICE_NO_CHANGE = 0xFFFFFFFF;

public enum ServiceStartupType : uint
{
    /// <summary>
    /// A device driver started by the system loader. This value is valid only for driver services.
    /// </summary>
    BootStart = 0,

    /// <summary>
    /// A device driver started by the IoInitSystem function. This value is valid only for driver services.
    /// </summary>
    SystemStart = 1,

    /// <summary>
    /// A service started automatically by the service control manager during system startup.
    /// </summary>
    Automatic = 2,

    /// <summary>
    /// A service started by the service control manager when a process calls the StartService function.
    /// </summary>
    Manual = 3,

    /// <summary>
    /// A service that cannot be started. Attempts to start the service result in the error code ERROR_SERVICE_DISABLED.
    /// </summary>
    Disabled = 4
}

I don't think that editing the registry is the recommended way to do this. Unfortunately it isn't exposed in the ServiceController class. I would recommend using WMI, which has the method ChangeStartMode (You will need to add a reference to System.Management.dll):

using System.Management;

public static void EnableTheService(string serviceName)
{
    using (var mo = new ManagementObject(string.Format("Win32_Service.Name=\"{0}\"", serviceName)))
    {
        mo.InvokeMethod("ChangeStartMode", new object[] { "Automatic" });
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!