应选worker service模板(.net 6+),它基于ihostedservice实现生命周期管理,需用sc.exe注册为windows服务并配置自包含发布;开发时可通过--console参数以控制台模式调试。

Windows服务项目模板选哪个?
Visual Studio里新建项目时,别直接选“Windows服务(.NET Framework)”——那是旧版,.NET 6/7/8 默认不支持。现在主流做法是用 WorkerService 模板,它本质是带生命周期管理的长期运行后台程序,能被 sc.exe 或 InstallUtil.exe 注册为 Windows 服务,也兼容 dotnet publish 部署。
- 新建项目选
Worker Service(语言选 C#,框架选 .NET 6+)
- 如果必须兼容 .NET Framework(比如依赖老 Win32 API),才用传统
ServiceBase 类,但得手动处理安装、日志、交互权限等细节
-
WorkerService 模板默认启用 IHostedService,启动/停止逻辑写在 StartAsync/StopAsync 里,比重写 OnStart 更符合现代依赖注入习惯
怎么让 WorkerService 真正注册成 Windows 服务?dotnet publish 出来的只是普通可执行文件,不会自动变成服务。必须用 Windows 原生命令注册:
- 先确保程序编译为自包含(
self-contained)发布,避免目标机缺运行时:dotnet publish -c Release -r win-x64 --self-contained true
- 发布后进到输出目录,用
sc.exe 创建服务(管理员权限运行 cmd):sc create "MyBackgroundSvc" binPath= "C:\path\to\MyApp.exe" start= auto
- 注意
binPath= 后面有空格,且路径含空格时要加英文双引号;start= auto 表示开机自启,也可用 start= demand 手动启动
Worker Service(语言选 C#,框架选 .NET 6+)ServiceBase 类,但得手动处理安装、日志、交互权限等细节WorkerService 模板默认启用 IHostedService,启动/停止逻辑写在 StartAsync/StopAsync 里,比重写 OnStart 更符合现代依赖注入习惯dotnet publish 出来的只是普通可执行文件,不会自动变成服务。必须用 Windows 原生命令注册:
- 先确保程序编译为自包含(
self-contained)发布,避免目标机缺运行时:dotnet publish -c Release -r win-x64 --self-contained true
- 发布后进到输出目录,用
sc.exe创建服务(管理员权限运行 cmd):sc create "MyBackgroundSvc" binPath= "C:\path\to\MyApp.exe" start= auto
- 注意
binPath=后面有空格,且路径含空格时要加英文双引号;start= auto表示开机自启,也可用start= demand手动启动
常见错误:
- 忘了以管理员身份运行 cmd → 报错
Access is denied -
binPath写成相对路径或漏掉.exe→ 服务状态卡在Starting...,查Event Viewer → Windows Logs → System会看到Service cannot be started - 没加
--self-contained true,目标机没装对应 .NET 运行时 → 服务启动失败,事件日志提示0xc0000135
服务里怎么安全读配置、写日志、访问文件?
Windows 服务默认以 LocalSystem 或 NetworkService 身份运行,权限跟登录用户完全不同:
- 配置文件(如
appsettings.json)放在服务可执行文件同级目录即可,IConfiguration 默认能加载;但别硬编码 C:\Users... 这类用户路径
- 写日志别直接写桌面或文档目录——服务账户没那些路径权限。推荐:
- 用
NLog 或 Microsoft.Extensions.Logging.EventLog 写入 Windows 事件日志(需在 Program.cs 中注册 AddEventLog)
- 或指定绝对路径如
C:\ProgramData\MyApp\logs\,并提前给该目录授予 NT AUTHORITY\SYSTEM 修改权限
- 访问网络共享或数据库时,确认服务账户有对应凭据;若需用户上下文(比如访问 OneDrive),基本不可行——服务不能交互式登录
调试阶段怎么绕过安装和启动流程?
每次改代码都 sc delete + sc create + 启动太慢,而且 IDE 无法直接附加调试:
- 开发时在
Program.cs 加个开关,让程序既能当控制台运行,也能当服务运行:var isService = !(Debugger.IsAttached || args.Contains("--console"));
- 然后用
Host.CreateDefaultBuilder().UseWindowsService() 包裹,仅在 isService 为 true 时启用
- 运行时加参数
--console 就直接跑在命令行里,断点、日志、异常全可见
- 发布前删掉这个判断,或通过
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") 控制
appsettings.json)放在服务可执行文件同级目录即可,IConfiguration 默认能加载;但别硬编码 C:\Users... 这类用户路径 - 用
NLog或Microsoft.Extensions.Logging.EventLog写入 Windows 事件日志(需在Program.cs中注册AddEventLog) - 或指定绝对路径如
C:\ProgramData\MyApp\logs\,并提前给该目录授予NT AUTHORITY\SYSTEM修改权限
sc delete + sc create + 启动太慢,而且 IDE 无法直接附加调试:
- 开发时在
Program.cs加个开关,让程序既能当控制台运行,也能当服务运行:var isService = !(Debugger.IsAttached || args.Contains("--console")); - 然后用
Host.CreateDefaultBuilder().UseWindowsService()包裹,仅在isService为 true 时启用 - 运行时加参数
--console就直接跑在命令行里,断点、日志、异常全可见 - 发布前删掉这个判断,或通过
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")控制
容易忽略的一点:服务启动超时默认 30 秒,如果 StartAsync 里做了耗时初始化(比如连数据库、加载大文件),务必用 cancellationToken 做超时响应,否则服务会卡死在 “Starting” 状态,系统最终报 Error 1053。











