-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
149 lines (134 loc) · 5.84 KB
/
Program.cs
File metadata and controls
149 lines (134 loc) · 5.84 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
using System;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using CUIckScan.Services;
namespace CUIckScan;
internal static class Program
{
[STAThread]
private static void Main(string[] args)
{
// ── Single-instance enforcement — prevent multiple instances from competing
// for the same SQLite database or scan state ──
using var mutex = new Mutex(true, @"Local\CUIckScan_SingleInstance", out var createdNew);
if (!createdNew)
{
MessageBox.Show(
"CUIckScan is already running.\n\nOnly one instance can run at a time to prevent database conflicts.",
"CUIckScan",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
// ── Parse command line flags ──
var debug = args.Any(a => a.Equals("--debug", StringComparison.OrdinalIgnoreCase)
|| a.Equals("-debug", StringComparison.OrdinalIgnoreCase)
|| a.Equals("/debug", StringComparison.OrdinalIgnoreCase));
CrashLog.DebugMode = debug;
// ── Global exception handlers — catch everything, log to disk ──
AppDomain.CurrentDomain.UnhandledException += (_, e) =>
{
var ex = e.ExceptionObject as Exception;
CrashLog.Fatal($"Unhandled AppDomain exception (isTerminating={e.IsTerminating})", ex);
};
Application.ThreadException += (_, e) =>
{
CrashLog.Fatal("Unhandled UI thread exception", e.Exception);
MessageBox.Show(
$"CUIckScan encountered an unexpected error.\n\nDetails have been logged to:\n{CrashLog.CurrentLogPath}",
"CUIckScan Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
};
TaskScheduler.UnobservedTaskException += (_, e) =>
{
CrashLog.Error("Unobserved Task exception (non-fatal)", e.Exception);
e.SetObserved(); // Prevent process termination
};
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
ApplicationConfiguration.Initialize();
// Prune old logs on startup and resume audit hash chain for tamper-evidence continuity
CrashLog.PruneOldLogs();
CrashLog.PruneOldAuditLogs();
CrashLog.InitAuditHashChain();
// --fallback: force WinForms UI even when WebView2 is available (debug mode only)
var fallback = debug && args.Any(a => a.Equals("--fallback", StringComparison.OrdinalIgnoreCase)
|| a.Equals("-fallback", StringComparison.OrdinalIgnoreCase)
|| a.Equals("/fallback", StringComparison.OrdinalIgnoreCase));
var version = ReadVersion();
CrashLog.Log($"CUIckScan v{version} starting{(debug ? " [DEBUG MODE]" : "")}{(fallback ? " [FALLBACK UI]" : "")}");
CrashLog.Debug($"Args: [{string.Join(", ", args)}]");
CrashLog.Debug($"BaseDirectory: {AppContext.BaseDirectory}");
CrashLog.Debug($"OS: {Environment.OSVersion}, .NET: {Environment.Version}, 64bit: {Environment.Is64BitProcess}");
CrashLog.Debug($"User: {Environment.UserDomainName}\\{Environment.UserName}");
try
{
CrashLog.Debug("Checking WebView2 availability...");
if (!fallback && IsWebView2Available())
{
CrashLog.Debug("WebView2 available — creating MainForm...");
var mainForm = new MainForm();
CrashLog.Debug("MainForm created — creating ScanApiHost...");
var apiHost = new ScanApiHost(mainForm);
mainForm.ApiHost = apiHost;
CrashLog.Debug("ScanApiHost created — starting API server...");
var apiUrl = apiHost.Start();
CrashLog.Log($"API server started at {apiUrl}");
CrashLog.Debug("Navigating WebView2 to API URL...");
mainForm.NavigateTo(apiUrl);
CrashLog.Debug("Application.Run() — entering WinForms message loop");
Application.Run(mainForm);
CrashLog.Debug("Message loop exited — stopping API server...");
apiHost.Stop();
}
else
{
CrashLog.Warn(fallback
? "Debug --fallback flag active — using FallbackForm"
: "WebView2 not available — showing fallback form");
Application.Run(new FallbackForm(version, debug));
}
}
catch (Exception ex)
{
CrashLog.Fatal("Fatal exception in Main()", ex);
MessageBox.Show(
$"CUIckScan failed to start.\n\nDetails have been logged to:\n{CrashLog.CurrentLogPath}",
"CUIckScan Fatal Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
CrashLog.Log("CUIckScan shutting down normally");
}
private static bool IsWebView2Available()
{
try
{
var versionInfo = CoreWebView2Environment.GetAvailableBrowserVersionString();
return !string.IsNullOrEmpty(versionInfo);
}
catch
{
return false;
}
}
private static string ReadVersion()
{
var candidates = new[]
{
Path.Combine(AppContext.BaseDirectory, "release_version.txt"),
Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "release_version.txt"),
};
foreach (var path in candidates)
{
try
{
if (File.Exists(path))
return File.ReadAllText(path).Trim();
}
catch (IOException) { }
}
return "0.0.0";
}
}