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

Posted

技术标签:

【中文标题】为啥 .NET 通用主机在与 WinUI3 一起使用时不会停止?【英文标题】:Why doesn't the .NET Generic Host stop when used with WinUI3?为什么 .NET 通用主机在与 WinUI3 一起使用时不会停止? 【发布时间】:2021-08-21 19:22:39 【问题描述】:

我正在使用 .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 实现在进程退出之前停止主机。

我的IHostedService.StopAsync 实现返回的Task 完成,但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 事件从未被触发。

还有其他想法吗?

【问题讨论】:

您是否在使用具有确定性垃圾收集功能的编程环境(如 C++)时遇到同样的问题? App() 在哪里定义?您使用的是 WPF、UWP XAML 还是其他一些 XAML 框架? 你真的不应该在App.xaml的构造函数中设置你的IHost,因为它隐藏了很多设置发生在你程序的其他地方的事实,这可能是导致问题的原因你有。相反,您需要定义自己的Main 并设置IHost before WPF、UWP、Jupiter、whatever-Microsoft-is-calling-their-latest-XAML-environment-this -season 开始。 @IInspectable C++ 不是“确定性垃圾收集”语言:real C++ 中根本没有垃圾收集。即使是这样,C++ 也不能用于托管 IHost,因为这需要 CLR。 (并且编译 C++/CLI 不会神奇地让您在“真正的”C++ 中使用 CLR 类型)。 @dai 你确定你了解垃圾收集是什么?原因everybody thinks about garbage collection the wrong way. 【参考方案1】:

我听取了 cmets 的 @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 应用程序仍然可以正常运行并且主机在主窗口关闭时完全停止。

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

【讨论】:

感谢您这样做。我真的很喜欢使用托管管道,遵循这种模式非常有意义。希望有一些官方指南或扩展。由于 Maui 正在使用 Hosting/StartUp 模式做类似的事情

以上是关于为啥 .NET 通用主机在与 WinUI3 一起使用时不会停止?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 LLVM 的泄漏消毒剂在与其他启用的消毒剂一起使用时不起作用

.Net Core中的通用主机——系统配置

WinUI 3.0 - 为啥 UWP 项目要求 MS Edge for Business 用于 WebView2

为啥.net网站访问出错 provider: TCP 提供程序, error: 0 -

为啥注入的任何 DLL 都会使主机进程崩溃?

在没有前缀的通用主机中读取环境变量(.NET)