-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
139 lines (120 loc) · 5.14 KB
/
Copy pathProgram.cs
File metadata and controls
139 lines (120 loc) · 5.14 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
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using PBIRelay.Services;
namespace PBIRelay
{
internal static class Program
{
/// <summary>
/// The app's one logger, so a crash never opens a second lock object on the
/// same log.txt. Null until <see cref="SetLogger"/> runs, which happens once
/// <c>ApplicationPresenter</c> exists - an exception before then still falls
/// back to a fresh instance in <see cref="HandleException"/>.
/// </summary>
private static ILogger _logger;
internal static void SetLogger(ILogger logger) => _logger = logger;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// #64: concurrent PBIRelay processes share config.json/log.txt without
// coordination and compete for the same fixed ports - allow only one
// instance and front the existing one instead. The mutex handle is
// held for the process lifetime; the kernel object disappears with
// the process, so a crashed PBIRelay never blocks the next start.
using var singleInstance = AcquireSingleInstanceMutex(out bool isFirstInstance);
if (!isFirstInstance)
{
FrontExistingInstance();
return;
}
// #87: --silent makes the app start hidden in the tray (used by the
// auto-start registry key so PBIRelay doesn't pop a window at login).
bool startSilent = args.Any(a => a.Equals("--silent", StringComparison.OrdinalIgnoreCase));
// Set up global exception handlers
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
Application.ThreadException += Application_ThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
ApplicationConfiguration.Initialize();
Application.Run(new MainForm(startSilent));
}
private static Mutex AcquireSingleInstanceMutex(out bool isFirstInstance)
{
try
{
return new Mutex(initiallyOwned: true, @"Global\PBIRelay_SingleInstance", out isFirstInstance);
}
catch (UnauthorizedAccessException)
{
// The mutex exists but belongs to another session/user - still a
// running PBIRelay (ports are machine-wide, so this counts too).
isFirstInstance = false;
return null;
}
}
private static void FrontExistingInstance()
{
var current = Process.GetCurrentProcess();
foreach (var process in Process.GetProcessesByName(current.ProcessName))
{
if (process.Id == current.Id) continue;
var hwnd = process.MainWindowHandle;
if (hwnd != IntPtr.Zero)
{
if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE);
SetForegroundWindow(hwnd);
return;
}
}
// No window to front (minimized to tray, or the other instance runs
// in another session) - at least say why nothing opened.
MessageBox.Show(
"PBIRelay is already running - check the system tray.",
"PBIRelay", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
HandleException(e.Exception);
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception ex)
{
HandleException(ex);
}
}
private static void HandleException(Exception ex)
{
string errorMessage = $"An unexpected error occurred:\n\n{ex.Message}\n\nStack Trace:\n{ex.StackTrace}";
MessageBox.Show(
errorMessage,
"PBIRelay - Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error
);
// Log using the app's shared logger, falling back to a fresh one only if
// the crash happened before ApplicationPresenter set it up.
try
{
var logger = _logger ?? new LoggerService();
logger.LogError("Global", "Unhandled exception occurred", ex);
}
catch
{
// If we can't log, just ignore
}
}
}
}