using MAX.Framework.Logger; using NLog; using Prism.Autofac; using Prism.Ioc; using Prism.Services.Dialogs; using Store.Management.Framework.Configs; using Store.Management.Framework.Extensions; using Store.Management.Framework.Services; using System.Threading; using System.Windows; using System.Windows.Threading; namespace Store.Management.Framework; public abstract class FrameworkApplication : PrismApplication { private Mutex? _mutex; private bool _ownsMutex; private int _fatalHandling; private int _shutdownRequested; public static bool ShuttingDown { get; private set; } public static bool IsAppInitialized { get; private set; } public static event Action? FatalErrorRaised; protected FrameworkApplication() { DispatcherUnhandledException += OnDispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += OnDomainUnhandledException; TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; } /// 返回 null 可允许多开;默认限制同一登录会话中的应用实例。 public virtual string? GetMutexName() => @"Local\Store.Management"; protected override void OnStartup(StartupEventArgs e) { try { //string? name = GetMutexName(); //if (!string.IsNullOrWhiteSpace(name)) //{ // _mutex = new Mutex(true, name, out _ownsMutex); // if (!_ownsMutex) // { // MessageBox.Show("程序已在运行。", Config.AppName, MessageBoxButton.OK, MessageBoxImage.Information); // Shutdown(); // return; // } //} FolderConfig.Initialize(); base.OnStartup(e); ApplicationLogService.Initialize(); NLogger.Instance.Info($"软件启动:{Config.AppFullName},版本:{Config.GetFullVersionString()},会话:{Config.SessionID},启动时间:{Config.AppStartTime:O},Windows:{Config.WindowsVersion}"); DISource.Resolver = Resolve; } catch (Exception ex) { Fatal(ex); } } protected override void OnInitialized() { base.OnInitialized(); IsAppInitialized = true; NLogger.Instance.Info("软件初始化成功,进入主页面"); } protected override void RegisterRequiredTypes(IContainerRegistry registry) { base.RegisterRequiredTypes(registry); registry.RegisterDialogWindow(); } private object Resolve(Type type, string name) { if (type == null || Container is null) return null!; return name != null ? Container.Resolve(type, name) : Container.Resolve(type); } private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { e.Handled = true; Fatal(e.Exception); } private void OnDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) { Exception exception = e.ExceptionObject as Exception ?? new Exception(e.ExceptionObject?.ToString()); // CLR 终止期间同步记录,不等待 UI 线程。 if (e.IsTerminating) ApplicationLogService.WriteFatal(exception, "AppDomain.UnhandledException"); else Fatal(exception); } private void OnUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) { ApplicationLogService.WriteFatal(e.Exception, "TaskScheduler.UnobservedTaskException"); e.SetObserved(); } /// 先同步保存报告,再通知订阅者并关闭应用。 public static void Fatal(Exception exception) { ArgumentNullException.ThrowIfNull(exception); string? path = ApplicationLogService.WriteFatal(exception, "Fatal"); if (Current is not FrameworkApplication app || Interlocked.Exchange(ref app._fatalHandling, 1) != 0) return; foreach (Action handler in FatalErrorRaised?.GetInvocationList() ?? []) { try { handler(exception); } catch (Exception ex) { ApplicationLogService.WriteFatal(ex, "FatalErrorRaised"); } } void ShowErrorAndShutdown() { try { MessageBox.Show($"软件发生致命错误,即将关闭。\n{exception.Message}\n\n日志:{path ?? "写入失败,请查看调试输出"}", Config.AppName, MessageBoxButton.OK, MessageBoxImage.Error); } finally { TotalShutdown(-1); } } if (app.Dispatcher.CheckAccess()) ShowErrorAndShutdown(); else if (!app.Dispatcher.HasShutdownStarted) app.Dispatcher.BeginInvoke((Action)ShowErrorAndShutdown); } /// 通过 WPF 正常退出,执行退出清理。 public static void TotalShutdown(int exitCode = 0) { if (Current is not FrameworkApplication app || Interlocked.Exchange(ref app._shutdownRequested, 1) != 0) return; ShuttingDown = true; if (app.Dispatcher.CheckAccess()) app.Shutdown(exitCode); else if (!app.Dispatcher.HasShutdownStarted) app.Dispatcher.BeginInvoke((Action)(() => app.Shutdown(exitCode))); } protected override void OnExit(ExitEventArgs e) { ShuttingDown = true; try { base.OnExit(e); } finally { DispatcherUnhandledException -= OnDispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException -= OnDomainUnhandledException; TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException; DISource.Resolver = null!; try { if (_ownsMutex) _mutex?.ReleaseMutex(); } finally { _mutex?.Dispose(); try { NLogger.Instance.Info($"软件关闭,退出码:{e.ApplicationExitCode}"); } finally { LogManager.Shutdown(); } } } } }