You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET 8 WebAPI中捕获Windows程序关闭事件及处理服务终止

解决.NET 8自包含Windows WebAPI捕获窗口/工具栏关闭事件的问题

核心问题分析

你遇到的问题本质是:ApplicationStopping仅响应WebHost主动发起的关闭信号(比如Ctrl+C),而Windows窗口关闭/工具栏右键关闭是操作系统向控制台进程发送的控制台控制事件,默认不会触发WebApplication的生命周期事件;ProcessExit触发时CLR已进入终止流程,大部分对象已被回收,无法正常操作;析构函数(终结器)不可靠,因为进程退出时CLR可能跳过终结器执行。

推荐解决方案

方案1:用IHostedService托管你的DLL服务(优先推荐)

.NET的Host系统会统一管理服务生命周期,无论是Ctrl+C还是Windows关闭事件,Host都会触发优雅关闭流程,调用所有IHostedService的StopAsync方法,此时服务对象完全可用。

步骤1:包装你的DLL服务为IHostedService

public class YourDllServiceHost : IHostedService
{
    private readonly YourDllService _targetService;

    // 通过依赖注入获取你的DLL服务实例
    public YourDllServiceHost(YourDllService targetService)
    {
        _targetService = targetService;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // 启动你的DLL服务(如果需要)
        _targetService.Initialize();
        return Task.CompletedTask;
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        // 在这里执行你需要的终止操作,Host会在应用关闭时自动调用
        await _targetService.ExecuteCleanupOperationsAsync();
        // 如果是同步操作,直接执行即可:_targetService.ExecuteCleanup();
    }
}

步骤2:在Program.cs中注册服务

var builder = WebApplication.CreateBuilder(args);

// 注册你的DLL服务为单例(根据实际生命周期调整)
builder.Services.AddSingleton<YourDllService>();
// 注册托管服务,让Host管理生命周期
builder.Services.AddHostedService<YourDllServiceHost>();

// ... 配置WebAPI路由、中间件等

var app = builder.Build();
app.Run();

方案2:注册控制台关闭事件处理程序(补充方案)

如果无法修改服务的注册方式,可以通过P/Invoke直接捕获Windows的控制台关闭事件,在事件触发时主动调用清理逻辑。

实现控制台关闭事件处理

using System.Runtime.InteropServices;

public static class ConsoleShutdownHandler
{
    private delegate bool ConsoleCtrlHandler(CtrlTypes ctrlType);

    private enum CtrlTypes
    {
        CTRL_C_EVENT = 0,
        CTRL_BREAK_EVENT = 1,
        CTRL_CLOSE_EVENT = 2, // 对应窗口关闭/工具栏关闭操作
        CTRL_LOGOFF_EVENT = 5,
        CTRL_SHUTDOWN_EVENT = 6
    }

    [DllImport("Kernel32.dll")]
    private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandler handler, bool add);

    private static IHost? _host;

    public static void Register(IHost host)
    {
        _host = host;
        SetConsoleCtrlHandler(OnConsoleClose, true);
    }

    private static bool OnConsoleClose(CtrlTypes ctrlType)
    {
        if (ctrlType == CtrlTypes.CTRL_CLOSE_EVENT && _host != null)
        {
            // 获取你的DLL服务实例并执行清理
            var service = _host.Services.GetRequiredService<YourDllService>();
            service.ExecuteCleanup();

            // 触发Host的优雅关闭(可选,确保WebAPI正常停止)
            _host.StopAsync().Wait();
        }
        return true;
    }
}

在Program.cs中注册处理程序

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<YourDllService>();

var app = builder.Build();
// 注册控制台关闭事件处理
ConsoleShutdownHandler.Register(app.Services.GetRequiredService<IHost>());

// ... 配置WebAPI中间件
app.Run();

关键注意事项

  1. 不要依赖析构函数:进程退出时CLR不会等待终结器队列执行,析构函数大概率不会触发,所有清理逻辑必须放在显式调用的方法中。
  2. 优雅关闭超时:可以通过HostOptions.ShutdownTimeout配置关闭超时时间,确保清理操作有足够时间完成:
    builder.Services.Configure<HostOptions>(options =>
    {
        options.ShutdownTimeout = TimeSpan.FromSeconds(10);
    });
    

内容的提问来源于stack exchange,提问作者Po-Sen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:27:41