Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Single Instance Best Practices

This is the code I implemented so far to create a single instance WPF application:

#region Using Directives using System; using System.Globalization; using System.Reflection; using System.Threading; using System.Windows; using System.Windows.Interop; #endregion  namespace MyWPF {     public partial class MainApplication : Application, IDisposable     {         #region Members         private Int32 m_Message;         private Mutex m_Mutex;         #endregion          #region Methods: Functions         private IntPtr HandleMessages(IntPtr handle, Int32 message, IntPtr wParameter, IntPtr lParameter, ref Boolean handled)         {             if (message == m_Message)             {                 if (MainWindow.WindowState == WindowState.Minimized)                     MainWindow.WindowState = WindowState.Normal;                  Boolean topmost = MainWindow.Topmost;                  MainWindow.Topmost = true;                 MainWindow.Topmost = topmost;             }              return IntPtr.Zero;         }          private void Dispose(Boolean disposing)         {             if (disposing && (m_Mutex != null))             {                 m_Mutex.ReleaseMutex();                 m_Mutex.Close();                 m_Mutex = null;             }         }          public void Dispose()         {             Dispose(true);             GC.SuppressFinalize(this);         }         #endregion          #region Methods: Overrides         protected override void OnStartup(StartupEventArgs e)         {             Assembly assembly = Assembly.GetExecutingAssembly();             Boolean mutexCreated;             String mutexName = String.Format(CultureInfo.InvariantCulture, "Local\\{{{0}}}{{{1}}}", assembly.GetType().GUID, assembly.GetName().Name);              m_Mutex = new Mutex(true, mutexName, out mutexCreated);             m_Message = NativeMethods.RegisterWindowMessage(mutexName);              if (!mutexCreated)             {                 m_Mutex = null;                  NativeMethods.PostMessage(NativeMethods.HWND_BROADCAST, m_Message, IntPtr.Zero, IntPtr.Zero);                  Current.Shutdown();                  return;             }              base.OnStartup(e);              MainWindow window = new MainWindow();             MainWindow = window;             window.Show();               HwndSource.FromHwnd((new WindowInteropHelper(window)).Handle).AddHook(new HwndSourceHook(HandleMessages));         }          protected override void OnExit(ExitEventArgs e)         {             Dispose();             base.OnExit(e);         }         #endregion     } } 

Everything works perfectly... but I have some doubts about it and I would like to receive your suggestions about how my approach could be improved.

1) I was asked by Code Analysis to implement IDisposable interface because I was using IDisposable members (the Mutex). Is my Dispose() implementation good enough? Should I avoid it because it's never going to be called?

2) It's better to use m_Mutex = new Mutex(true, mutexName, out mutexCreated); and check for the result or to use m_Mutex = new Mutex(false, mutexName); and then check for m_Mutex.WaitOne(TimeSpan.Zero, false); ? In case of multithreading I mean...

3) RegisterWindowMessage API call should return UInt32... but HwndSourceHook is only accepting Int32 as message value... should I be worried about unexpected behaviors (like a result bigger than Int32.MaxValue)?

4) In OnStartup override... should I execute base.OnStartup(e); even if another instance is already running and I'm going to shutdown the application?

5) Is there a better way to bring the existing instance to the top that doesn't need to set Topmost value? Maybe Activate()?

6) Can you see any flaw in my approach? Something concerning multithreading, bad exceptions handling and something like that? For example... what happens if my application crashes between OnStartup and OnExit?

like image 713
Tommaso Belluzzo Avatar asked Jan 24 '13 16:01

Tommaso Belluzzo


2 Answers

There are Several choices,

  • Mutex
  • Process manager
  • Named Semaphore
  • Use a listener socket

Mutex

Mutex myMutex ;  private void Application_Startup(object sender, StartupEventArgs e) {     bool aIsNewInstance = false;     myMutex = new Mutex(true, "MyWPFApplication", out aIsNewInstance);       if (!aIsNewInstance)     {         MessageBox.Show("Already an instance is running...");         App.Current.Shutdown();       } } 

Process manager

private void Application_Startup(object sender, StartupEventArgs e) {     Process proc = Process.GetCurrentProcess();     int count = Process.GetProcesses().Where(p=>          p.ProcessName == proc.ProcessName).Count();      if (count > 1)     {         MessageBox.Show("Already an instance is running...");         App.Current.Shutdown();      } } 

Use a listener socket

One way to signal another application is to open a Tcp connection to it. Create a socket, bind to a port, and listen on a background thread for connections. If this succeeds, run normally. If not, make a connection to that port, which signals the other instance that a second application launch attempt has been made. The original instance can then bring its main window to the front, if appropriate.

“Security” software / firewalls might be an issue.

Single Instance Application C#.Net along with Win32

like image 55
C-va Avatar answered Sep 21 '22 09:09

C-va


I wanted to have a bit better user experience - if another instance is already running let's activate it rather than showing an error about the second instance. Here is my implementation.

I use named Mutex for making sure that only one instance is running and named EventWaitHandle to pass notification from one instance to another.

App.xaml.cs:

/// <summary>Interaction logic for App.xaml</summary> public partial class App {     #region Constants and Fields      /// <summary>The event mutex name.</summary>     private const string UniqueEventName = "{GUID}";      /// <summary>The unique mutex name.</summary>     private const string UniqueMutexName = "{GUID}";      /// <summary>The event wait handle.</summary>     private EventWaitHandle eventWaitHandle;      /// <summary>The mutex.</summary>     private Mutex mutex;      #endregion      #region Methods      /// <summary>The app on startup.</summary>     /// <param name="sender">The sender.</param>     /// <param name="e">The e.</param>     private void AppOnStartup(object sender, StartupEventArgs e)     {         bool isOwned;         this.mutex = new Mutex(true, UniqueMutexName, out isOwned);         this.eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);          // So, R# would not give a warning that this variable is not used.         GC.KeepAlive(this.mutex);          if (isOwned)         {             // Spawn a thread which will be waiting for our event             var thread = new Thread(                 () =>                 {                     while (this.eventWaitHandle.WaitOne())                     {                         Current.Dispatcher.BeginInvoke(                             (Action)(() => ((MainWindow)Current.MainWindow).BringToForeground()));                     }                 });              // It is important mark it as background otherwise it will prevent app from exiting.             thread.IsBackground = true;              thread.Start();             return;         }          // Notify other instance so it could bring itself to foreground.         this.eventWaitHandle.Set();          // Terminate this instance.         this.Shutdown();     }      #endregion } 

And BringToForeground in MainWindow.cs:

    /// <summary>Brings main window to foreground.</summary>     public void BringToForeground()     {         if (this.WindowState == WindowState.Minimized || this.Visibility == Visibility.Hidden)         {             this.Show();             this.WindowState = WindowState.Normal;         }          // According to some sources these steps gurantee that an app will be brought to foreground.         this.Activate();         this.Topmost = true;         this.Topmost = false;         this.Focus();     } 

And add Startup="AppOnStartup" (thanks vhanla!):

<Application x:Class="MyClass.App"                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              Startup="AppOnStartup">     <Application.Resources>     </Application.Resources> </Application> 

Works for me :)

like image 22
ZakiMa Avatar answered Sep 17 '22 09:09

ZakiMa