Aore.WindowsService 0.1.3

dotnet add package Aore.WindowsService --version 0.1.3
                    
NuGet\Install-Package Aore.WindowsService -Version 0.1.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Aore.WindowsService" Version="0.1.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Aore.WindowsService" Version="0.1.3" />
                    
Directory.Packages.props
<PackageReference Include="Aore.WindowsService" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Aore.WindowsService --version 0.1.3
                    
#r "nuget: Aore.WindowsService, 0.1.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Aore.WindowsService@0.1.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Aore.WindowsService&version=0.1.3
                    
Install as a Cake Addin
#tool nuget:?package=Aore.WindowsService&version=0.1.3
                    
Install as a Cake Tool

Aore.WindowsService

Version License: MIT TFM Build

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


目录


支持的目标框架

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)。两个集成关键点(缺一不可):

  1. 内容根校正:SCM 启动服务时工作目录是 System32,而 ASP.NET Core 默认以"当前目录"作为内容根——必须在构建 Host 前校正,否则 appsettings/wwwroot 全部失效;
  2. 先分发管理动词: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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.

Version Downloads Last Updated
0.1.3 43 9/25/2026
0.1.2 55 9/25/2026 0.1.2 is deprecated because it is no longer maintained.