using System.IO; using System.Text; using System.Windows; using System.Windows.Threading; using AxiOmron.PcbCheck.Options; using AxiOmron.PcbCheck.Services.Implementations; using AxiOmron.PcbCheck.Services.Interfaces; using AxiOmron.PcbCheck.Utils; using AxiOmron.PcbCheck.ViewModels; using AxiOmron.PcbCheck.Views.Pages; using AxiOmron.PcbCheck.Views.Windows; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using NLog.Extensions.Logging; namespace AxiOmron.PcbCheck; /// /// 表示 WPF 应用入口,负责 Host/DI、配置和日志初始化。 /// public partial class App : Application { private static readonly NLog.Logger FallbackLogger = NLog.LogManager.GetCurrentClassLogger(); private IHost? _host; /// /// 获取当前应用服务容器。 /// public static IServiceProvider Services { get; private set; } = null!; /// /// 应用启动入口。 /// /// 启动事件参数。 protected override async void OnStartup(StartupEventArgs e) { base.OnStartup(e); RegisterGlobalExceptionHandlers(); try { Console.OutputEncoding = Encoding.UTF8; } catch (IOException) { // WinExe 在没有控制台句柄时忽略 } try { _host = BuildHost(); await _host.StartAsync().ConfigureAwait(true); Services = _host.Services; var mainWindow = Services.GetRequiredService(); MainWindow = mainWindow; mainWindow.Show(); } catch (Exception ex) { LogGlobalException("应用启动失败", ex); MessageBox.Show($"应用启动失败: {ex.Message}", "Axi Omron PCB Check", MessageBoxButton.OK, MessageBoxImage.Error); Shutdown(-1); } } /// /// 应用退出入口。 /// /// 退出事件参数。 protected override async void OnExit(ExitEventArgs e) { UnregisterGlobalExceptionHandlers(); if (_host is not null) { try { await ShutdownHelper.StopHostAsync(_host, TimeSpan.FromSeconds(2)); } finally { _host.Dispose(); } } base.OnExit(e); } /// /// 构建应用 Host 与依赖注入容器。 /// /// 已构建的 Host 实例。 private static IHost BuildHost() { return Host.CreateDefaultBuilder() .ConfigureAppConfiguration((context, configurationBuilder) => { configurationBuilder.SetBasePath(AppContext.BaseDirectory); configurationBuilder.AddJsonFile("appConfig.json", optional: true, reloadOnChange: false); configurationBuilder.AddJsonFile($"appConfig.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: false); }) .ConfigureLogging(logging => { logging.ClearProviders(); logging.SetMinimumLevel(LogLevel.Information); logging.AddNLog(); }) .ConfigureServices((context, services) => { var appConfig = new AppConfig(); context.Configuration.Bind(appConfig); services.AddSingleton(context.Configuration); services.AddSingleton(); services.AddSingleton(appConfig); services.AddSingleton(); services.AddSingleton(typeof(IAppLogger<>), typeof(AppLogger<>)); services.AddSingleton(); services.AddSingleton(TimeProvider.System); services.AddHttpClient(nameof(AndonService)); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(sp => sp.GetRequiredService()); services.AddHostedService(sp => sp.GetRequiredService()); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddTransient(); services.AddSingleton(); }) .Build(); } /// /// 注册全局异常处理器,用于兜底记录 UI 线程、未观察任务和域级异常。 /// private void RegisterGlobalExceptionHandlers() { DispatcherUnhandledException += OnDispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainUnhandledException; TaskScheduler.UnobservedTaskException += OnTaskSchedulerUnobservedTaskException; } /// /// 注销全局异常处理器。 /// private void UnregisterGlobalExceptionHandlers() { DispatcherUnhandledException -= OnDispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException -= OnCurrentDomainUnhandledException; TaskScheduler.UnobservedTaskException -= OnTaskSchedulerUnobservedTaskException; } /// /// 处理 UI 线程未捕获异常,记录日志并阻止应用直接闪退。 /// /// 事件源。 /// 异常事件参数。 private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { LogGlobalException("UI 线程未处理异常", e.Exception); MessageBox.Show($"程序捕获到未处理异常,已写入日志:{e.Exception.Message}", "Axi Omron PCB Check", MessageBoxButton.OK, MessageBoxImage.Warning); e.Handled = true; } /// /// 处理未观察任务异常,记录日志并标记为已观察。 /// /// 事件源。 /// 异常事件参数。 private void OnTaskSchedulerUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { LogGlobalException("未观察任务异常", e.Exception); e.SetObserved(); } /// /// 处理应用程序域未捕获异常,尽量在进程退出前补充日志。 /// /// 事件源。 /// 异常事件参数。 private void OnCurrentDomainUnhandledException(object? sender, UnhandledExceptionEventArgs e) { var exception = e.ExceptionObject as Exception ?? new Exception(e.ExceptionObject?.ToString() ?? "未知未处理异常"); LogGlobalException($"应用程序域未处理异常(IsTerminating={e.IsTerminating})", exception); } /// /// 统一记录全局异常日志,优先复用宿主日志器,失败时退回 NLog 直接写入。 /// /// 日志消息。 /// 异常对象。 private static void LogGlobalException(string message, Exception exception) { try { if (Services is not null) { var logger = Services.GetService>(); logger?.LogError(exception, message); } } catch { // 忽略宿主日志失败,继续使用回退日志。 } try { FallbackLogger.Error(exception, message); } catch { // 全局兜底日志不再向外抛出。 } } }