diff --git a/.semver.txt b/.semver.txt
index c2320f5..b112f91 100644
--- a/.semver.txt
+++ b/.semver.txt
@@ -1 +1 @@
-1.0.20
+1.0.21
diff --git a/src/MonsterMonitor/MonsterMonitor.csproj b/src/MonsterMonitor/MonsterMonitor.csproj
index 950745e..14abc3d 100644
--- a/src/MonsterMonitor/MonsterMonitor.csproj
+++ b/src/MonsterMonitor/MonsterMonitor.csproj
@@ -6,10 +6,10 @@
7.3
MonsterMonitor
devenv
- 1.0.20
- 1.0.20
- 1.0.20
- 1.0.20
+ 1.0.21
+ 1.0.21
+ 1.0.21
+ 1.0.21
diff --git a/src/MonsterMonitor/Program.cs b/src/MonsterMonitor/Program.cs
index b126fbe..183628d 100644
--- a/src/MonsterMonitor/Program.cs
+++ b/src/MonsterMonitor/Program.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading;
using System.Windows.Forms;
using MonsterMonitor.UI;
@@ -6,9 +7,21 @@ namespace MonsterMonitor
{
internal static class Program
{
+ private const string SingleInstanceMutexName = @"Global\MonsterMonitor_v2_SingleInstance";
+ private const string ActivateEventName = @"Global\MonsterMonitor_v2_Activate";
+
[STAThread]
private static void Main()
{
+ var createdNew = false;
+ using (var mutex = new Mutex(true, SingleInstanceMutexName, out createdNew))
+ {
+ if (!createdNew)
+ {
+ TrySignalExistingInstance();
+ return;
+ }
+
Application.ThreadException += (sender, args) =>
{
MessageBox.Show(
@@ -29,7 +42,38 @@ namespace MonsterMonitor
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
- Application.Run(new MainForm());
+ using (var activateEvent = new EventWaitHandle(false, EventResetMode.AutoReset, ActivateEventName))
+ {
+ var form = new MainForm();
+ ThreadPool.RegisterWaitForSingleObject(
+ activateEvent,
+ (state, timedOut) =>
+ {
+ var mainForm = state as MainForm;
+ mainForm?.RestoreAndActivateFromExternalSignal();
+ },
+ form,
+ Timeout.Infinite,
+ false);
+
+ Application.Run(form);
+ }
+ }
+ }
+
+ private static void TrySignalExistingInstance()
+ {
+ try
+ {
+ using (var activateEvent = EventWaitHandle.OpenExisting(ActivateEventName))
+ {
+ activateEvent.Set();
+ }
+ }
+ catch
+ {
+ // Если не удалось отправить сигнал, просто завершаем второй инстанс.
+ }
}
}
}
diff --git a/src/MonsterMonitor/UI/MainForm.cs b/src/MonsterMonitor/UI/MainForm.cs
index a88b6c3..081f158 100644
--- a/src/MonsterMonitor/UI/MainForm.cs
+++ b/src/MonsterMonitor/UI/MainForm.cs
@@ -135,6 +135,33 @@ namespace MonsterMonitor.UI
Activate();
}
+ public void RestoreAndActivateFromExternalSignal()
+ {
+ if (InvokeRequired)
+ {
+ BeginInvoke(new Action(RestoreAndActivateFromExternalSignal));
+ return;
+ }
+
+ _log.Info("Получен сигнал активации от второго инстанса.");
+
+ if (!Visible)
+ {
+ Show();
+ }
+
+ if (WindowState == FormWindowState.Minimized)
+ {
+ WindowState = FormWindowState.Normal;
+ }
+
+ // Небольшой трюк для надежного вывода окна на передний план.
+ TopMost = true;
+ Activate();
+ TopMost = false;
+ Focus();
+ }
+
private void ExitApplication()
{
_allowClose = true;