2

我正在使用 .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事件从未被触发。

还有其他想法吗?

4

1 回答 1

2

我从评论中听取了@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();
    }
}

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

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

于 2021-06-03T18:37:30.590 回答