Files
2026-10-27 14:35:03 +08:00

151 lines
5.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<Exception>? FatalErrorRaised;
protected FrameworkApplication()
{
DispatcherUnhandledException += OnDispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += OnDomainUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
}
/// <summary>返回 null 可允许多开;默认限制同一登录会话中的应用实例。</summary>
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<Controls.DialogWindow>();
}
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();
}
/// <summary>先同步保存报告,再通知订阅者并关闭应用。</summary>
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<Exception> 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);
}
/// <summary>通过 WPF 正常退出,执行退出清理。</summary>
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(); }
}
}
}
}