跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

C#怎么使用Topshelf_C#快速将控制台程序部署为Windows服务【实用】

推荐直接使用 Microsoft.Extensions.Hosting.WindowsServices,因其为微软官方推荐方案,兼容 .NET 6+ 宿主模型、原生集成 DI 与配置,无需额外依赖;Topshelf 已归档,不支持现代 .NET,易出现服务未真正运行、日志失败及安装报错等问题。 To ps helf 是个已被官方归档的库(2023 年停止维护),现在直接用
Microsoft.Extensions.Hosting.WindowsServices
更稳、更轻、无额外依赖。如果你手头已有 Topshelf 项目,迁移成本不高;但新项目别再引入它了。 为什么 Topshelf 现在不推荐用 Topshelf 本质是封装了
ServiceBase
的调用逻辑,并提供命令行交互能力。但它长期没适配 .NET 6+ 的宿主模型,也不支持现代 DI 和配置系统原生集成。官方 GitHub 已标记为
Archived
,NuGet 上最新版(4.2.1)仍基于 .NET Framework 4.6.1,无法真正跑在 .NET 6+ 的 Windows 服务模式下。 常见现象: 安装后服务状态显示“已启动”,但
ExecuteAsync
根本没执行——其实是 Topshelf 启动了控制台宿主,却没正确切换到 Windows Service 上下文
topshelf install
报错 “Could not load file or assembly 'System.ServiceProcess'”——.NET Core/5+ 默认不带这个程序集 日志写不到事件查看器,
EventLog.WriteEntry
抛
SecurityException
,因为 Topshelf 没帮你提权注册事件源 .NET 6+ 替代方案:用 Worker Service + WindowsServices 包 这是微软当前唯一推荐路径,零学习成本,且天然兼容所有
IHostedService
生态(如
BackgroundService
、Quartz.NET、Hangfire.Client)。 操作步骤: 新建项目选
Worker Service
模板(不是 “Windows Service (.NET Framework)”) NuGet 安装
Microsoft.Extensions.Hosting.WindowsServices
修改
Program.cs
:在
Host.CreateApplicationBuilder
后加
builder.Services.AddHostedService()
,再调用
builder.UseWindowsService()
确保发布时选
win-x64
或
win-x86
,不要用
AnyCPU
(尤其当你引用了 x86 原生 DLL) 关键代码片段:
var builder = Host.CreateApplicationBuilder(args); builder.UseWindowsService(); // ← 这一行必须有,且要在 Build() 前 builder.Services.AddHostedService(); var host = builder.Build(); host.Run();
sc.exe 安装服务时最常踩的坑 不用
InstallUtil.exe
,也不用 Topshelf 的
install
命令。直接用系统自带的
sc.exe
,干净、可控、权限明确。 稿定在线PS PS软件网页版 下载 典型命令:
sc create MyService binPath= "C:\MyService\MyService.exe" start= auto obj= "LocalSystem"
注意细节:
binPath=
后面**必须有空格**,且路径不能带引号(除非含空格,才用英文双引号包裹整个路径)
obj=
指定运行账户:
LocalSystem
权限最高,但访问网络资源受限;若需连 SQL 或 SMB 共享,请用
DOMAIN\user
并提前赋权 安装前务必以管理员身份运行 CMD/PowerShell,否则
Access Denied
卸载用
sc delete MyService
,别漏掉已存在的同名服务残留 OnStart 超时失败?你可能根本没在用 Windows Service 模式 很多人以为只要项目类型叫“Windows Service”就自动进服务上下文,其实不是。.NET 6+ 必须显式调用
UseWindowsService()
,否则即使你用
sc create
注册了,进程仍是普通控制台应用,只是被 SCM(服务控制管理器)拉起来而已。 验证方式: 任务管理器 → 详细信息 → 找到你的服务进程 → 右键“转到服务”,如果没关联,说明没走 Windows Service 流程 在
Worker.ExecuteAsync
开头加一行
_logger.LogInformation("Running as Windows Service: {IsService}", Environment.IsService())
,输出
false
就是没生效 服务启动超时(30 秒)不是因为你代码慢,而是 SCM 根本没收到“服务已就绪”信号——因为宿主没注册服务通知回调 真正容易被忽略的一点:发布后的 EXE 文件名,必须和
sc create
里写的
binPath
完全一致,包括大小写。Windows 文件系统不区分大小写,但 SCM 解析
binPath
时会校验签名和路径有效性,名字对不上会导致静默失败。

相关文章