如何在.NET中以编程方式重新启动Windows服务

80

如何在.NET中以编程方式重启Windows服务?
另外,当服务重新启动完成时,我需要执行一个操作。

10个回答

84

这篇文章使用ServiceController类编写了启动、停止和重启Windows服务的方法, 值得一看。

文章中的片段(“重启服务”方法):

public static void RestartService(string serviceName, int timeoutMilliseconds)
{
  ServiceController service = new ServiceController(serviceName);
  try
  {
    int millisec1 = Environment.TickCount;
    TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds);

    service.Stop();
    service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);

    // count the rest of the timeout
    int millisec2 = Environment.TickCount;
    timeout = TimeSpan.FromMilliseconds(timeoutMilliseconds - (millisec2-millisec1));

    service.Start();
    service.WaitForStatus(ServiceControllerStatus.Running, timeout);
  }
  catch
  {
    // ...
  }
}

1
那种解决方案的问题在于,在我的情况下,这个操作需要管理员权限。而且我无法在运行时更改(指定)用户。 - Slavik
4
@Slavik 如果需要管理员权限,那么无论您采取什么编程方法都无关紧要;您可能需要使用管理员级别的控制台来使用命令行工具,如果您尝试通过任务管理器,则可能会收到访问被拒绝的错误。 - kayleeFrye_onDeck

44

请查看ServiceController类。

要执行服务重新启动时需要执行的操作,如果是您自己的服务,则应该在服务本身中执行。
如果您无法访问服务源代码,则可以尝试使用ServiceControllerWaitForStatus方法。


5
这是否适用于服务本身?一个 Windows 服务能否通过编程自行重启? - Paul Karam

28

使用ServiceController类的示例

private void RestartWindowsService(string serviceName)
{
    ServiceController serviceController = new ServiceController(serviceName);
    try
    {
        if ((serviceController.Status.Equals(ServiceControllerStatus.Running)) || (serviceController.Status.Equals(ServiceControllerStatus.StartPending)))
        {
            serviceController.Stop();
        }
        serviceController.WaitForStatus(ServiceControllerStatus.Stopped);
        serviceController.Start();
        serviceController.WaitForStatus(ServiceControllerStatus.Running);
    }
    catch
    {
        ShowMsg(AppTexts.Information, AppTexts.SystematicError, MessageBox.Icon.WARNING);
    }
}

19

你也可以调用net命令来实现此操作。例如:

System.Diagnostics.Process.Start("net", "stop IISAdmin");
System.Diagnostics.Process.Start("net", "start IISAdmin");

5
我喜欢这个答案,因为它不需要额外的引用或辅助类。你可以添加一个 .WaitForExit(); 来确保服务在重新启动之前已经停止:`System.Diagnostics.Process.Start("net", "stop IISAdmin").WaitForExit();` `System.Diagnostics.Process.Start("net", "start IISAdmin").WaitForExit();` - Joris Talma

15
这个答案基于 @Donut 的回答(这个问题中获得最多赞的回答),但是进行了一些修改。
  1. 在每次使用后,处理 ServiceController 类,因为它实现了 IDisposable 接口。
  2. 减少方法的参数:没有必要为每个方法传递 serviceName 参数,我们可以在构造函数中设置它,每个方法都将使用该服务名称。
    这也更符合面向对象编程的原则。
  3. 以使该类成为组件可用的方式处理异常捕获。
  4. 从每个方法中删除 timeoutMilliseconds 参数。
  5. 添加两个新方法 StartOrRestartStopServiceIfRunning,这两个方法可以被视为其他基本方法的包装器。这些方法的目的仅是避免异常,如注释所述。

这是该类:

public class WindowsServiceController
{
    private readonly string serviceName;

    public WindowsServiceController(string serviceName)
    {
        this.serviceName = serviceName;
    }

    // this method will throw an exception if the service is NOT in Running status.
    public void RestartService()
    {
        using (ServiceController service = new ServiceController(serviceName))
        {
            try
            {
                service.Stop();
                service.WaitForStatus(ServiceControllerStatus.Stopped);

                service.Start();
                service.WaitForStatus(ServiceControllerStatus.Running);
            }
            catch (Exception ex)
            {
                throw new Exception($"Can not restart the Windows Service {serviceName}", ex);
            }
        }
    }

    // this method will throw an exception if the service is NOT in Running status.
    public void StopService()
    {
        using (ServiceController service = new ServiceController(serviceName))
        {
            try
            {
                service.Stop();
                service.WaitForStatus(ServiceControllerStatus.Stopped);
            }
            catch (Exception ex)
            {
                throw new Exception($"Can not Stop the Windows Service [{serviceName}]", ex);
            }
        }
    }

    // this method will throw an exception if the service is NOT in Stopped status.
    public void StartService()
    {
        using (ServiceController service = new ServiceController(serviceName))
        {
            try
            {
                service.Start();
                service.WaitForStatus(ServiceControllerStatus.Running);
            }
            catch (Exception ex)
            {
                throw new Exception($"Can not Start the Windows Service [{serviceName}]", ex);
            }
        }
    }

    // if service running then restart the service if the service is stopped then start it.
    // this method will not throw an exception.
    public void StartOrRestart()
    {
        if (IsRunningStatus)
            RestartService();
        else if (IsStoppedStatus)
            StartService();
    }

    // stop the service if it is running. if it is already stopped then do nothing.
    // this method will not throw an exception if the service is in Stopped status.
    public void StopServiceIfRunning()
    {
        using (ServiceController service = new ServiceController(serviceName))
        {
            try
            {
                if (!IsRunningStatus)
                    return;

                service.Stop();
                service.WaitForStatus(ServiceControllerStatus.Stopped);
            }
            catch (Exception ex)
            {
                throw new Exception($"Can not Stop the Windows Service [{serviceName}]", ex);
            }
        }
    }

    public bool IsRunningStatus => Status == ServiceControllerStatus.Running;

    public bool IsStoppedStatus => Status == ServiceControllerStatus.Stopped;

    public ServiceControllerStatus Status
    {
        get
        {
            using (ServiceController service = new ServiceController(serviceName))
            {
                return service.Status;
            }
        }
    }
}

如果你正在使用.NET Core,你必须下载System.ServiceProcess包
并且这只适用于WINDOWS操作系统。


8
如何呢?
var theController = new System.ServiceProcess.ServiceController("IISAdmin");

theController.Stop();
theController.Start();

不要忘记将System.ServiceProcess.dll添加到您的项目中,以使其正常工作。

3

请查看这篇文章

以下是来自文章的代码片段。

//[QUICK CODE] FOR THE IMPATIENT
using System;
using System.Collections.Generic;
using System.Text;
// ADD "using System.ServiceProcess;" after you add the 
// Reference to the System.ServiceProcess in the solution Explorer
using System.ServiceProcess;
namespace Using_ServiceController{
    class Program{
        static void Main(string[] args){
            ServiceController myService = new ServiceController();
            myService.ServiceName = "ImapiService";
            string svcStatus = myService.Status.ToString();
                if (svcStatus == "Running"){
                    myService.Stop();
                }else if(svcStatus == "Stopped"){
                    myService.Start();
                }else{
                    myService.Stop();
                }
        }
    }
}

17
为什么要在枚举上调用ToString(),然后比较字符串而不是直接比较枚举? - dzendras

2

您可以设置服务在失败后重新启动。因此,通过抛出异常可以强制重新启动。

在服务属性上使用恢复选项卡。

请务必使用重置故障计数属性以防止服务完全停止。


这是我的最后选择,但我没有找到其他解决方法。 - Riera

2

我需要更复杂的方法,因为有时候依赖服务无法重启,只是抛出异常或者服务被设置为“禁用”等等。

这就是我所做的:

(它会检查服务是否存在,如果它是“已禁用”,它会将服务设置为“自动”,当服务不能重新启动时,它将使用taskkill命令通过PID杀死服务,然后再次启动服务(在使用此功能时需要小心依赖服务,因为你还需要启动/重启它们)。

它返回true/false以表示重启成功与否。

仅在WIN10上测试过。

附注:正在开发版本,使用taskkill时可以检测到依赖服务并重启它们。

//Get windows service status
    public static string GetServiceStatus(string NameOfService)
    {
        ServiceController sc = new ServiceController(NameOfService);

        switch (sc.Status)
        {
            case ServiceControllerStatus.Running:
                return "Running";
            case ServiceControllerStatus.Stopped:
                return "Stopped";
            case ServiceControllerStatus.Paused:
                return "Paused";
            case ServiceControllerStatus.StopPending:
                return "Stopping";
            case ServiceControllerStatus.StartPending:
                return "Starting";
            default:
                return "Status Changing";
        }
    }

    //finds if service exists in OS
    public static bool DoesServiceExist(string serviceName)
    {
        return ServiceController.GetServices().Any(serviceController => serviceController.ServiceName.Equals(serviceName));
    }

    //finds startup type of service
    public static string GetStartupType(string serviceName)
    {
        ManagementObject objManage = new ManagementObject("Win32_Service.Name='"+serviceName+"'");
        objManage.Get();

        string status1 = objManage["StartMode"].ToString();

        return status1;
    }

    //restart service through PID
    public static bool RestartServiceByPID(string NameOfService)
    {
        LogWriter log = new LogWriter("TaskKilling: " + NameOfService);

        string strCmdText = "/C taskkill /f /fi \"SERVICES eq " + NameOfService + "\"";
        Process.Start("CMD.exe", strCmdText);

        using(ServiceController ScvController = new ServiceController(NameOfService))
        {
            ScvController.WaitForStatus(ServiceControllerStatus.Stopped);

            if (GetServiceStatus(NameOfService) == "Stopped")
            {
                ScvController.Start();
                ScvController.WaitForStatus(ServiceControllerStatus.Running);

                if (GetServiceStatus(NameOfService) == "Running")
                {
                    return true;
                }
                else
                {
                    return false;
                }

            }
            else
            {
                return false;
            }
        }
    }

    //Restart windows service
    public static bool RestartWindowsService(string NameOfService)
    {

        try
        {
            //check if service exists
            if(DoesServiceExist(NameOfService) == false)
            {
                MessageBox.Show("Service " + NameOfService + " was not found.");
                return false;
            }
            else
            {
                //if it does it check startup type and if it is disabled it will set it to "Auto"
                if (GetStartupType(NameOfService) == "Disabled")
                {
                    using (var svc = new ServiceController(NameOfService))
                    {
                        ServiceHelper.ChangeStartMode(svc, ServiceStartMode.Automatic);

                        if (svc.Status != ServiceControllerStatus.Running)
                        {
                            svc.Start();
                            svc.WaitForStatus(ServiceControllerStatus.Running);

                            if(GetServiceStatus(NameOfService) == "Running")
                            {
                                return true;
                            }
                            else
                            {
                                return false;
                            }
                        }
                        else
                        {
                            svc.Stop();
                            svc.WaitForStatus(ServiceControllerStatus.Stopped);

                            if(GetServiceStatus(NameOfService) == "Stopped")
                            {
                                svc.Start();
                                svc.WaitForStatus(ServiceControllerStatus.Running);

                                if(GetServiceStatus(NameOfService) == "Running")
                                {
                                    return true;
                                }
                                else
                                {
                                    return false;
                                }
                            }
                            //restart through PID
                            else
                            {
                                return RestartServiceByPID(NameOfService);
                            }
                        }

                    }
                }
                //If service is not disabled it will restart it
                else
                {
                    using(ServiceController ScvController = new ServiceController(NameOfService))
                    {
                        if(GetServiceStatus(NameOfService) == "Running")
                        {

                            ScvController.Stop();
                            ScvController.WaitForStatus(ServiceControllerStatus.Stopped);

                            if(GetServiceStatus(NameOfService) == "Stopped")
                            {
                                ScvController.Start();
                                ScvController.WaitForStatus(ServiceControllerStatus.Running);

                                if(GetServiceStatus(NameOfService) == "Running")
                                {
                                    return true;
                                }
                                else
                                {
                                    return false;
                                }

                            }
                            //if stopping service fails, it uses taskkill
                            else
                            {
                                return RestartServiceByPID(NameOfService);
                            }
                        }
                        else
                        {
                            ScvController.Start();
                            ScvController.WaitForStatus(ServiceControllerStatus.Running);

                            if(GetServiceStatus(NameOfService) == "Running")
                            {
                                return true;
                            }
                            else
                            {
                                return false;
                            }

                        }
                    }
                }
            }
        }
        catch(Exception ex)
        {
            return RestartServiceByPID(NameOfService);
        }
    }

-2

调用 Environment.Exit 并返回大于 0 的错误代码,这似乎是合适的,然后在安装时配置该服务在出错时重新启动。

Environment.Exit(1);

我在我的服务中做了同样的事情。它运行良好。


1
服务重新启动时的操作怎么样? - Fabrício Santos
1
它表现得像正常的全新启动。 - Sid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接