-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
258 lines (237 loc) · 11.4 KB
/
Copy pathProgram.cs
File metadata and controls
258 lines (237 loc) · 11.4 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
* File: Program.cs
* Project: Planetoid-DB
* Namespace: Planetoid_DB
* Description: Provides the main entry point and core initialization logic for the application.
*
* Author: Michael Johne
* Company: Mijo Software
*
* Copyright (c) 2026 Michael Johne
*
* Licensed under the GNU General Public License v3.0.
* See LICENSE file in the project root for license information.
*/
using Krypton.Toolkit;
using NLog;
using NLog.Config;
using Planetoid_DB.Helpers;
using Planetoid_DB.Properties;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
namespace Planetoid_DB;
/// <summary>Provides the main entry point and core initialization logic for the application.</summary>
/// <remarks>This class is responsible for starting the application, configuring essential features, handling startup errors, and ensuring proper shutdown of resources. It cannot be instantiated and contains only static members.</remarks>
internal static class Program
{
/// <summary>NLog logger instance for logging application events.</summary>
/// <remarks>This logger is used throughout the application to log important events and errors.</remarks>
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>Feature ID for disabling navigation sounds.</summary>
/// <remarks>This feature ID is used to disable navigation sounds in the application.</remarks>
private const int FeatureDisableNavigationSounds = 21;
/// <summary>Feature ID for setting the feature on the current thread.</summary>
/// <remarks>This feature ID is used to set the feature on the current thread.</remarks>
private const int SetFeatureOnThread = 0x00000001;
/// <summary>Feature ID for setting the feature on the current process.</summary>
/// <remarks>This feature ID is used to set the feature on the current process.</remarks>
private const int SetFeatureOnProcess = 0x00000002;
/// <summary>Feature ID for setting the feature in the registry.</summary>
/// <remarks>This feature ID is used to set the feature in the registry.</remarks>
private const int SetFeatureInRegistry = 0x00000004;
/// <summary>Feature ID for setting the feature on the current thread for local machine.</summary>
/// <remarks>This feature ID is used to set the feature on the current thread for local machine.</remarks>
private const int SetFeatureOnThreadLocalMachine = 0x00000008;
/// <summary>Feature ID for setting the feature on the current thread for intranet.</summary>
/// <remarks>This feature ID is used to set the feature on the current thread for intranet.</remarks>
private const int SetFeatureOnThreadIntranet = 0x00000010;
/// <summary>Feature ID for setting the feature on the current thread for trusted sites.</summary>
/// <remarks>This feature ID is used to set the feature on the current thread for trusted sites.</remarks>
private const int SetFeatureOnThreadTrusted = 0x00000020;
/// <summary>Feature ID for setting the feature on the current thread for internet.</summary>
/// <remarks>This feature ID is used to set the feature on the current thread for internet.</remarks>
private const int SetFeatureOnThreadInternet = 0x00000040;
/// <summary>Feature ID for setting the feature on the current thread for restricted sites.</summary>
/// <remarks>This feature ID is used to set the feature on the current thread for restricted sites.</remarks>
private const int SetFeatureOnThreadRestricted = 0x00000080;
/// <summary>Disables navigation sounds.</summary>
/// <param name="featureEntry">The feature ID.</param>
/// <param name="dwFlags">The flags.</param>
/// <param name="fEnable">Specifies whether the feature should be enabled or disabled.</param>
/// <returns>An HRESULT value indicating success or failure.</returns>
/// <remarks>This method sets the specified feature for the current process.</remarks>
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[DllImport(dllName: "urlmon.dll")]
[PreserveSig]
[return: MarshalAs(unmanagedType: UnmanagedType.Error)]
private static extern int CoInternetSetFeatureEnabled(int featureEntry, [MarshalAs(unmanagedType: UnmanagedType.U4)] int dwFlags, bool fEnable);
/// <summary>The main entry point for the application.</summary>
/// <param name="args">Command line arguments. If provided, the first argument is treated as the path to a custom MPCORB.DAT file.</param>
/// <remarks>This method is responsible for starting the application and initializing the main form.</remarks>
[STAThread]
private static void Main(string[] args)
{
// Try to set the application to use the default font settings
try
{
// Configure NLog to capture log events for the LogViewerForm — must be first
// so that no log statement issued before this call is silently dropped.
ConfigureLogEventStore();
// Restore log events that were persisted during the previous session.
Task.Run(LogEventStore.LoadAsync).GetAwaiter().GetResult();
// Disable navigation sounds
DisableNavigationSounds();
// Initialize the application configuration
ApplicationConfiguration.Initialize();
// Check if a custom MPCORB.DAT file path was provided as a command line argument
string mpcorbFilePath = Settings.Default.systemFilenameMpcorbDat;
if (args.Length > 0 && !string.IsNullOrWhiteSpace(value: args[0]))
{
// Trim any surrounding quotes and whitespace from the provided path
string customPath = args[0].Trim(trimChars: ['"', ' ']);
// Check if the file exists at the provided path
if (File.Exists(path: customPath))
{
// If the file exists, use the custom path and log the information
mpcorbFilePath = customPath;
logger.Info(message: $"Using custom MPCORB.DAT file from command line: {mpcorbFilePath}");
}
// If the file does not exist, log a warning and continue using the default path
else
{
logger.Warn(message: $"Custom MPCORB.DAT file not found: {customPath}. Using default path.");
}
}
// Check if the MPCORB.DAT file exists at the determined path
if (!File.Exists(path: mpcorbFilePath))
{
// Show the PreLoadForm if the file is missing
HandleMissingFile();
}
// If the file exists, start the main form with the specified MPCORB.DAT file path
else
{
// Start the main form with the specified MPCORB.DAT file path
using PlanetoidDbForm mainForm = new(mpcorbDatFilePath: mpcorbFilePath);
Application.Run(mainForm: mainForm);
//Application.Run(mainForm: new Forms.TestForm());
}
}
// Catch specific exceptions and handle them accordingly
catch (UnauthorizedAccessException ex)
{
// Log the error and show a message box
logger.Error(exception: ex, message: "Access denied");
ShowErrorMessage(message: $"Access denied: {ex.Message}");
// Exit the application with a non-zero exit code
Environment.Exit(exitCode: 1);
}
catch (FileNotFoundException ex)
{
// Log the error and show a message box
logger.Error(exception: ex, message: "File not found");
ShowErrorMessage(message: $"File not found: {ex.Message}");
// Exit the application with a non-zero exit code
Environment.Exit(exitCode: 1);
}
catch (IOException ex)
{
// Log the error and show a message box
logger.Error(exception: ex, message: "I/O error");
ShowErrorMessage(message: $"I/O error: {ex.Message}");
// Exit the application with a non-zero exit code
Environment.Exit(exitCode: 1);
}
catch (NetworkInformationException ex)
{
// Log the error and show a message box
logger.Error(exception: ex, message: "network error");
ShowErrorMessage(message: $"network error: {ex.Message}");
// Exit the application with a non-zero exit code
Environment.Exit(exitCode: 1);
}
catch (Exception ex)
{
// Log the error and show a message box
logger.Error(exception: ex, message: "An unexpected error occurred.");
ShowErrorMessage(message: $"An unexpected error occurred: {ex.Message}");
// Exit the application with a non-zero exit code
Environment.Exit(exitCode: 1);
}
finally
{
// Persist all log events collected during this session for the next start.
LogEventStore.SaveAsync().GetAwaiter().GetResult();
// Ensure that the logger is properly shut down
LogManager.Shutdown();
}
}
/// <summary>Disables the navigation sounds.</summary>
/// <remarks>This method disables the navigation sounds for the current process.</remarks>
private static void DisableNavigationSounds()
{
// Disable navigation sounds for the current process
_ = CoInternetSetFeatureEnabled(featureEntry: FeatureDisableNavigationSounds, dwFlags: SetFeatureOnProcess, fEnable: true);
}
/// <summary>Registers a <see cref="LogEventTarget"/> with the current NLog configuration so that every log event is also captured in <see cref="Helpers.LogEventStore"/>.</summary>
/// <remarks>
/// If NLog already has a configuration, the target is appended; otherwise a new <see cref="LoggingConfiguration"/> is created.
/// The method calls <see cref="LogManager.ReconfigExistingLoggers()"/> so that loggers created before this call also route events to the new target.
/// </remarks>
private static void ConfigureLogEventStore()
{
// Get the existing NLog configuration, or create a new one if none exists
LoggingConfiguration config = LogManager.Configuration ?? new LoggingConfiguration();
// Create the custom in-memory target
#pragma warning disable CA2000
// Ownership is transferred to NLog by LoggingConfiguration.AddTarget and released by LogManager.Shutdown.
LogEventTarget storeTarget = new(name: "logEventStore");
#pragma warning restore CA2000
// Register the target
config.AddTarget(target: storeTarget);
// Route all levels from all loggers to this target
config.AddRule(minLevel: LogLevel.Trace, maxLevel: LogLevel.Fatal, target: storeTarget, loggerNamePattern: "*");
// Apply the updated configuration
LogManager.Configuration = config;
// Reconfigure existing loggers so they pick up the new rule
LogManager.ReconfigExistingLoggers();
}
/// <summary>Handles the case when the file is missing.</summary>
/// <remarks>This method handles the case when the file is missing.</remarks>
private static void HandleMissingFile()
{
// Create an instance of the PreLoadForm
using PreloadForm formPreload = new();
// Show the PreLoadForm
_ = formPreload.ShowDialog();
// Check if the form is exited with a cancel result
if (formPreload.DialogResult == DialogResult.Cancel)
{
// Exit the application with a non-zero exit code
Environment.Exit(exitCode: 1);
}
// Check if the file path is empty
if (string.IsNullOrEmpty(value: formPreload.MpcOrbDatFilePath))
{
// Show an error message if the file path is empty
logger.Error(message: "File not found");
ShowErrorMessage(message: "File not found");
// Exit the application with a non-zero exit code
Environment.Exit(exitCode: 1);
}
else
{
// Start the main form with the specified file path
using PlanetoidDbForm mainForm = new(mpcorbDatFilePath: formPreload.MpcOrbDatFilePath);
Application.Run(mainForm: mainForm);
}
}
/// <summary>Displays an error message.</summary>
/// <param name="message">The error message.</param>
/// <remarks>This method displays an error message to the user.</remarks>
private static void ShowErrorMessage(string message)
{
// Log the error message
_ = KryptonMessageBox.Show(text: message, caption: I18nStrings.ErrorCaption, buttons: KryptonMessageBoxButtons.OK, icon: KryptonMessageBoxIcon.Error);
}
}