You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中更新并重启正在运行的Windows服务?

可靠的Windows服务自重启更新方案(.NET 6独立部署)

针对你遇到的.NET 6独立部署Windows服务自更新重启需求,以下是几个经过验证的可靠方案:

方案1:使用独立辅助进程(推荐)

核心思路是让当前服务启动一个脱离自身进程树的轻量级辅助程序,由它负责完成「停止服务→替换二进制文件→启动服务」的完整流程,避免服务自身停止后无法继续执行后续步骤。

实现步骤:

  1. 编写一个独立的控制台应用(例如命名为ServiceUpdater.exe),接收服务名称、新版本文件路径等参数,执行更新逻辑:
    using System.Diagnostics;
    using System.IO;
    using System.Threading;
    
    if (args.Length < 2) return;
    
    var serviceName = args[0];
    var newBinaryDir = args[1];
    var currentServiceDir = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
    
    // 等待3秒确保服务完全停止
    Thread.Sleep(3000);
    
    // 停止目标服务
    using var stopProcess = Process.Start(new ProcessStartInfo
    {
        FileName = "sc.exe",
        Arguments = $"stop {serviceName}",
        CreateNoWindow = true,
        UseShellExecute = false
    });
    stopProcess?.WaitForExit();
    
    // 替换服务文件(覆盖现有文件)
    foreach (var file in Directory.GetFiles(newBinaryDir))
    {
        var destPath = Path.Combine(currentServiceDir, Path.GetFileName(file));
        File.Copy(file, destPath, overwrite: true);
    }
    
    // 启动目标服务
    using var startProcess = Process.Start(new ProcessStartInfo
    {
        FileName = "sc.exe",
        Arguments = $"start {serviceName}",
        CreateNoWindow = true,
        UseShellExecute = false
    });
    startProcess?.WaitForExit();
    
  2. 在主服务中,检测到新版本后,先将新版本文件下载到临时目录,启动辅助程序并传递参数,随后主动退出服务:
    // 检测到新版本后的执行逻辑
    var updaterPath = Path.Combine(AppContext.BaseDirectory, "ServiceUpdater.exe");
    var tempNewDir = @"C:\Temp\ServiceNewVersion"; // 新版本文件存放路径
    var serviceName = "YourTargetServiceName";
    
    var startInfo = new ProcessStartInfo(updaterPath)
    {
        Arguments = $"\"{serviceName}\" \"{tempNewDir}\"",
        CreateNoWindow = true,
        UseShellExecute = false,
        WindowStyle = ProcessWindowStyle.Hidden,
        WorkingDirectory = AppContext.BaseDirectory
    };
    Process.Start(startInfo);
    
    // 主动退出服务,让辅助程序完成后续操作
    Environment.Exit(0);
    

方案2:利用Windows任务计划程序

通过创建一次性定时任务,让系统任务计划服务托管更新流程,不受原服务进程生命周期影响,确保服务停止后仍能执行更新和重启操作。

实现步骤:

在主服务中检测到新版本后,生成更新脚本并创建一次性任务:

using System;
using System.Diagnostics;
using System.IO;

var serviceName = "YourTargetServiceName";
var currentServiceDir = AppContext.BaseDirectory;
var tempNewDir = @"C:\Temp\ServiceNewVersion";
var updateScriptPath = Path.Combine(currentServiceDir, "update_task.bat");

// 生成批处理脚本
File.WriteAllText(updateScriptPath, $@"
@echo off
sc stop {serviceName}
timeout /t 3 /nobreak >nul
xcopy ""{tempNewDir}\*"" ""{currentServiceDir}"" /E /Y /Q
sc start {serviceName}
del ""{updateScriptPath}""
");

// 创建一次性任务,延迟2秒执行
var taskName = $"ServiceUpdate_{Guid.NewGuid()}";
using var taskProcess = Process.Start(new ProcessStartInfo
{
    FileName = "schtasks.exe",
    Arguments = $"/create /tn \"{taskName}\" /tr \"{updateScriptPath}\" /sc once /st {DateTime.Now.AddSeconds(2).ToString("HH:mm:ss")} /f /rl highest",
    CreateNoWindow = true,
    UseShellExecute = false
});
taskProcess?.WaitForExit();

// 主动退出服务
Environment.Exit(0);

方案3:复用服务故障恢复机制

通过让服务以特定退出码主动终止,利用Windows服务的故障恢复配置自动重启服务,同时在服务重启时完成文件替换。

实现步骤:

  1. 提前配置服务故障恢复策略:
    • 打开「服务」控制台,找到目标服务→右键属性→「恢复」标签页
    • 设置「第一次失败」为「重启服务」,重启延迟设为3秒
  2. 主服务检测到新版本后的逻辑:
    using System.Diagnostics;
    using System.IO;
    
    var tempNewDir = @"C:\Temp\ServiceNewVersion";
    var flagPath = Path.Combine(tempNewDir, "update_flag.txt");
    
    // 下载新版本文件到临时目录
    // ...(省略下载逻辑)
    
    // 标记需要更新,写入当前服务目录路径
    File.WriteAllText(flagPath, AppContext.BaseDirectory);
    
    // 写入自定义事件日志,说明是主动更新操作
    EventLog.WriteEntry("YourServiceName", "触发版本更新,主动终止服务以执行重启", EventLogEntryType.Information);
    
    // 以非0码退出,触发故障恢复重启
    Environment.Exit(1);
    
  3. 在服务OnStart方法开头添加更新检测逻辑:
    protected override void OnStart(string[] args)
    {
        var tempNewDir = @"C:\Temp\ServiceNewVersion";
        var flagPath = Path.Combine(tempNewDir, "update_flag.txt");
    
        if (File.Exists(flagPath))
        {
            var targetDir = File.ReadAllText(flagPath);
            // 替换服务文件
            foreach (var file in Directory.GetFiles(tempNewDir))
            {
                var destPath = Path.Combine(targetDir, Path.GetFileName(file));
                File.Copy(file, destPath, overwrite: true);
            }
            // 清理临时文件
            Directory.Delete(tempNewDir, recursive: true);
        }
    
        // 正常启动服务的业务逻辑
        // ...
    }
    

内容的提问来源于stack exchange,提问作者Fabian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 21:55:14