-
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathProgram.cs
More file actions
119 lines (105 loc) · 4.07 KB
/
Copy pathProgram.cs
File metadata and controls
119 lines (105 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
using System.Diagnostics;
using NMSE.Config;
using NMSE.Core;
using NMSE.Data;
using NMSE.UI;
namespace NMSE;
static class Program
{
/// <summary>
/// The main entry point for the NMS Save Editor application.
/// </summary>
[STAThread]
static void Main()
{
// Remove the stale .old executable left by a previous self-update.
UpdateService.CleanupOldExeIfPresent();
// Initialise the UI string tables early so crash dialogs are localised
// even when a failure happens before the main form finishes loading.
InitializeUiStrings();
// Wire up global exception handlers for crash logging
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += OnThreadException;
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
// Opt in to per-monitor DPI awareness so WinForms scales controls,
// fonts, and images proportionally when the display scale factor changes.
// Done as best effort - if not supported, default and silently degrade to default DPI.
try
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
}
catch
{
// Not supported in compatibility layer or env
}
ApplicationConfiguration.Initialize();
// Show a lightweight splash screen so the user gets immediate
// visual feedback while the main form performs heavy initialisation
// (database loading, icon preloading, panel construction).
using var splash = new SplashForm();
splash.Show();
Application.DoEvents(); // Process WM_SETICON and taskbar updates before heavy startup.
splash.Refresh();
var mainForm = new MainFormResources();
mainForm.SetSplash(splash);
mainForm.PerformStartup();
Application.Run(mainForm);
}
private static void OnThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
LogCrash(e.Exception);
MessageBox.Show(
UiStrings.Format("dialog.crash_message", e.Exception.Message),
UiStrings.Get("dialog.crash_title"),
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
/// <summary>
/// Loads the UI string tables using the language saved in the configuration,
/// so early startup failures (including the crash dialog) are localised.
/// </summary>
private static void InitializeUiStrings()
{
try
{
string uiLangDir = Path.Combine(AppContext.BaseDirectory, "Resources", "ui", "lang");
UiStrings.SetDirectory(uiLangDir);
var config = AppConfig.Instance;
config.Initialize();
UiStrings.Load(config.Language);
}
catch (Exception ex)
{
Debug.WriteLine($"UI string initialisation failed: {ex.Message}");
}
}
private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception ex)
LogCrash(ex);
}
private static void LogCrash(Exception ex)
{
try
{
string logPath = Path.Combine(AppContext.BaseDirectory, "crash.log");
string entry = $"""
=== NMSE Crash Report ===
Time (UTC): {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}
Time (Local): {DateTime.Now:yyyy-MM-dd HH:mm:ss}
Exception: {ex.GetType().FullName}
Message: {ex.Message}
Stack Trace:
{ex.StackTrace}
{(ex.InnerException != null ? $"\nInner Exception: {ex.InnerException.GetType().FullName}\nMessage: {ex.InnerException.Message}\nStack Trace:\n{ex.InnerException.StackTrace}" : "")}
============================
""";
File.AppendAllText(logPath, entry);
}
catch
{
// Last resort - can't even log the crash
Debug.WriteLine($"Failed to write crash log: {ex}");
}
}
}