Windows 服务如何以编程方式重新启动自身?

Posted

技术标签:

【中文标题】Windows 服务如何以编程方式重新启动自身?【英文标题】:How can a windows service programmatically restart itself? 【发布时间】:2010-09-18 05:39:16 【问题描述】:

我需要在 .NET 中编写健壮的代码以使 Windows 服务(服务器 2003)能够自行重启。最好的方法是什么?是否有一些 .NET API 可以做到这一点?

【问题讨论】:

“健壮”是一个狡猾的词 (***.fogbugz.com/default.asp?W13086) - 您需要哪些特定特征? 【参考方案1】:

将服务设置为在失败后重新启动(双击控制面板中的服务并查看这些选项卡 - 我忘记了它的名称)。然后,只要您希望服务重新启动,只需调用Environment.Exit(1)(或任何非零返回),操作系统就会为您重新启动它。

【讨论】:

仅供参考,位置在服务面板中,右键单击有问题的服务并选择属性,然后选择恢复选项卡。 我知道这是如何实现预期行为的,但返回码 1 是为了告诉系统出现错误。如果实际上没有错误而您只是想重新启动服务,这不是不好的做法吗? 这可以由服务安装程序在安装后事件中以编程方式完成,无需点击周围...如果您的服务在远程服务器上并且您需要每天安装几次怎么办,例如在测试期间? @mgttlinger:我认为是的,当您的服务健康时这是一种不好的做法,因此它永远不需要重新启动。但是某些服务架构超出了我们的能力范围,如果需要重新启动它们,则表明情况不佳,因此关闭并释放一些最低限度的资源(如果可能)并“砍断他们的脚”是没有问题的,因为离开服务运行不当可能会更糟(无用)。 @JohnLeidegren 如果您想要正确关闭,您可以设置Service.ExitCode = 1,然后执行Service.Stop(),同时在服务恢复设置屏幕上启用复选框“启用错误停止操作”。这样做可以正确关闭并仍然触发重新启动。【参考方案2】:
Dim proc As New Process()
Dim psi As New ProcessStartInfo()

psi.CreateNoWindow = True
psi.FileName = "cmd.exe"
psi.Arguments = "/C net stop YOURSERVICENAMEHERE && net start YOURSERVICENAMEHERE"
psi.LoadUserProfile = False
psi.UseShellExecute = False
psi.WindowStyle = ProcessWindowStyle.Hidden
proc.StartInfo = psi
proc.Start()

【讨论】:

如果服务名称包含空格,请记得在您的服务名称周围添加“”。 这仅适用于在特权帐户下运行的服务,无论如何这都是个坏主意。 根据您的环境,您可能必须确保 cmd.exe 进程在单独的进程组中运行。我试图实现这一点,但是每当执行“net stop”时,cmd.exe 子进程就死了。为避免这种情况,您需要调用 CreateProcess 并指定 CREATE_NEW_PROCESS_GROUP。【参考方案3】:

您甚至无法确定运行您的服务的用户帐户是否有权停止和重新启动服务。

【讨论】:

虽然 +1-ed 因为我遇到了同样的问题,但在查看 sc.exe 时发现它在调用 OpenSCManager () 时使用 SERVICE_QUERY_CONFIG 作为 dwDesiredAccess,然后使用 SERVICE_START | 调用 OpenService() SERVICE_STOP 打开一个特定的服务,它工作正常(访问没有问题)。不确定如何在 .NET 中完成此操作。 大多数 Windows 服务都作为系统运行,所以应该没问题。 @Switch 在 NETWORK SERVICE 下运行着一大群服务,默认情况下甚至没有权限打开自己的可执行文件。 @AgentFire,正确,但默认是本地系统,而不是网络服务。本地系统固有地对操作系统具有***别的权限 - 超过了分配给本地管理员组成员的权限。 @Switch 但使用最可用的权限违反了PoL-Principle。【参考方案4】:
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

其中SERVICENAME 是您的服务名称(包含双引号以说明服务名称中的空格,否则可以省略)。

干净,无需自动重启配置。

【讨论】:

学到了一些关于 & 与 && 的新知识:[command1] & [command2] 将始终按顺序执行这两个命令,而 [command1] && [command2] 只有在 command1 成功运行时才运行 command2 (autoitscript.com/forum/topic/…) 【参考方案5】:

您可以使用 Windows cmd.exe 创建一个重新启动自己的子进程:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();

【讨论】:

这个,不做任何其他事情,将继承调用线程的安全上下文......只要这是一个有足够能力重新启动的上下文,你就很好。【参考方案6】:

这取决于您希望它自行重启的原因。

如果您只是在寻找一种让服务定期清理自身的方法,那么您可以在服务中运行一个计时器,该计时器会定期导致清除例程。

如果您正在寻找一种在失败时重新启动的方法 - 服务主机本身可以在设置时提供该功能。

那么为什么需要重启服务器呢?你想达到什么目的?

【讨论】:

如果您担心大对象堆和内存碎片,这将不起作用。据说 .NET 4/4.5 和 64 位进程对此有很大帮助。【参考方案7】:

我不认为你可以在一个独立的服务中(当你调用 Restart 时,它会停止服务,这会中断 Restart 命令,并且它永远不会再次启动)。如果您可以添加第二个 .exe(使用 ServiceManager 类的控制台应用程序),那么您可以启动独立的 .exe 并让它重新启动服务然后退出。

再想一想,您可能可以让服务注册一个计划任务(例如,使用命令行“at”命令)来启动服务,然后让它自行停止;这可能会奏效。

【讨论】:

【参考方案8】:

脱壳到批处理文件或 EXE 的问题在于,服务可能具有运行外部应用程序所需的权限,也可能没有。

我发现的最简洁的方法是使用 OnStop() 方法,它是服务控制管理器的入口点。然后你的所有清理代码都会运行,并且你不会有任何挂起的套接字或其他进程,假设你的停止代码正在完成它的工作。

为此,您需要在终止之前设置一个标志,告诉 OnStop 方法以错误代码退出;那么SCM就知道服务需要重启了。如果没有此标志,您将无法从 SCM 手动停止服务。这还假设您已将服务设置为在出现错误时重新启动。

这是我的停止代码:

...

bool ABORT;

protected override void OnStop()

    Logger.log("Stopping service");
    WorkThreadRun = false;
    WorkThread.Join();
    Logger.stop();
    // if there was a problem, set an exit error code
    // so the service manager will restart this
    if(ABORT)Environment.Exit(1);

如果服务遇到问题并需要重新启动,我会启动一个线程来从 SCM 停止服务。这允许服务自行清理:

...

if(NeedToRestart)

    ABORT = true;
    new Thread(RestartThread).Start();


void RestartThread()

    ServiceController sc = new ServiceController(ServiceName);
    try
    
        sc.Stop();
    
    catch (Exception)  

【讨论】:

【参考方案9】:

我会使用 Windows 调度程序来安排重新启动您的服务。问题是你不能重新启动自己,但你可以停止自己。 (你基本上已经锯掉了你坐在的树枝......如果你明白我的比喻)你需要一个单独的过程来为你做这件事。 Windows 调度程序是一个合适的。安排一项一次性任务来重新启动您的服务(甚至在服务本身内)以立即执行。

否则,您将不得不创建一个“牧羊”过程来为您完成这项工作。

【讨论】:

【参考方案10】:

对这个问题的第一个回答是最简单的解决方案:“Environment.Exit(1)”我在 Windows Server 2008 R2 上使用它,它运行良好。服务自行停止,O/S 等待 1 分钟,然后重新启动。

【讨论】:

等待多长时间取决于您设置的等待时间。虽然默认是 1 分钟,但如果有人不希望你的回答会给人错误的印象。【参考方案11】:

我不认为它可以。当服务“停止”时,它会被完全卸载。

嗯,好吧,我想总会有办法的。例如,您可以创建一个分离的进程来停止服务,然后重新启动它,然后退出。

【讨论】:

【参考方案12】:

只是路过:我想我会添加一些额外的信息...

你也可以抛出一个异常,这将自动关闭 Windows 服务,并且自动重启选项刚刚启动。唯一的问题是,如果你的电脑上有一个开发环境,那么 JIT 会尝试启动,你会得到一个提示,说 debug Y/N。说不,然后它会关闭,然后重新启动正常。 (在没有 JIT 的 PC 上一切正常)。 我拖钓的原因是,这个 JIT 是 Win 7 的新手(它曾经与 XP 等一起正常工作)并且我试图找到一种禁用 JIT 的方法....我可以尝试这里提到的 Environment.Exit 方法,看看如何这也有效。

克里斯蒂安:英国布里斯托尔

【讨论】:

所有这些抛出异常、exit(1) 解决方案都避免了对应用程序的正确清理。不优雅地退出是一个糟糕的解决方案【参考方案13】:

像这样创建一个restart.bat文件

@echo on
set once="C:\Program Files\MyService\once.bat"
set taskname=Restart_MyService
set service=MyService
echo rem %time% >%once%
echo net stop %service% >>%once%
echo net start %service% >>%once%
echo del %once% >>%once%

schtasks /create /ru "System" /tn %taskname% /tr '%once%' /sc onstart /F /V1 /Z
schtasks /run /tn %taskname%

然后在你的 %service% 启动时删除任务 %taskname%

【讨论】:

【参考方案14】:

创建一个单独的 appdomain 来托管应用程序代码。当需要重新启动时,我们可以卸载并重新加载 appdomain 而不是进程(Windows 服务)。这就是 IIS 应用程序池的工作原理,它们不直接运行 asp.net 应用程序,它们使用单​​独的 appdmain。

【讨论】:

【参考方案15】:

更好的方法可能是使用 NT 服务作为应用程序的包装器。当 NT 服务启动时,您的应用程序可以以“空闲”模式启动,等待命令启动(或配置为自动启动)。

想象一辆汽车,当它启动时,它开始处于空闲状态,等待您的命令前进或后退。这也带来了其他好处,例如更好的远程管理,因为您可以选择如何公开您的应用程序。

【讨论】:

【参考方案16】:

最简单的方法是创建一个批处理文件:

净停止 网络开始

并以所需的时间间隔将文件添加到调度程序

【讨论】:

它不起作用,因为批处理在两个命令之间停止,因为它是服务本身的子进程。【参考方案17】:
private static void  RestartService(string serviceName)
    
        using (var controller = new ServiceController(serviceName))
        
            controller.Stop();
            int counter = 0;
            while (controller.Status != ServiceControllerStatus.Stopped)
            
                Thread.Sleep(100);
                controller.Refresh();
                counter++;
                if (counter > 1000)
                
                    throw new System.TimeoutException(string.Format("Could not stop service: 0", Constants.Series6Service.WindowsServiceName));
                
            

            controller.Start();
        
    

【讨论】:

以上是关于Windows 服务如何以编程方式重新启动自身?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 c#.net 中以编程方式重新启动我的窗口服务

如何在 Windows 中以编程方式强制重启/重新加载 Playback 设备?

如何在 C# 中以编程方式将 Windows 服务的启动类型更改为禁用

如何使用任务计划程序重新启动 Windows 服务

PC 上的双启动 Windows-Android:如何以编程方式切换操作系统?

以编程方式在远程计算机上安装 Windows 服务