【问题标题】:Set existing service to "Auto (Delayed start)"将现有服务设置为“自动(延迟启动)”
【发布时间】:2014-04-24 13:50:34
【问题描述】:

我正在尝试在 C# 中将 已安装 Windows 服务设置为自动延迟启动。如何将 Windows 服务设置为

Automatic (Delayed Start) 

在 ServiceStartMode 枚举中找不到该值。

编辑: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;
    }
}

我是这样称呼它的:

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

但没有结果。

【问题讨论】:

  • 为什么不直接在 MMC Services 中更改其启动方式?
  • @OverKiller 我正在为我的项目构建一个自动部署管理器,所以我试图在代码中进行更改。
  • @oleksii 我不认为这是一个骗局,因为这个问题是针对已安装服务的,而您链接到的问题是在 安装过程
  • @lc 撤回了我的投票

标签: c# service


【解决方案1】:

考虑调用 Windows ChangeServiceConfig2 函数,使用 SERVICE_CONFIG_DELAYED_AUTO_START_INFOdwInfoLevel 和将 fDelayedAutostart 设置为 TRUESERVICE_DELAYED_AUTO_START_INFO 结构。

或者,您可以使用命令行来执行此操作:

sc.exe config <servicename> start= delayed-auto

【讨论】:

  • 我尝试了 ChangeServiceConfig2 方法,但没有结果我可以用代码编辑我的帖子。
  • @Tan 它是否返回非零值(表示成功),或者如果不是,错误代码是什么?您的进程是否被提升或者您是否有权修改服务的配置?
  • 我拥有使用管理员帐户的权限。它不返回任何错误。但是启动类型jsut不会从手动更新为自动延迟。
  • @Tan 我不确定这是否仍然相关,但this answer 建议您可能需要重新启动才能显示更新。
  • 我读错了文档还是dwInfoLevel不是映射到uintDWORD?因为在那种情况下,他应该简单地通过3IMO...
【解决方案2】:

我正在使用以下内容,它适用于 Windows 7(以管理员身份运行):

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

你这样称呼它:

var serviceController = new ServiceController("Windows Update");
try
{
    serviceController.SetStartMode(ServiceStartModeEx.DelayedAutomatic);
}
finally
{
    serviceController.Close();
}

【讨论】:

    【解决方案3】:

    更新:这仅适用于设置新服务,而不是 OP 要求的:

    您可以使用ServiceInstallerDelayedAutoStart 属性。

    installer.DelayedAutoStart = true;
    

    【讨论】:

    • 我没有服务安装程序,因为该服务已经安装。
    • 操作,抱歉... - 让我检查一下其他解决方案。
    【解决方案4】:

    我相信您需要结合使用 ChangeServiceConfig 和 ChangeServiceConfig2 这两种方法。

    伪代码如下:

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

    编辑:您还需要在请求非延迟启动类型时删除“延迟自动”。否则无法设置“自动”类型。 (“自动延迟”覆盖“自动”)

    【讨论】:

      【解决方案5】:

      SERVICE_DELAYED_AUTO_START_INFO 结构文档说明:

      fDelayedAutostart

      如果此成员为 TRUE,则服务在其他自动启动后启动 服务开始加上短暂的延迟。否则,服务是 在系统启动期间启动。

      除非服务是自动启动服务,否则此设置将被忽略。

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

      所以我认为如果服务不是自动启动它不会将其更改为自动延迟,您将不得不使用 sc.exe

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-28
        • 1970-01-01
        • 2013-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多