worker service + usewindowsservice() 是 .net 6+ 首选方案,但必须满足三前提:项目 sdk 为 microsoft.net.sdk、usewindowsservice() 在 build() 前调用、发布须自包含;否则注册失败报错 1053。

直接用 ServiceBase 写 Windows 服务在 .NET 6+ 已不是首选;真正轻量、可测试、跨平台兼容的方案是 Worker Service + UseWindowsService(),但必须满足三个硬性前提,否则服务根本注册不进 SCM。
为什么 OnStart 阻塞会导致 Error 1053
SCM 要求服务在 30 秒内返回启动确认,而 OnStart 是同步方法 —— 一旦你在里面写 Thread.Sleep(5000)、Task.Run(...).Wait() 或直接调用阻塞 I/O(比如未加 async/await 的文件读写),SCM 就判定超时。
-
OnStart只能做“发车”动作:启动后台线程 /Task/BackgroundService,然后立刻返回 - 日志、配置加载、数据库连接池初始化等耗时操作,必须移到后台执行,不能卡在
OnStart里 - 调试时若用
Console.WriteLine输出,看不到内容 —— Windows 服务默认无控制台,需改用EventLog或ILogger+AddEventLog()
.NET 6+ Worker Service 必须配对的三要素
新建一个 Worker Service 项目后,光加 builder.UseWindowsService() 不会生效,缺一不可:
- 项目 SDK 必须是
<project sdk="Microsoft.NET.Sdk"></project>,不能是Microsoft.NET.Sdk.Web或Microsoft.NET.Sdk.Worker(后者已过时) -
UseWindowsService()必须在ConfigureServices()之后、Build()之前调用,否则IHostApplicationLifetime等接口无法注入 - 发布时必须用
dotnet publish -r win-x64 --self-contained true,否则运行时报错Could not load file or assembly 'System.ServiceProcess.ServiceController'
sc create 安装时路径和权限踩坑清单
sc create 是最干净的安装方式,但 Windows 对路径解析极严格:
- 可执行路径含空格时,
binPath=值**必须**用英文双引号包裹:sc create MySvc binPath= "C:My AppMySvc.exe" - 反斜杠要写成双反斜杠
\或正斜杠/,单会被当成转义符,导致路径截断 - 默认账户
LocalSystem不能访问网络共享或域数据库;如需访问,得显式指定obj= "NT AUTHORITYNetworkService",并确保该账户对程序目录有读+执行权限 - 安装后首次启动失败,别急着删服务 —— 先查 Windows 事件查看器 → Windows 日志 → 应用程序,过滤来源为
.NET Runtime或你的服务名
调试 Worker Service 不能双击运行
Worker Service 在 Windows 上以服务身份运行时没有 UI 和控制台,双击 exe 或按 F5 运行只会闪退或报错 Service cannot be started:
- 开发阶段直接按 Ctrl+F5 运行(不调试),它会以控制台模式启动,输出全可见
- 真机调试需以管理员身份启动 VS,然后「调试 → 附加到进程」→ 找到
svchost.exe→ 在「显示所有用户的进程」勾选下,筛选出你的服务名对应 PID - 加断点前务必确认:服务已用
sc start启动,且OnStart或ExecuteAsync确实已触发(可在入口加一行EventLog.WriteEntry("Debug: Entered OnStart", EventLogEntryType.Information)验证)
最容易被忽略的是:.NET 6+ 的 UseWindowsService() 本质仍是桥接到 ServiceBase,所以它只在 Windows 平台生效;若你本地开发用 macOS 或 Linux,UseWindowsService() 调用会被静默忽略 —— 这不是 bug,是设计使然。











