Worker Service应通过dotnet new worker -n MySvc或VS中搜索“Worker Service”模板创建,禁用已失效的.NET Framework Windows Service模板;需安装Microsoft.Extensions.Hosting.WindowsServices包并调用.UseWindowsService(),发布须自包含,配置与日志路径须匹配服务账户权限。

直接用 dotnet new worker 创建,别选“Windows服务(.NET Framework)”模板——它在 .NET 6+ 已彻底失效,硬上只会卡在启动失败或事件日志报错 1053。
Worker Service 项目怎么新建才不踩坑
Visual Studio 里新建项目时搜 “Worker Service”,不是 “Windows Service”;命令行执行:dotnet new worker -n MySvc。生成结构只有 Program.cs 和 Worker.cs,没有 WinForms/WPF 那套 UI 线程模型,天然避开了 SCM(服务控制管理器)拒绝加载的常见原因。
- 删掉旧版
ServiceBase相关代码或引用——System.ServiceProcess.dll在 .NET Core+ 中不被原生支持 - 不要手动改
.csproj添加——Worker Service 是控制台宿主,WinExe Exe类型已由 SDK 自动设置 - 若需兼容 Linux 守护进程,同一份代码后续加
Microsoft.Extensions.Hosting.Systemd即可,无需重写逻辑
为什么 sc start 后服务状态卡在 START_PENDING(错误 1053)
根本原因是宿主没正确绑定 Windows 服务上下文,或者初始化过程被阻塞。最常漏掉的是两件事:
- 没装
Microsoft.Extensions.Hosting.WindowsServicesNuGet 包:dotnet add package Microsoft.Extensions.Hosting.WindowsServices -
Program.cs里没调用.UseWindowsService(),或漏设options.ServiceName(值不能含空格、中文、特殊字符,比如"MyAppSvc"可以,"My App Service"会注册失败) - 用了
host.Start()+host.WaitForShutdown()替代host.Run()——Run()才会接管 SCM 生命周期信号,Start()只是异步启动,进程随即退出
发布和注册服务时 binPath 怎么写才有效
sc create 的 binPath= 参数对路径格式极其敏感,写错一个空格或引号就会导致服务无法启动,事件日志只报 “服务未及时响应”。
-
binPath=后面**必须有一个英文空格**,例如:sc create "MySvc" binPath="C:\MySvc\MySvc.exe" - 路径含空格时,整个路径必须用英文双引号包裹,且引号内不能有多余空格:
binPath="C:\Program Files\MySvc\MySvc.exe"✅,binPath=" C:\...\MySvc.exe "❌ - 务必用自包含发布:
dotnet publish -c Release -r win-x64 --self-contained true,否则目标机没装对应 .NET 运行时,事件日志报错0xc0000135 -
obj=推荐设为"NT AUTHORITY\NetworkService"或专用服务账户,避免用"LocalSystem"——权限过大,审计难通过
日志写哪?appsettings.json 放哪?
服务默认以 NT AUTHORITY\SYSTEM 身份运行,对用户目录(如 C:\Users\xxx\Desktop)无写入权限,硬写会静默失败。
- 配置文件
appsettings.json必须放在服务可执行文件同级目录,IConfiguration默认能加载,别用绝对路径硬编码 - 日志别写文件到桌面或文档夹——改用
Microsoft.Extensions.Logging.EventLog写入 Windows 事件查看器(需在Program.cs中调用services.AddEventLog()) - 若坚持写文件,路径必须是系统级可写位置,如
C:\ProgramData\MySvc\logs\,并提前用icacls授予NT AUTHORITY\SYSTEM修改权限 -
Worker.cs中的ExecuteAsync不能阻塞线程(如用Thread.Sleep),必须用await Task.Delay(..., stoppingToken)响应停止信号
真正容易被忽略的点是:服务账户权限和配置文件位置是耦合的——你改了日志路径,就得同步改 NTFS 权限;你换了运行账户,就得重新验证所有 IO 路径是否可达。这些细节不会报编译错误,但会让服务在测试环境跑得好好的,一上生产就静默挂掉。











