Skip to content

Commit

Permalink
Feature/check if a instance of macro deck is already running and show…
Browse files Browse the repository at this point in the history
… main window if (#162)

* First non-working test

* Switched pipe process spawn to STAThread (#161)

* Added SynchronizationContext to prevent thread issues in the ui

* Added SynchronizationContext to prevent thread issues in the ui

* Fixed: tray icon was shown multiple times when Macro Deck was started while running

Co-authored-by: RecklessBoon <16234384+RecklessBoon@users.noreply.github.com>
  • Loading branch information
manuelmayer-dev and RecklessBoon authored Feb 24, 2022
1 parent a4aabfc commit 1a51870
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 41 deletions.
2 changes: 1 addition & 1 deletion GUI/CustomControls/Form.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public partial class Form : System.Windows.Forms.Form

private const int cGrip = 16; // Grip size


public Form()
{
InitializeComponent();
Expand All @@ -35,7 +36,6 @@ public Form()
this.helpMenuExportLog.Text = LanguageManager.Strings.ExportLatestLog;
}


protected override void WndProc(ref Message m)
{
if (m.Msg == 0x84)
Expand Down
2 changes: 2 additions & 0 deletions GUI/MainWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public DeckView DeckView

public MainWindow()
{
MacroDeck.SyncContext ??= SynchronizationContext.Current;
this.InitializeComponent();
this.UpdateTranslation();
this.UpdateWarningsErrors();
Expand Down Expand Up @@ -162,6 +163,7 @@ public void SetView(Control view, bool clearAll = false)
private void MainWindow_Load(object sender, EventArgs e)
{
this.SetView(new LoadingView());

this.lblVersion.Text = "Macro Deck " + MacroDeck.VersionString + (Debugger.IsAttached ? " (debug)" : "");

PluginManager.OnPluginsChange += this.OnPluginsChanged;
Expand Down
117 changes: 77 additions & 40 deletions MacroDeck.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using SuchByte.MacroDeck.Hotkeys;
using SuchByte.MacroDeck.Icons;
using SuchByte.MacroDeck.Logging;
using SuchByte.MacroDeck.Model;
using SuchByte.MacroDeck.Pipes;
using SuchByte.MacroDeck.Plugins;
using SuchByte.MacroDeck.Profiles;
using SuchByte.MacroDeck.Server;
Expand All @@ -30,7 +32,7 @@

namespace SuchByte.MacroDeck
{
public static class MacroDeck
public class MacroDeck : NativeWindow
{
static Assembly assembly = Assembly.GetExecutingAssembly();
internal static readonly string VersionString = FileVersionInfo.GetVersionInfo(assembly.Location).ProductVersion;
Expand Down Expand Up @@ -64,6 +66,8 @@ public static class MacroDeck
public static string VariablesFilePath;
public static string ProfilesFilePath;

internal static SynchronizationContext SyncContext { get; set; }

private static void InitializePaths(bool portable)
{
if (portable)
Expand Down Expand Up @@ -100,7 +104,7 @@ private static void InitializePaths(bool portable)
{
Icon = Properties.Resources.appicon,
Text = "Macro Deck " + VersionString,
Visible = true,
Visible = false,
ContextMenuStrip = _trayIconContextMenu
};

Expand All @@ -122,7 +126,6 @@ public static MainWindow MainWindow

public static string[] StartParameters;


[STAThread]
static void Main(string[] args)
{
Expand All @@ -135,6 +138,36 @@ static void Main(string[] args)
Application.ThreadException += ApplicationThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;

// Check if Macro Deck is already running
Process proc = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(proc.ProcessName);
if (processes.Length > 1)
{
MacroDeckLogger.Warning("Detected running instance, trying to show it...");
if (MacroDeckPipeClient.SendShowMainWindowMessage())
{
MacroDeckLogger.Warning("Running instance should now be visible. Closing this instance...");
Environment.Exit(0);
return;
}
else
{
MacroDeckLogger.Warning("Running instance seems not to respond, killing it and continue this instance...");
foreach (var p in processes.Where(x => x.Id != proc.Id))
{
try
{
p.Kill();
MacroDeckLogger.Info($"Killed pid {p.Id}");
}
catch (Exception ex)
{
MacroDeckLogger.Warning($"Could not kill pid {p.Id}: {ex.Message}");
}
}
}
}

//AppDomain.CurrentDomain.ProcessExit += OnApplicationExit;
// Check for start arguments

Expand Down Expand Up @@ -198,18 +231,6 @@ static void Main(string[] args)
catch { }
MacroDeckLogger.Info($"Network interfaces: {networkInterfaces}");

// Check if Macro Deck is already running
if (Process.GetProcessesByName(Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location)).Count() > 1)
{
MacroDeckLogger.Warning("Macro Deck is already running");
using (var messageBox = new GUI.CustomControls.MessageBox())
{
messageBox.ShowDialog("Macro Deck is already running", "You can't start more than one instance of Macro Deck.", MessageBoxButtons.OK);
}
Environment.Exit(0);
return;
}


// Check if directories exist
if (!Directory.Exists(UserDirectoryPath))
Expand Down Expand Up @@ -386,7 +407,6 @@ public static void SaveConfiguration()
private static void Start(bool show = false, int port = -1)
{
Language.LanguageManager.SetLanguage(_configuration.Language);
CreateTrayIcon();
_ = new HotkeyManager();
VariableManager.Load();
PluginManager.Load();
Expand All @@ -405,6 +425,10 @@ private static void Start(bool show = false, int port = -1)
ProfileManager.AddVariableChangedListener();
ProfileManager.AddWindowFocusChangedListener();

MacroDeckPipeServer.Initialize();
MacroDeckPipeServer.PipeMessage += MacroDeckPipeServer_PipeMessage;

CreateTrayIcon();

long startTook = DateTimeOffset.Now.ToUnixTimeMilliseconds() - _macroDeckStarted;
MacroDeckLogger.Info($"Macro Deck startup finished (took {startTook}ms)");
Expand All @@ -417,6 +441,16 @@ private static void Start(bool show = false, int port = -1)
Application.Run();
}

private static void MacroDeckPipeServer_PipeMessage(string message)
{
switch (message)
{
case "show":
ShowMainWindow();
break;
}
}

private static void OnUpdateAvailable(object sender, EventArgs e)
{
Updater.Updater.OnUpdateAvailable -= OnUpdateAvailable;
Expand All @@ -429,6 +463,8 @@ private static void OnUpdateAvailable(object sender, EventArgs e)

private static void CreateTrayIcon()
{
_trayIcon.Visible = true;

ToolStripMenuItem showItem = new ToolStripMenuItem
{
Text = Language.LanguageManager.Strings.Show,
Expand Down Expand Up @@ -469,6 +505,7 @@ private static void RestartItemClick(object sender, EventArgs e)

public static void RestartMacroDeck(string parameters = "")
{
_trayIcon.Visible = false;
var p = new Process
{
StartInfo = new ProcessStartInfo(ExecutablePath)
Expand All @@ -483,6 +520,7 @@ public static void RestartMacroDeck(string parameters = "")

private static void ExitItemClick(object sender, EventArgs e)
{
_trayIcon.Visible = false;
Environment.Exit(0);
}

Expand All @@ -496,8 +534,29 @@ private static void OnTrayIconMouseDown(object sender, MouseEventArgs e)

public static void ShowMainWindow()
{
if (Application.OpenForms.OfType<MainWindow>().Count() > 0)
if (SyncContext == null)
{
CreateMainForm();
} else
{
SyncContext.Send(o =>
{
CreateMainForm();
}, null);
}

MacroDeckLogger.Trace("MainWindow created");
}

private static void CreateMainForm()
{
if (Application.OpenForms.OfType<MainWindow>().Count() > 0 && mainWindow != null && !mainWindow.IsDisposed)
{
if (mainWindow.InvokeRequired)
{
mainWindow.Invoke(new Action(() => ShowMainWindow()));
return;
}
mainWindow.WindowState = FormWindowState.Minimized;
mainWindow.Show();
mainWindow.WindowState = FormWindowState.Normal;
Expand All @@ -507,7 +566,6 @@ public static void ShowMainWindow()
mainWindow.Load += MainWindowLoadEvent;
mainWindow.FormClosed += MainWindow_FormClosed;
mainWindow.Show();
MacroDeckLogger.Trace("MainWindow created");
}

private static void MainWindow_FormClosed(object sender, FormClosedEventArgs e)
Expand Down Expand Up @@ -546,38 +604,17 @@ private static void OnPackageManagerUpdateCheckFinished(object sender, EventArgs
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
MacroDeckLogger.Error(typeof(MacroDeck), "CurrentDomainOnUnhandledException: " + e.ExceptionObject.ToString());
//ShowCrashReport(e.ExceptionObject.ToString());
}

private static void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
{
MacroDeckLogger.Error(typeof(MacroDeck), "ApplicationThreadException: " + e.Exception.Message + Environment.NewLine + e.Exception.StackTrace);
//ShowCrashReport(e.Exception.Message + Environment.NewLine + Environment.NewLine + e.Exception.StackTrace);
}

/*private static void ShowCrashReport(string crashReport)
{
foreach (GUI.CustomControls.Form form in Application.OpenForms)
{
if (form.Name.Equals("CrashReportDialog"))
{
return;
}
}
using (var crashReportDialog = new CrashReportDialog(crashReport))
{
crashReportDialog.ShowDialog();
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}*/

public static bool IsAdministrator()
{
return (new WindowsPrincipal(WindowsIdentity.GetCurrent())).IsInRole(WindowsBuiltInRole.Administrator);
}

}
}
39 changes: 39 additions & 0 deletions Pipe/MacroDeckPipeClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Newtonsoft.Json.Linq;
using SuchByte.MacroDeck.Logging;
using SuchByte.MacroDeck.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Text;

namespace SuchByte.MacroDeck.Pipes
{
public class MacroDeckPipeClient
{

internal static bool SendShowMainWindowMessage()
{
return SendPipeMessage("show");
}


internal static bool SendPipeMessage(string message)
{
var client = new NamedPipeClientStream("macrodeck");
client.Connect(2000);
if (client.IsConnected)
{
byte[] buffer = Encoding.ASCII.GetBytes(message);
client.Write(buffer, 0, buffer.Length);
client.Close();
client.Dispose();
return true;
} else
{
return false;
}
}

}
}
67 changes: 67 additions & 0 deletions Pipe/MacroDeckPipeServer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Newtonsoft.Json.Linq;
using SuchByte.MacroDeck.Logging;
using SuchByte.MacroDeck.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace SuchByte.MacroDeck.Pipes
{

public delegate void DelegateMessage(string message);
public class MacroDeckPipeServer
{
public static event DelegateMessage PipeMessage;


private static void WaitForConnectionCallBack(IAsyncResult iar)
{
try
{
using (var pipeServer = (NamedPipeServerStream)iar.AsyncState)
{
pipeServer.EndWaitForConnection(iar);
byte[] buffer = new byte[255];
pipeServer.Read(buffer, 0, 255);
string stringData = Encoding.ASCII.GetString(buffer).Trim('\0');
Thread t = new Thread(new ThreadStart(() => PipeMessage.Invoke(stringData)));
t.SetApartmentState(ApartmentState.STA);
t.Start();
pipeServer.Close();
SpawnServerStream();
}
}
catch
{
return;
}
}

private static void SpawnServerStream()
{
try
{
MacroDeckLogger.Trace(typeof(MacroDeckPipeServer), $"Spawning new server stream...");
NamedPipeServerStream pipeServer = new NamedPipeServerStream("macrodeck", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
pipeServer.BeginWaitForConnection(new AsyncCallback(WaitForConnectionCallBack), pipeServer);
}
catch (Exception ex)
{
MacroDeckLogger.Error(typeof(MacroDeckPipeServer), $"Failed: {ex.Message}");
}
}

public static void Initialize()
{
MacroDeckLogger.Info(typeof(MacroDeckPipeServer), $"Initializing pipe server");
SpawnServerStream();
MacroDeckLogger.Info(typeof(MacroDeckPipeServer), $"Initializing pipe server complete");
}
}
}

0 comments on commit 1a51870

Please sign in to comment.