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

WinUI3 C#:主窗口关闭后如何保持后台线程(本地服务器)运行?

在WinUI3中实现主UI关闭后保持服务器线程运行的解决方案

问题核心在于:WinUI3应用默认会在最后一个窗口关闭时终止整个进程,无论线程是后台还是前台都会被强制终止。要实现需求需要两步关键操作:阻止窗口关闭触发进程退出,让服务器线程成为前台线程维持进程活跃。

步骤1:修改应用退出行为,阻止窗口关闭终止进程

在App.xaml.cs中配置应用仅在显式调用退出时才终止,同时拦截主窗口的关闭事件改为隐藏窗口:

using Microsoft.UI.Xaml;

namespace YourAppNamespace;

public partial class App : Application
{
    private Window? _mainWindow;

    public App()
    {
        InitializeComponent();
        // 设置仅显式调用Exit()才会终止应用
        this.SetExitMode(ApplicationExitMode.Explicit);
    }

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        _mainWindow = new MainWindow();
        _mainWindow.Closed += MainWindow_Closed;
        _mainWindow.Activate();
    }

    private void MainWindow_Closed(object sender, WindowEventArgs e)
    {
        // 阻止窗口关闭触发应用退出,改为隐藏窗口
        e.Handled = true;
        _mainWindow?.Hide();
    }
}

步骤2:创建前台服务器线程并实现优雅启停

将服务器线程设置为前台线程(IsBackground = false),这样只要该线程在运行,进程就会保持活跃。同时添加取消令牌实现优雅终止:

using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

// 在MainWindow或对应业务类中实现
private Thread? _serverThread;
private CancellationTokenSource? _serverCts;

// 启动服务器的方法
public void StartLocalServer()
{
    _serverCts = new CancellationTokenSource();
    _serverThread = new Thread(() => RunServerLoop(_serverCts.Token))
    {
        IsBackground = false, // 前台线程,维持进程存活
        Name = "LocalSocketServer"
    };
    _serverThread.Start();
}

private void RunServerLoop(CancellationToken cancellationToken)
{
    var listener = new TcpListener(IPAddress.Loopback, 8080);
    listener.Start();

    try
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            // 用Pending()+睡眠避免AcceptTcpClient阻塞导致无法终止
            if (listener.Pending())
            {
                var client = listener.AcceptTcpClient();
                // 用线程池处理客户端连接,避免阻塞服务器主线程
                ThreadPool.QueueUserWorkItem(HandleClientConnection, client);
            }
            else
            {
                Thread.Sleep(100);
            }
        }
    }
    catch (SocketException ex)
    {
        // 排除主动终止时的中断异常
        if (ex.SocketErrorCode != SocketError.Interrupted && !cancellationToken.IsCancellationRequested)
        {
            // 这里添加异常日志或处理逻辑
        }
    }
    finally
    {
        listener.Stop();
    }
}

private void HandleClientConnection(object? clientObj)
{
    if (clientObj is not TcpClient client) return;
    
    // 这里实现具体的Socket通信逻辑
    // 示例:读取客户端数据并返回
    using var stream = client.GetStream();
    var buffer = new byte[1024];
    var bytesRead = stream.Read(buffer, 0, buffer.Length);
    // ... 处理数据逻辑
    client.Close();
}

// 主动退出应用时调用(比如系统托盘的退出按钮)
public void ShutdownServerAndExit()
{
    // 取消服务器循环
    _serverCts?.Cancel();
    // 等待线程优雅终止(最多1秒)
    _serverThread?.Join(1000);
    // 显式终止应用进程
    Application.Current.Exit();
}

关键注意事项

  • 必须提供手动退出入口:因为主窗口关闭只是隐藏,进程仍在后台运行,需要给用户提供退出方式(比如添加系统托盘图标,或在隐藏窗口的右键菜单添加退出选项),调用ShutdownServerAndExit()彻底终止进程。
  • 优雅终止线程:使用CancellationToken控制服务器循环,避免强制终止导致Socket资源泄漏。
  • 客户端连接处理:用线程池处理每个客户端连接,防止服务器主线程被阻塞。

内容的提问来源于stack exchange,提问作者Abdullah Omer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 23:01:03