Aore.WindowsService
0.1.3
dotnet add package Aore.WindowsService --version 0.1.3
NuGet\Install-Package Aore.WindowsService -Version 0.1.3
<PackageReference Include="Aore.WindowsService" Version="0.1.3" />
<PackageVersion Include="Aore.WindowsService" Version="0.1.3" />
<PackageReference Include="Aore.WindowsService" />
paket add Aore.WindowsService --version 0.1.3
#r "nuget: Aore.WindowsService, 0.1.3"
#:package Aore.WindowsService@0.1.3
#addin nuget:?package=Aore.WindowsService&version=0.1.3
#tool nuget:?package=Aore.WindowsService&version=0.1.3
Aore.WindowsService
Windows Service 开发核心库 —— 新服务程序只需引用本库、实现一个业务类、写两行入口代码,即可获得「双击 exe 控制台运行、SCM 服务运行、CLI 安装/卸载/启停」的完整生产级能力。
- 双形态运行:双击 exe 自动进入控制台模式(Ctrl+C 优雅停机);由服务控制管理器(SCM)启动自动进入服务模式——无需任何配置切换;
- 原生 SCM 实现:基于 advapi32 原生 API(
StartServiceCtrlDispatcher/SetServiceStatus等),完全绕开ServiceBase,netfx 与 modern .NET 行为完全一致; - 自管理 CLI:
install/uninstall/start/stop/status/run/help/version全动词,无需 sc.exe; - 状态机正确性:waitHint / checkpoint 自动续报,慢启动与慢停止不会被判失联;支持暂停/继续、失败自动重启恢复策略;
- 0 错误 0 警告门禁:
TreatWarningsAsErrors全 TFM 机械保证;92 个单元测试 + 真实 SCM 集成测试。
Copyright © 2026-2026 WEI.ZHOU (Willis). All rights reserved. — MIT License
目录
- 支持的目标框架
- 核心包 Aore.WindowsService
- Hosting 集成包 Aore.WindowsService.Hosting
- 快速开始
- 运行方式总览
- 完整示例代码(10 个场景)
- 服务元数据配置(三层优先级)
- 失败恢复 / 运行账户 / 日志
- CLI 完整参考
- 构建与测试
- NuGet 发布
- AOT / 裁剪说明
- 项目结构
- 文档导航
- 许可证
支持的目标框架
| TFM | 运行时 | 说明 |
|---|---|---|
| net462 / net472 / net48 | .NET Framework 4.6.2+ | 由 NuGet 引用程序集编译,无需本机 targeting pack |
| net8.0 / net9.0 / net10.0 | 对应 .NET 版本 | 同样代码、同样行为 |
公共 API 在全部 TFM 上一致;netfx 兼容红线(禁用 Span/ValueTuple/Index 等)由全 TFM 构建门禁机械保证。
1. 核心包 Aore.WindowsService
必装包。包含四大部分:SCM 运行时、两级服务 API、自管理 CLI、原生安装器。仅依赖 Microsoft.Extensions.Logging.Abstractions(8.0.x LTS)。
1.1 静态入口 AoreService
| 方法 | 说明 |
|---|---|
int Run<TService>(string[]? args = null, Action<ServiceDescriptor>? configure = null) |
一站式入口(高阶服务):解析 CLI 动词 → 检测运行模式 → 驱动生命周期 → 返回退出码 |
int Run(IWin32Service service, ...) |
低阶服务变体 |
int Run(IWin32ServiceStateMachine stateMachine, ...) |
进阶状态机变体(运行路径限定服务模式) |
int RunAsService(IWin32Service service) / RunAsService(IWin32ServiceStateMachine) |
强制服务模式(须由 SCM 启动进程) |
int RunAsConsole(IWin32Service service) |
强制控制台模式 |
int? HandleCommandLine(string[]? args = null, ...) |
仅处理管理动词(install/start/...),返回 null 表示继续应用运行路径——供 ASP.NET Core 等自管宿主使用(见示例 9) |
static ILoggerFactory? LoggerFactory { get; set; } |
注入业务日志工厂(Serilog/NLog 等) |
1.2 高阶服务基类 AoreWindowsServiceBase
| 成员 | 说明 |
|---|---|
ExecuteAsync(CancellationToken) |
唯一必须实现:返回或令牌触发即优雅停机;未处理异常 → 服务以 -1 退出码停止 |
StopGracePeriod |
停机宽限期(默认 30 秒,可配置);超时任务被强制终止并如实上报 -1 |
Logger / Log |
日志器(控制台模式自动注入);Log 为空实现回退的便捷访问器 |
ExitCode |
执行退出码(0 成功 / -1 异常或超时) |
ExecutionTask |
业务任务(控制台模式等待其完成) |
OnStarting() / OnStopping() |
启动/停机前钩子 |
1.3 低阶接口(完全控制)
public interface IWin32Service
{
string ServiceName { get; }
// 快速返回;长启动可上报 StartPending + waitHint;上报 Stopped 可直接结束服务
void Start(string[] startupArguments, IServiceStatusCallback statusCallback);
int Stop(); // 返回值 = 服务退出码
}
public interface IPausableWin32Service : IWin32Service
{
void Pause(); // 实现后 SCM 才出现暂停/继续按钮
void Continue();
}
public interface IServiceStatusCallback
{
void SetStatus(ServiceState state, int waitHintMs = 0, int exitCode = 0);
}
1.4 进阶状态机
public interface IWin32ServiceStateMachine
{
string ServiceName { get; }
bool SupportsPause { get; } // true 时向 SCM 声明 PAUSE_CONTINUE
void Start(string[] startupArguments, IServiceStatusCallback statusCallback);
void HandleCommand(ServiceControl control, int eventType, IntPtr eventData);
void Dispose();
}
处理 SessionChange(终端会话)、PowerEvent(电源)等扩展控制;内置 SimpleServiceStateMachine 负责把 IWin32Service 适配为完整状态机。
1.5 配置类型(命名空间 Aore.WindowsService.Configuration)
| 类型 | 用途 |
|---|---|
[assembly: AoreWindowsService("名称", DisplayName=, Description=, StartMode=, Arguments=)] |
声明式服务元数据,install/status 零代码 |
ServiceDescriptor |
服务名/显示名/描述/启动类型/凭据/失败恢复/命令行参数 |
AoreServiceStartMode |
Automatic / AutomaticDelayedStart / Manual / Disabled |
AoreServiceCredentials |
LocalSystem / NetworkService / LocalService / FromUserAccount(账户, 密码)(SecureString 重载推荐,原生缓冲即用即清零) |
AoreFailureActions / AoreFailureAction |
失败恢复:RestartOnFailure(1000, 5000, 30000, resetPeriodDays) |
1.6 枚举
| 枚举 | 值 |
|---|---|
ServiceState |
Stopped=1, StartPending=2, StopPending=3, Running=4, ContinuePending=5, PausePending=6, Paused=7 |
ServiceControl |
Stop=1, Pause=2, Continue=3, Interrogate=4, Shutdown=5, PowerEvent=13, SessionChange=14, PreShutdown=15 |
ServiceAcceptFlags |
Stop=1, PauseAndContinue=2, Shutdown=4 |
AoreExitCode(进程退出码) |
Success=0, UnknownError=1, InvalidArguments=2, AccessDenied=3, ScmOperationFailed=4, ServiceNotInstalled=5, ServiceAlreadyExists=6, ServiceStateConflict=7, ServiceModeInitializationFailed=8 |
1.7 环境检测与日志
AoreServiceEnvironment.IsWindows(); // 平台守卫
AoreServiceEnvironment.IsRunningAsWindowsService(); // 父进程是否 services.exe
AoreServiceEnvironment.IsRunningElevated(); // 是否管理员
new AoreConsoleLogger { MinimumLevel = LogLevel.Information, UseColor = true };
2. Hosting 集成包 Aore.WindowsService.Hosting
可选包:把 Aore 服务生命周期桥接进 Microsoft.Extensions.Hosting 世界——DI、IHostedService、appsettings、ILogger<T> 全部可用。
2.1 注册扩展
public static class AoreWindowsServiceHostingExtensions
{
// 注册 IHostLifetime:服务模式桥接 SCM;控制台模式自动接管 Ctrl+C
public static IServiceCollection AddAoreWindowsServiceLifetime(
this IServiceCollection services,
Action<AoreWindowsServiceOptions>? configureOptions = null);
}
public sealed class AoreWindowsServiceOptions { public string? ServiceName { get; set; } }
2.2 生命周期映射
| 宿主事件 | SCM 状态 |
|---|---|
ApplicationStarted |
RUNNING |
ApplicationStopping |
STOP_PENDING |
ApplicationStopped |
STOPPED |
| SCM 停止/关机命令 | StopApplication() |
服务模式在后台线程驱动 SCM 分派;控制台模式自动接管 Ctrl+C。注意:本包会替换默认 ConsoleLifetime(IHostLifetime),控制台停机由本包负责。
2.3 用法
// 1. 先处理管理动词(install/start/...)
var cliExitCode = AoreService.HandleCommandLine(args);
if (cliExitCode.HasValue) return cliExitCode.Value;
// 2. 宿主运行
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddAoreWindowsServiceLifetime(options =>
options.ServiceName = builder.Configuration.GetSection("AoreService")["ServiceName"]);
builder.Services.AddHostedService<Worker>();
await builder.Build().RunAsync();
应用侧需自行引用
Microsoft.Extensions.Hosting(8.0.0+)。
快速开始
// 1. 业务服务:只需实现一个类
public sealed class DataSyncService : AoreWindowsServiceBase
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 业务循环,token 触发即优雅退出
}
return Task.CompletedTask;
}
}
// 2. 入口:一行
public static int Main(string[] args) => AoreService.Run<DataSyncService>(args);
声明式安装信息(可选):
[assembly: AoreWindowsService("DataSyncSvc",
DisplayName = "数据同步服务",
StartMode = AoreServiceStartMode.AutomaticDelayedStart)]
运行方式总览
| 操作 | 命令 | 说明 |
|---|---|---|
| 控制台运行(开发调试) | 双击 exe 或 MyService.exe run |
Ctrl+C 优雅停机 |
| 安装为服务 | MyService.exe install(管理员) |
支持显示名/描述/启动类型/账户/失败重启 |
| 安装(自定义账户,推荐凭据输入) | MyService.exe install --credential user --username DOMAIN\user --password-secure |
密码不回显、不落命令行 |
| 启动 / 停止 | MyService.exe start / stop |
自动等待状态切换 |
| 查询状态 | MyService.exe status |
状态 / PID / 启动类型 / 账户 |
| 卸载 | MyService.exe uninstall |
先停止再删除 |
| 服务模式 | 由 SCM 启动(net start、services.msc) |
自动检测,无需参数 |
完整示例代码(10 个场景)
以下示例与 samples/ 下的可运行项目一一对应,均为完整可用代码,复制即用。
示例 1:心跳服务(高阶 API,最小项目)
对应 samples/Sample.Simple(net48; net8.0)。
MyService.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aore.WindowsService" Version="0.1.3" />
</ItemGroup>
</Project>
Worker.cs:
using Aore.WindowsService;
using Microsoft.Extensions.Logging;
namespace MyService;
public sealed class Worker : AoreWindowsServiceBase
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var count = 0;
while (!stoppingToken.IsCancellationRequested)
{
Log.LogInformation("心跳 #{Count}", ++count);
try
{
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
catch (OperationCanceledException)
{
break; // 停机令牌触发:尽快退出循环
}
}
Log.LogInformation("服务停机完成。"); // 这里做清理(落盘/关连接)
}
}
Program.cs:
using Aore.WindowsService;
using Aore.WindowsService.Configuration;
[assembly: AoreWindowsService("MySvc",
DisplayName = "我的服务",
Description = "示例:心跳服务",
StartMode = AoreServiceStartMode.AutomaticDelayedStart)]
namespace MyService;
public static class Program
{
public static int Main(string[] args) => AoreService.Run<Worker>(args);
}
运行:
dotnet build
bin\Debug\net8.0\MyService.exe :: 控制台模式(双击等价)
MyService.exe install --start :: 管理员:安装并启动
MyService.exe status / stop / uninstall
示例 2:定时批处理(PeriodicTimer)
对应 samples/Sample.BatchWorker(net8.0;netfx 用 Task.Delay 循环等价)。逻辑要点:错误隔离——单次批次失败只记日志,不终止服务。
using Aore.WindowsService;
using Microsoft.Extensions.Logging;
public sealed class BatchWorker : AoreWindowsServiceBase
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var timer = new PeriodicTimer(TimeSpan.FromMinutes(5));
try
{
while (await timer.WaitForNextTickAsync(stoppingToken))
{
try
{
await RunBatchAsync(stoppingToken);
}
catch (OperationCanceledException)
{
throw; // 停机取消要向上传播
}
catch (Exception ex)
{
Log.LogError(ex, "批次执行失败,等待下一周期重试");
}
}
}
catch (OperationCanceledException)
{
Log.LogInformation("批处理服务停机。");
}
}
private async Task RunBatchAsync(CancellationToken token)
{
Log.LogInformation("批次开始 {Time}", DateTimeOffset.Now);
await Task.Delay(TimeSpan.FromSeconds(2), token); // 替换为真实批处理
Log.LogInformation("批次完成");
}
}
netfx(net462/472/48)等价循环(PeriodicTimer 不可用):
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await RunBatchAsync(stoppingToken);
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
catch (OperationCanceledException) { break; }
catch (Exception ex) { Log.LogError(ex, "批次执行失败"); }
}
}
示例 3:TCP 回显监听
对应 samples/Sample.TcpEcho(net8.0)。逻辑要点:Stop 先停监听解除 Accept 阻塞,循环随之退出。
using System.Net;
using System.Net.Sockets;
using Aore.WindowsService;
public sealed class EchoListenerService : IWin32Service
{
private readonly int _port;
private TcpListener? _listener;
public EchoListenerService(int port) => _port = port;
public string ServiceName => "AoreSampleTcp";
public void Start(string[] startupArguments, IServiceStatusCallback statusCallback)
{
_listener = new TcpListener(IPAddress.Any, _port);
_listener.Start();
_ = Task.Run(AcceptLoopAsync); // 快速返回:Accept 循环转后台
}
private async Task AcceptLoopAsync()
{
try
{
while (true)
{
var client = await _listener!.AcceptTcpClientAsync();
_ = HandleClientAsync(client); // 每连接独立处理
}
}
catch (ObjectDisposedException) { } // Stop() 停止监听后的正常退出
catch (SocketException) { } // 监听套接字被强制关闭
}
private async Task HandleClientAsync(TcpClient client)
{
using (client)
await using (var stream = client.GetStream())
{
var buffer = new byte[4096];
var read = await stream.ReadAsync(buffer);
await stream.WriteAsync(buffer.AsMemory(0, read));
}
}
public int Stop()
{
_listener?.Stop(); // 解除 Accept 阻塞
_listener = null;
return 0;
}
}
入口(构造注入走低阶重载):public static int Main(string[] args) => AoreService.Run(new EchoListenerService(9000), args);
示例 4:文件目录监控(FileSystemWatcher)
对应 samples/Sample.FileWatch(net8.0)。逻辑要点:事件驱动型服务的主任务只"等停机";资源在 OnStarting/OnStopping 对称管理。
using Aore.WindowsService;
using Microsoft.Extensions.Logging;
public sealed class FileWatchService : AoreWindowsServiceBase
{
private const string WatchDirectory = @"D:\data\inbound";
private FileSystemWatcher? _watcher;
protected override void OnStarting()
{
_watcher = new FileSystemWatcher(WatchDirectory)
{
EnableRaisingEvents = true,
IncludeSubdirectories = true,
InternalBufferSize = 64 * 1024, // 事件洪峰防丢失
};
_watcher.Created += (_, e) => Log.LogInformation("发现文件 {File}", e.FullPath);
_watcher.Error += (_, e) => Log.LogError(e.GetException(), "监视器错误(缓冲溢出后应重建监视器)");
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
// 主任务仅等待停机
return Task.Delay(Timeout.InfiniteTimeSpan, stoppingToken)
.ContinueWith(_ => { }, TaskScheduler.Default);
}
protected override void OnStopping()
{
_watcher?.Dispose();
_watcher = null;
}
}
示例 5:队列消费(Channel)
对应 samples/Sample.QueueWorker(net8.0)。逻辑要点:ReadAllAsync(stoppingToken) 随停机令牌结束枚举;SingleReader=true 启用零分配快路径。
using System.Threading.Channels;
using Aore.WindowsService;
using Microsoft.Extensions.Logging;
public sealed class QueueWorker : AoreWindowsServiceBase
{
private readonly Channel<string> _queue; // 由应用共享(单例)
public QueueWorker(Channel<string> queue) => _queue = queue;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
await foreach (var item in _queue.Reader.ReadAllAsync(stoppingToken))
{
Log.LogInformation("处理 {Item}", item);
ProcessItem(item);
}
}
catch (OperationCanceledException)
{
Log.LogInformation("队列消费停机。");
}
}
private void ProcessItem(string item) { /* 业务处理 */ }
}
入口(构造注入走低阶重载):
public static int Main(string[] args)
{
var queue = Channel.CreateUnbounded<string>(new UnboundedChannelOptions { SingleReader = true });
// 生产者:queue.Writer.TryWrite("job-1");
return AoreService.Run(new QueueWorker(queue), args);
}
示例 6:低阶 API(长启动续报 / 暂停继续 / 自定义退出码)
对应 samples/Sample.FullFramework(net48)。逻辑要点:Start 快速返回;长启动主动上报 waitHint;暂停用信号量实现。
using Aore.WindowsService;
public sealed class ImportService : IWin32Service, IPausableWin32Service
{
public string ServiceName => "AoreImportSvc";
private CancellationTokenSource? _cts;
private Task? _work;
private ManualResetEventSlim? _paused;
public void Start(string[] startupArguments, IServiceStatusCallback statusCallback)
{
// Start 必须快速返回:全部工作转入后台任务
_cts = new CancellationTokenSource();
_paused = new ManualResetEventSlim(false);
_work = Task.Run(() =>
{
try
{
// 长启动:主动上报进度(运行时对 Pending 状态自动 checkpoint 续报)
statusCallback.SetStatus(ServiceState.StartPending, 60000);
WarmUp(); // 可能耗时 1 分钟的预热
statusCallback.SetStatus(ServiceState.Running);
MainLoop(_cts.Token);
}
catch (OperationCanceledException) { }
});
}
public int Stop()
{
_cts?.Cancel();
try
{
_work?.Wait(TimeSpan.FromSeconds(30));
return 0; // 自定义退出码
}
catch (AggregateException)
{
return -1; // 停止过程异常 → 失败退出码
}
}
public void Pause() => _paused?.Set(); // 挂起业务循环
public void Continue() => _paused?.Reset();
private void MainLoop(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
_paused?.Wait(token); // 暂停时在此等待
// ... 执行一个工作单元 ...
}
}
private void WarmUp() { /* ... */ }
}
示例 7:进阶状态机(SessionChange / PowerEvent)
对应 samples/Sample.StateMachine(net48; net8.0)。逻辑要点:HandleCommand 在 SCM 控制线程执行,必须快速返回;耗时清理放后台任务。
using Aore.WindowsService;
public sealed class SessionAwareStateMachine : IWin32ServiceStateMachine
{
public string ServiceName => "AoreSessionSvc";
public bool SupportsPause => false; // 不支持暂停就不向 SCM 声明该能力
private IServiceStatusCallback? _callback;
public void Start(string[] startupArguments, IServiceStatusCallback statusCallback)
{
_callback = statusCallback;
statusCallback.SetStatus(ServiceState.StartPending, 10000);
_ = Task.Run(() =>
{
// ... 业务初始化 ...
statusCallback.SetStatus(ServiceState.Running); // 就绪后上报
});
}
public void HandleCommand(ServiceControl control, int eventType, IntPtr eventData)
{
switch (control)
{
case ServiceControl.Stop:
case ServiceControl.Shutdown:
_callback?.SetStatus(ServiceState.StopPending, 5000);
Task.Run(() =>
{
// ... 清理资源 ...
_callback?.SetStatus(ServiceState.Stopped, exitCode: 0);
});
break;
case ServiceControl.SessionChange: // eventType:WTS_SESSION_LOCK(7)/UNLOCK(8) 等
// 处理会话锁定/解锁
break;
case ServiceControl.PowerEvent: // eventType:PBT_APMSUSPEND(4)/恢复(18) 等
break;
}
}
public void Dispose() { }
}
入口:public static int Main(string[] args) => AoreService.Run(new SessionAwareStateMachine(), args);(状态机重载同样支持完整 CLI 与安装元数据;运行路径限定服务模式)。
示例 8:Hosting 集成(DI / appsettings)
对应 samples/Sample.Hosting(net8.0)。需引用 Microsoft.Extensions.Hosting 与 Aore.WindowsService.Hosting。
Program.cs:
using Aore.WindowsService; // HandleCommandLine 需要
using Aore.WindowsService.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
// 1. 先处理管理动词(install/uninstall/start/stop/status/help/version)
var cliExitCode = AoreService.HandleCommandLine(args);
if (cliExitCode.HasValue)
{
return cliExitCode.Value;
}
// 2. 无管理动词(或 run)→ 继续 Worker 宿主运行路径
var builder = Host.CreateApplicationBuilder(args);
// 生命周期桥接:服务模式 ↔ SCM;控制台模式自动跳过(并接管 Ctrl+C)
builder.Services.AddAoreWindowsServiceLifetime(options =>
{
var name = builder.Configuration.GetSection("AoreService")["ServiceName"];
if (name != null)
{
options.ServiceName = name;
}
});
builder.Services.AddHostedService<DataWorker>(); // 业务
await builder.Build().RunAsync();
return 0;
appsettings.json:
{
"Logging": { "LogLevel": { "Default": "Information" } },
"AoreService": { "ServiceName": "AoreHostingSvc" }
}
行为说明:服务模式下宿主在后台线程驱动 SCM 分派(ApplicationStarted → RUNNING、ApplicationStopping → STOP_PENDING、ApplicationStopped → STOPPED;SCM 停止命令 → StopApplication());控制台模式自动接管 Ctrl+C。安装器仍来自核心包:exe install 照常可用。
示例 9:ASP.NET Core Web 集成
对应 samples/Sample.AspNetCore(net8.0)。两个集成关键点(缺一不可):
- 内容根校正:SCM 启动服务时工作目录是
System32,而 ASP.NET Core 默认以"当前目录"作为内容根——必须在构建 Host 前校正,否则 appsettings/wwwroot 全部失效; - 先分发管理动词:
HandleCommandLine处理 install/start/stop 等动词后直接返回退出码,无动词才继续 Web 运行路径(否则exe install会变成"启动网站")。
Program.cs(完整):
using Aore.WindowsService;
using Aore.WindowsService.Configuration;
using Aore.WindowsService.Hosting;
[assembly: AoreWindowsService("AoreSampleWeb",
DisplayName = "Aore 示例 - ASP.NET Core 集成",
StartMode = AoreServiceStartMode.Manual)]
// 关键点 1:校正内容根
Environment.CurrentDirectory = AppContext.BaseDirectory;
// 关键点 2:管理动词分发
var cliExitCode = AoreService.HandleCommandLine(args);
if (cliExitCode.HasValue)
{
return cliExitCode.Value;
}
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAoreWindowsServiceLifetime(options =>
{
var name = builder.Configuration.GetSection("AoreService")["ServiceName"];
if (name != null)
{
options.ServiceName = name;
}
});
builder.Services.AddHostedService<HeartbeatWorker>();
var app = builder.Build();
app.MapGet("/", () => "服务运行中");
app.MapGet("/api/uptime", () => Results.Json(new { UptimeSeconds = 123 }));
app.Run();
return 0;
运行验证:
MyWebApp.exe install :: 安装(管理动词被 HandleCommandLine 处理)
MyWebApp.exe start :: SCM 启动 → Kestrel 监听 :5000 → RUNNING
curl http://localhost:5000/ :: 正常响应
MyWebApp.exe status / stop / uninstall
直接双击 exe = 控制台模式(Kestrel 前台运行,Ctrl+C 优雅停机)。
示例 10:net48 传统项目接入
对应 samples/Sample.FullFramework、Sample.Simple(net48 目标)。要点:公共 API 与 modern .NET 完全一致;async/await、Task.Delay、字符串内插可直接使用;不可用的仅 PeriodicTimer、Channel、带令牌的异步 I/O 重载等(见示例 2/3 备注)。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aore.WindowsService" Version="0.1.3" />
</ItemGroup>
</Project>
服务与入口代码与示例 1 完全相同。低阶 API + 暂停/继续的 net48 完整示例见 samples/Sample.FullFramework。
服务元数据配置(三层优先级)
代码默认值 → [assembly: AoreWindowsService] → ServiceDescriptor 代码配置 → CLI 参数覆盖
// 代码配置(运行入口回调)
AoreService.Run<Worker>(args, descriptor =>
{
descriptor.FailureActions = AoreFailureActions.RestartOnFailure(1000, 5000, 30000, resetPeriodDays: 1);
descriptor.Credentials = AoreServiceCredentials.NetworkService;
});
失败恢复 / 运行账户 / 日志
| 能力 | 用法 |
|---|---|
| 失败自动重启 | install --failure-restart 5000(三段重启策略)或 AoreFailureActions.RestartOnFailure(...) |
| 运行账户 | --credential localsystem/networkservice/localservice/user;密码推荐 --password-secure(不回显,原生缓冲即用即清零) |
| 日志 | 控制台模式内置 ConsoleLogger;服务模式通过 AoreService.LoggerFactory 接入 Serilog/NLog |
CLI 完整参考
动词:run(--service 强制服务模式)· install · uninstall(--force)· start · stop · status · help · version(-v);无动词 = 自动模式。
install 选项:
| 选项 | 说明 |
|---|---|
--name / --display-name / --description |
服务名 / 显示名 / 描述 |
--start-mode auto\|delayed-auto\|manual\|disabled |
启动类型 |
--credential localsystem\|networkservice\|localservice\|user |
运行账户 |
--username <账户> --password <密码> |
自定义账户(明文注意风险) |
--password-secure |
交互式密码输入(不回显,推荐) |
--failure-restart <毫秒> |
失败自动重启(三段策略) |
--arg <参数> / --arg=-x |
附加服务命令行参数(可重复;等值写法支持以 - 开头的值) |
--start |
安装后立即启动 |
退出码:0 成功 · 1 未知错误 · 2 参数错误 · 3 权限不足 · 4 SCM 操作失败 · 5 服务未安装 · 6 服务已存在 · 7 状态冲突 · 8 服务模式初始化失败。
构建与测试
build\Build.cmd # 还原 + 全 TFM 构建 + 单测(0 警告门禁)
build\build.ps1 -SkipTests # 只构建
build\build.ps1 -RunIntegrationTests # 需管理员:真实 SCM 全链路
build\build.ps1 -Pack # 构建+测试+打包到 artifacts/nuget
build\build.ps1 -Pack -Push [-DryRun] # 发布 NuGet(Key:-NuGetApiKey 或环境变量 AORE_NUGET_API_KEY)
| 门禁 | 内容 |
|---|---|
| 构建 | 6 TFM × 14 工程,TreatWarningsAsErrors(任何警告即失败) |
| 单测 | 92 个(net48 / net8.0 / net10.0) |
| 集成 | 真实 SCM 全链路(管理员 + -RunIntegrationTests) |
NuGet 发布
build\build.ps1 -Pack # artifacts/nuget/*.nupkg + *.snupkg(含图标/README/版权动态年份)
build\build.ps1 -Push # 推送 nuget.org(--NuGetSource 可指向私有源)
版本集中于 Directory.Build.props(SemVer);CHANGELOG 随版本更新;发布后 git tag vX.Y.Z。
AOT / 裁剪说明
net462 ~ net48目标不支持 NativeAOT / PublishTrimmed(框架限制);net8.0+目标仅使用类型化特性查找(裁剪安全)与 P/Invoke,理论兼容PublishAot/PublishTrimmed(未强制验证);裁剪时请保留入口程序集的[assembly: AoreWindowsService]特性(默认保留)。
项目结构
docs/ 完整手册(MANUAL.md)、设计方案、需求 RQ、规范、审计、交接、SOP
src/Aore.WindowsService/ 核心包(Interop/Runtime/Management/Cli/Configuration/Diagnostics)
src/Aore.WindowsService.Hosting/ Hosting 集成包
tests/ 单元测试 + SCM 集成测试 + 测试专用崩溃服务
samples/ 10 个可运行示例(对照上方示例章节)
build/ build.ps1 + Build.cmd
文档导航
| 文档 | 说明 |
|---|---|
| docs/MANUAL.md | 完整文档手册:安装 / 全部 API / 10 类服务搭建场景全代码 |
| docs/sop/QUICK-START-SOP.md | 10 分钟快速上手 |
| docs/sop/USAGE-SOP.md | 详细使用:CLI/凭据/失败恢复/排障手册 |
| docs/sop/DEVELOPMENT-SOP.md | 本项目开发与发布 SOP |
| docs/design/PROJECT-DESIGN.md | 全局详细设计方案(含决策记录) |
| docs/audit/CODE-AUDIT-LOG.md | 代码安全与质量审计日志(22 项全部已修复) |
| docs/HANDOVER.md | AI 会话交接文档 |
| CHANGELOG.md | 版本历史 |
许可证
MIT —— Copyright © 2026-2026 WEI.ZHOU (Willis). All rights reserved.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 is compatible. net481 was computed. |
-
.NETFramework 4.6.2
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.1)
-
.NETFramework 4.7.2
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.1)
-
.NETFramework 4.8
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.1)
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.1)
-
net8.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.1)
-
net9.0
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Aore.WindowsService:
| Package | Downloads |
|---|---|
|
Aore.WindowsService.Hosting
Aore.WindowsService 的 Microsoft.Extensions.Hosting 集成包:IHostLifetime 生命周期桥接,与服务控制管理器(SCM)无缝组合。 |
GitHub repositories
This package is not used by any popular GitHub repositories.