.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界面启动(用于测试),又能以服务模式运行,无需拆分项目。
- 修改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(); } } }
- 抽离核心业务逻辑
把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(); } }
- 安装服务
用管理员权限打开命令提示符,执行以下命令:
# 创建服务 sc create "WinFormsWebService" binPath="C:\你的应用路径\YourWinFormsApp.exe --service" # 启动服务 sc start WinFormsWebService
方案二:用Worker Service托管核心逻辑
如果不想改动WinForms项目结构,可以把核心逻辑抽成类库,用Worker Service项目托管,更符合.NET 6+的服务规范。
- 创建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); } } } }
- 注册服务依赖
在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();
- 发布并安装服务
用.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
相关产品推荐
相关产品推荐

