为什么 .NET 通用主机与 WinUI3 一起使用时不会停止?

问题描述

我正在使用 .NET 5 编写 WinUI3(Project Reunion 0.5)应用程序,并想使用 .NET 通用主机。我使用带有自定义 IHostedService认主机:

public App() {
    _host = Host.CreateDefaultBuilder()
        .ConfigureServices((context,services) =>
        {
            services.AddHostedService<MyHostedService>();
        }).Build();
    InitializeComponent();
}

托管服务在 StopAsync 中执行一些异步操作。出于演示目的,假设它延迟了 1 秒(此代码仍然会产生问题):

public override async Task StopAsync(CancellationToken cancellationToken)
{
    await Task.Delay(1000);
}

我在 OnLaunched 启动主机:

protected override async void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
    await _host.StartAsync();
    m_window = new MainWindow();
    m_window.Activate();
}

我让认的 ConsoleLifetime 实现在进程退出之前停止主机。

我的 Task 实现返回的 IHostedService.StopAsync 已完成,但 IHost.StopAsync 永远不会返回并且进程在输出中挂起此消息:

Microsoft.Hosting.Lifetime: information: Application is shutting down...
Microsoft.Hosting.Lifetime: information: Waiting for the host to be disposed. Ensure all 'IHost' instances are wrapped in 'using' blocks.

如果我单步调试调试器,有时 IHost.StopAsync 方法会超时并抛出异常。这永远不会发生在调试器之外。我曾尝试在 MainWindow 关闭时明确停止和处置主机,但没有任何区别。

我认为 dispatcherQueueSynchronizationContext 可能在主机停止并且任务没有得到服务之前被关闭,但 dispatcherQueue.ShutdownStarting 事件从未被触发。

还有其他想法吗?

解决方法

我从评论中接受了@Dai 的建议,并调查了在单独的线程上运行 WinUI 并在主线程上运行主机。

我创建了一个 IHostedService 来管理 WinUI 应用程序:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.System;
using Microsoft.UI.Xaml;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyApp.Hosting
{

public class WinUIHostedService<TApplication> : IHostedService,IDisposable
    where TApplication : Application,new()
{
    private readonly IHostApplicationLifetime HostApplicationLifetime;
    private readonly IServiceProvider ServiceProvider;

    public WinUIHostedService(
        IHostApplicationLifetime hostApplicationLifetime,IServiceProvider serviceProvider)
    {
        HostApplicationLifetime = hostApplicationLifetime;
        ServiceProvider = serviceProvider;
    }

    public void Dispose()
    {
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        var thread = new Thread(Main);
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    private void Main()
    {
        WinRT.ComWrappersSupport.InitializeComWrappers();
        Application.Start((p) => {
            var context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
            SynchronizationContext.SetSynchronizationContext(context);
            new TApplication();
        });
        HostApplicationLifetime.StopApplication();
    }
}

}

我在构建设置中定义了 DISABLE_XAML_GENERATED_MAIN 并添加了我自己的 Main

public class Program
{
    public static void Main(string[] args)
    {
        Host.CreateDefaultBuilder()
            .ConfigureServices(services =>
            {
                services.AddHostedService<WinUIHostedService<App>>();
            })
            .Build().Run();
    }
}

瞧!即使 IHostedService.StopAsync 运行异步代码,WinUI 应用程序仍然可以正常运行并且主机在主窗口关闭时完全停止。

请注意,此代码只是第一个起作用的东西。它可能会得到改进,我不完全理解通用主机生命周期语义。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...