增加MessageBoxService服务

This commit is contained in:
chfei committed 2026-11-13 10:16:43 +08:00
1 parent 3dcb6a1c34
commit 30a6a5ea24
15 files changed
+517 -153

No files matched your search

@@ -1,3 +1,4 @@
using MAX.Common.Helpers;
using MAX.Framework.Logger;
using NLog;
using Prism.Autofac;
@@ -15,141 +16,155 @@ 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;
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;
private IStatusBarService StatusBarService;
private IMessageBoxService MessageBoxService;
private static bool FatalShutdown = false;
protected FrameworkApplication()
{
DispatcherUnhandledException += OnDispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += OnDomainUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
}
protected FrameworkApplication()
{
DispatcherUnhandledException += OnDispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += OnDomainUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
}
/// <summary>返回 null 可允许多开;默认限制同一登录会话中的应用实例。</summary>
public virtual string? GetMutexName() => @"Local\Store.Management";
/// <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 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("软件初始化成功,进入主页面");
StatusBarService = Container?.Resolve<IStatusBarService>();
MessageBoxService = Container?.Resolve<IMessageBoxService>();
}
protected override void OnInitialized()
{
base.OnInitialized();
IsAppInitialized = true;
NLogger.Instance.Info("软件初始化成功,进入主页面");
}
protected override void RegisterRequiredTypes(IContainerRegistry registry)
{
base.RegisterRequiredTypes(registry);
registry.RegisterDialogWindow<Controls.DialogWindow>();
}
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 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;
StatusBarService.Report(e.Exception.Message, StatusLevel.Error);
Fatal(e.Exception);
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 OnDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception exception = e.ExceptionObject as Exception ?? new Exception(e.ExceptionObject?.ToString());
StatusBarService.Report(exception.Message, StatusLevel.Error);
// 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();
}
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>先同步保存报告,再通知订阅者并关闭应用。</summary>
public 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()
{
if (FatalShutdown)
{
try
{
MessageBox.Show($"软件发生致命错误,即将关闭。\n{exception.Message}\n\n日志:{path ?? "写入失败,请查看调试输出"}",
Config.AppName, MessageBoxButton.OK, MessageBoxImage.Error);
}
finally { TotalShutdown(-1); }
}
else
{
MessageBoxService.ShowError(exception.Message);
}
}
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)));
}
/// <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
{
try { Container?.Resolve<StoreApiClient>().Dispose(); }
finally { 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(); }
}
}
}
protected override void OnExit(ExitEventArgs e)
{
ShuttingDown = true;
try
{
try { Container?.Resolve<StoreApiClient>().Dispose(); }
finally { 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(); }
}
}
}
}