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

.NET 6/7下如何将Windows Forms应用转为Windows服务运行?

问题

我有一个定时调用Web服务的Windows Forms应用,当初选WinForms是方便跟踪通信和测试。现在要把它改成Windows服务部署到服务器实现无人值守。网上的示例都是从Worker Service项目开始,但我想直接改造现有WinForms应用,不想重新做。

之前在.NET 4.5里我是这么实现的:

.NET 4.5 服务启动代码

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[] 
        { 
            new CRDService() 
        };
        ServiceBase.Run(ServicesToRun);
    }
}

.NET 4.5 服务加载器代码

public partial class CRDService : ServiceBase
{
    private int processId;
    private string filePath;

    public CRDService()
    {
        InitializeComponent();
    }

    public void Verify(string[] args)
    {
        this.OnStart(args);

        // do something here...  

        this.OnStop();
    }  

    protected override void OnStart(string[] args)
    {
        var location = new Uri(Assembly.GetEntryAssembly().CodeBase).LocalPath;
        var path = Path.GetDirectoryName(location);
        string appName = ConfigurationManager.AppSettings["AppName"];
        var serverPath = Path.Combine(path, appName);
        Process cmd = new Process();
        cmd.StartInfo.FileName = serverPath;
        cmd.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        using (var f = File.Create(Path.Combine(path, "TestFile.txt")))
        {
            filePath = f.Name;
            string msg = "Process started at " + DateTime.Now.ToString();
            f.Write(Encoding.ASCII.GetBytes(msg),0,msg.Length);
        }

        cmd.StartInfo.Arguments = filePath;
        cmd.Start();
        processId = cmd.Id;  
    }

    protected override void OnStop()
    {
        Process process = null;
        try
        {
            process = Process.GetProcessById((int)processId);
        }
        finally
        {
            if (process != null)
            {
                process.Kill();
                process.WaitForExit();
                process.Dispose();
            }

            File.Delete(filePath);
        }  
    }
}

但这套代码在.NET 6/7的BackgroundService环境下跑不起来,求可行的实现方式?


可行实现方案

方案一:改造现有WinForms应用,支持双模式运行

在.NET 6+/7里,可以通过判断启动参数,让程序既能以WinForms界面启动(用于测试),又能以服务模式运行,无需拆分项目。

  1. 修改Program.cs入口逻辑
    替换原WinForms入口,添加服务模式分支:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.ServiceProcess;

namespace YourWinFormsApp
{
    internal static class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            // 检测启动参数,判断是否以服务模式运行
            if (args.Contains("--service"))
            {
                var services = new ServiceBase[] { new WinFormsHostService() };
                ServiceBase.Run(services);
                return;
            }

            // 正常启动WinForms界面
            ApplicationConfiguration.Initialize();
            Application.Run(new MainForm());
        }
    }

    // 自定义服务类,托管核心业务逻辑
    public class WinFormsHostService : ServiceBase
    {
        private Timer? _taskTimer;
        private WebServiceCaller? _caller; // 从WinForms抽离的核心逻辑类

        protected override void OnStart(string[] args)
        {
            // 初始化核心逻辑(和WinForms界面解耦)
            _caller = new WebServiceCaller();
            
            // 设置定时触发规则,比如每分钟执行一次
            _taskTimer = new Timer(ExecuteTask, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
        }

        private void ExecuteTask(object? state)
        {
            // 执行原来WinForms里的定时调用逻辑
            _caller?.InvokeWebService();
        }

        protected override void OnStop()
        {
            // 停止定时器并释放资源
            _taskTimer?.Change(Timeout.Infinite, 0);
            _taskTimer?.Dispose();
            _caller?.Dispose();
        }
    }
}
  1. 抽离核心业务逻辑
    把WinForms中调用Web服务的代码从Form类里独立出来,做成不依赖UI的类:
public class WebServiceCaller : IDisposable
{
    private readonly HttpClient _httpClient = new HttpClient();

    public void InvokeWebService()
    {
        // 这里写原来WinForms里调用Web服务的代码
        // 注意:不能引用任何WinForms控件或UI相关API
        var response = _httpClient.GetAsync("https://your-service-url").Result;
        response.EnsureSuccessStatusCode();
    }

    public void Dispose()
    {
        _httpClient.Dispose();
    }
}
  1. 安装服务
    用管理员权限打开命令提示符,执行以下命令:
# 创建服务
sc create "WinFormsWebService" binPath="C:\你的应用路径\YourWinFormsApp.exe --service"

# 启动服务
sc start WinFormsWebService

方案二:用Worker Service托管核心逻辑

如果不想改动WinForms项目结构,可以把核心逻辑抽成类库,用Worker Service项目托管,更符合.NET 6+的服务规范。

  1. 创建Worker Service项目
    在Visual Studio中新建Worker Service项目,将抽离的WebServiceCaller类添加到项目中,修改Worker类:
namespace YourWorkerService
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly WebServiceCaller _caller;

        public Worker(ILogger<Worker> logger, WebServiceCaller caller)
        {
            _logger = logger;
            _caller = caller;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("开始调用Web服务:{Time}", DateTimeOffset.Now);
                _caller.InvokeWebService();
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
            }
        }
    }
}
  1. 注册服务依赖
    在Program.cs中注册WebServiceCaller:
var builder = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
        services.AddSingleton<WebServiceCaller>();
    });

builder.UseWindowsService(); // 启用Windows服务支持

var host = builder.Build();
host.Run();
  1. 发布并安装服务
    用.NET CLI发布后安装:
# 发布服务
dotnet publish -c Release -o C:\发布路径\WorkerService

# 安装服务
sc create "WebServiceWorker" binPath="C:\发布路径\WorkerService\YourWorkerService.exe"

# 启动服务
sc start WebServiceWorker

关键注意事项

  • 服务模式下绝对不能访问UI控件,所有业务逻辑必须和WinForms界面完全解耦。
  • 服务运行账户需要有访问Web服务、本地文件/数据库的权限,建议用专用服务账户。
  • 日志改用.NET自带的ILogger,避免依赖WinForms的日志控件,方便排查问题。
  • 调试服务时,可以添加--debug参数,让程序以控制台模式运行,无需安装服务。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 20:59:56