-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
226 lines (201 loc) · 8.49 KB
/
Copy pathProgram.cs
File metadata and controls
226 lines (201 loc) · 8.49 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
using System.Diagnostics;
using System.IO.Pipes;
using System.Runtime.InteropServices;
namespace Wrok
{
internal static class Program
{
private const string MutexName = "Global\\Wrok_SingleInstanceMutex";
private const string PipeName = "Wrok_SingleInstancePipe";
private static Mutex? _singleInstanceMutex;
private const int HWND_BROADCAST = 0xFFFF;
private const int WM_SHOWWINDOW = 0x0018;
[DllImport("user32.dll", SetLastError = true)]
private static extern bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[STAThread]
private static void Main()
{
// --- Persistent file logging setup (writes Trace to %LOCALAPPDATA%\Wrok\logs\app.log) ---
try
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var appDir = Path.Combine(localAppData, "Wrok");
var logsDir = Path.Combine(appDir, "logs");
Directory.CreateDirectory(logsDir);
var logFile = Path.Combine(logsDir, "app.log");
// Keep a rolling simple policy: rename existing file if bigger than 5 MB
try
{
const long maxSize = 5 * 1024 * 1024;
if (File.Exists(logFile))
{
var fi = new FileInfo(logFile);
if (fi.Length > maxSize)
{
var archived = Path.Combine(logsDir, $"app-{DateTime.UtcNow:yyyyMMddHHmmss}.log");
try { File.Move(logFile, archived); } catch { /* ignore */ }
}
}
}
catch { /* defensive */ }
var tw = new StreamWriter(new FileStream(logFile, FileMode.Append, FileAccess.Write, FileShare.Read))
{
AutoFlush = true
};
Trace.Listeners.Add(new TextWriterTraceListener(tw));
Trace.AutoFlush = true;
Trace.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] Wrok starting (pid={Process.GetCurrentProcess().Id})");
}
catch
{
// If logging setup fails, continue without file logging.
}
// --- end logging setup ---
bool isNewInstance;
_singleInstanceMutex = new Mutex(true, MutexName, out isNewInstance);
if (!isNewInstance)
{
// Another instance is running � notify it to show its window and exit.
NotifyExistingInstance();
return;
}
UpgradeSettingsIfNeeded();
// In the single (first) instance: start a named-pipe server to receive SHOW messages.
StartNamedPipeServer();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
try
{
// Null-conditional: nach RestartApplication() ist das Feld bereits
// freigegeben und genullt, dann soll hier einfach nichts passieren.
_singleInstanceMutex?.ReleaseMutex();
_singleInstanceMutex?.Dispose();
}
catch
{
// Swallow exceptions on shutdown; not critical.
}
}
/// <summary>
/// Übernimmt beim ersten Start einer neuen Assembly-Version die
/// Einstellungen (Makros, Grok-Konten, Fensterposition usw.) aus der
/// zuletzt installierten Vorgängerversion. .NET legt Settings pro
/// AssemblyVersion in einem eigenen Ordner ab und startet dort ohne
/// diesen Aufruf mit lauter Standardwerten - Settings.Default.Upgrade()
/// kopiert die alten Werte einmalig rüber, bevor sie gelesen werden.
/// SettingsUpgraded verhindert, dass das bei jedem Start erneut passiert
/// (was sonst z. B. absichtlich geänderte Werte wieder überschreiben
/// würde, falls aus Versehen zwei Ordner nebeneinander existieren).
/// </summary>
private static void UpgradeSettingsIfNeeded()
{
try
{
if (!Properties.Settings.Default.SettingsUpgraded)
{
Properties.Settings.Default.Upgrade();
Properties.Settings.Default.SettingsUpgraded = true;
Properties.Settings.Default.Save();
Trace.WriteLine("Settings aus vorheriger Version übernommen (Settings.Default.Upgrade).");
}
}
catch (Exception ex)
{
Trace.WriteLine($"UpgradeSettingsIfNeeded fehlgeschlagen: {ex}");
}
}
/// <summary>
/// Startet eine neue Instanz und beendet die aktuelle geordnet. Für
/// Einstellungen, die erst beim Neuerzeugen der CoreWebView2Environment
/// wirken (z. B. Proxy). Mutex zuerst freigeben, dann die neue Instanz
/// starten – sonst hält die alte Instanz den Mutex noch, wenn die neue
/// ihn prüft, und die neue hielte sich fälschlich für einen Zweitstart.
/// </summary>
public static void RestartApplication()
{
try
{
_singleInstanceMutex?.ReleaseMutex();
_singleInstanceMutex?.Dispose();
_singleInstanceMutex = null;
}
catch (Exception ex)
{
Trace.WriteLine($"RestartApplication: Mutex-Freigabe fehlgeschlagen: {ex}");
}
try
{
Process.Start(Environment.ProcessPath ?? Application.ExecutablePath);
}
catch (Exception ex)
{
Trace.WriteLine($"RestartApplication: neue Instanz konnte nicht gestartet werden: {ex}");
}
Application.Exit();
}
private static void NotifyExistingInstance()
{
try
{
using (var client = new NamedPipeClientStream(
".",
PipeName,
PipeDirection.Out,
PipeOptions.Asynchronous))
{
client.Connect(500); // 0.5 second timeout
using (var writer = new StreamWriter(client))
{
writer.AutoFlush = true;
writer.WriteLine("SHOW");
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Failed to notify existing instance: {ex.Message}");
}
}
private static void StartNamedPipeServer()
{
var thread = new Thread(() =>
{
while (true)
{
try
{
using (var server = new NamedPipeServerStream(
PipeName,
PipeDirection.In,
1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous))
{
server.WaitForConnection();
using (var reader = new StreamReader(server))
{
var message = reader.ReadLine();
if (string.Equals(message, "SHOW", StringComparison.OrdinalIgnoreCase))
{
// Broadcast WM_SHOWWINDOW � MainForm.WndProc will react and bring the window forward.
PostMessage((IntPtr)HWND_BROADCAST, WM_SHOWWINDOW, IntPtr.Zero, IntPtr.Zero);
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Named pipe server error: {ex.Message}");
// Continue loop and accept next client.
}
}
})
{
IsBackground = true,
Name = "Wrok_SingleInstancePipeServer"
};
thread.Start();
}
}
}