WPF Single Instance Best Practices [closed]
Asked Answered
O

13

54

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?

Obliteration answered 24/1, 2013 at 16:44 Comment(6)
Good question. However, it contains quite a few questions at once, so codereview.stackexchange.com might be more appropriate.Pavier
Ok, I'll probably move it!Obliteration
did you check Microsoft's implementation? elegantcode.com/wp-content/uploads/2011/03/…Autochthon
Managing it from a remote IPC looks like a little bit too much for me. It's like using a nuclear bomb to kill a fly.Obliteration
#2850187Vocalize
From that link and other links on it, I like this answer https://mcmap.net/q/339572/-how-to-call-win32-createmutex-from-netVocalize
S
8

1) It looks like a standard Dispose implementation to me. It is not really necessary (see point 6) but it does not do any harm. (Cleanup on closing it's a bit like cleaning the house before burning it down, IMHO, but opinions on the matter differs..)

Anyway, why not using "Dispose" as the name of the cleanup method, even if it does not get called directly? You could have called it "Cleanup", but remember you also write code for humans, and Dispose looks familiar and anyone on .NET understands what is it for. So, go for "Dispose".

2) I have always seen m_Mutex = new Mutex(false, mutexName); I think it's more a convention that a technical advantage, however.

3) From MSDN:

If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.

So I would not worry. Usually, for this class of functions, UInt is not used for "it does not fit in Int, let's use UInt so we have something more" but to clarify a contract "function never returns a negative value".

4) I would avoid calling it if you will shutdown, same reason as #1

5) There are a couple of ways of doing it. The easiest way in Win32 is simply to have the second instance make the call to SetForegroundWindow (Look here: http://blogs.msdn.com/b/oldnewthing/archive/2009/02/20/9435239.aspx); however, I don't know if there is an equivalent WPF functionality or if you need to PInvoke it.

6)

For example... what happens if my application crashes between OnStartup and OnExit?

It's OK: when a process terminates, all handles owned by the process are released; the mutex is released as well.

In short, my recommendations:

  • I would used an approach based on named synchronization objects: it is the more established on the windows platform(s). (Be careful when considering a multi-user system, like terminal server! Name the synchronization object as a combination of, maybe, user name/SID and application name)
  • Use the Windows API to raise the previous instance (see my link at point #5), or the WPF equivalent.
  • You probably do not have to worry about crashes (kernel will decrease the ref counter for the kernel object for you; do a little test anyway), BUT If I may suggest an improvement: what if your first application instance does not crash but hangs? (Happens with Firefox.. I'm sure it happened to you too! No window, ff process, you cannot open a new one). In that case it may be good to combine another technique or two, to a) test if the application/window responds; b) find the hung instance and terminate it

For example, you can use your technique (trying to send/post a message to the window - if does not answer back it is stuck), plus MSK technique, to find and terminate the old process. Then start normally.

Snort answered 5/2, 2013 at 8:12 Comment(0)
L
66

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

Leaves answered 5/2, 2013 at 7:48 Comment(4)
Computers with no NIC/IPAddress -> not a problem, you are on the same machine/will use the loopback adapterNarbada
As an added touch, you may want to add calls to SetForegroundWindow(IntPtr hWnd) and ShowWindowAsync(IntPtr hWnd, int nCmdShow) to bring the currently running instance to the foreground instead of showing a message box. You'll need to use [DllImport("user32.dll")] for this.Gollin
Is "MyWPFApplication" name of EXE?Clientele
@Clark - Name of the Mutex. Could be any unique name. So, the mutex allows only one instance under that unique name.Leaves
C
56

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 :)

Chimera answered 19/5, 2014 at 5:17 Comment(6)
You forgot to mention that we need to add AppOnStartup to the App.Xaml file: Startup="AppOnStartup"Cloison
This is a fantastic answer. I was able to plug it into my app almost word for word, and then easily extend it to support my requirements. Works like a charm!Vacuity
The only downfall is that this use a thread. Probably not good for performanceAngy
This has solved my problem too so many thanks. I should add that my App.xaml file has Startup="AppOnStartup" and StartupUri="MainWindow.xaml". I also have the minor issue that a network deployed/published application checks for a new version each time the user starts it from the desktop but I can live with that.Infertile
@AltianoGerung, no problem for performance as the thread will not be active when it is waiting on the event.Intercept
I like the thread approach - it's better than the old approach of searching for top-level windows with the same name as the app. Just make sure your event name and mutex name really are unique - I use the app's name followed by a GUID created with the Create GUID too.Intercept
A
49

For WPF just use:

public partial class App : Application
{
    private static Mutex _mutex = null;

    protected override void OnStartup(StartupEventArgs e)
    {
        const string appName = "MyAppName";
        bool createdNew;

        _mutex = new Mutex(true, appName, out createdNew);

        if (!createdNew)
        {
            //app is already running! Exiting the application  
            Application.Current.Shutdown();
        }

        base.OnStartup(e);
    }          
}
Andry answered 11/12, 2015 at 19:37 Comment(2)
that is the best answer in code shortly , working without use bloody external dll and a large deprecated code.Multistage
Can simply this more and remove an arbitrary string: _mutex = new Mutex(true, this.GetType().Namespace.ToString(), out createdNew);Saadi
C
13

to prevent a second instance (and signal the existing),

  • using EventWaitHandle (since we are talking about an event),
  • using Task,
  • no Mutex code required,
  • no TCP,
  • no Pinvokes,
  • no GarbageCollection stuff,
  • thread save
  • simple

it could be done like this (this for an WPF app (see ref to App()), but works on WinForms as well):

public partial class App : Application
{
    public App()
    {
        // initiate it. Call it first.
        preventSecond();
    }

    private const string UniqueEventName = "{GENERATE-YOUR-OWN-GUID}";

    private void preventSecond()
    {
        try
        {
            EventWaitHandle.OpenExisting(UniqueEventName); // check if it exists
            this.Shutdown();
        }
        catch (WaitHandleCannotBeOpenedException)
        {
            new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName); // register
        }
    }
}

Second version: above plus signaling the other instance to show the window (change the MainWindow part for WinForms):

public partial class App : Application
{
    public App()
    {
        // initiate it. Call it first.
        //preventSecond();
        SingleInstanceWatcher();
    }

    private const string UniqueEventName = "{GENERATE-YOUR-OWN-GUID}";
    private EventWaitHandle eventWaitHandle;

    /// <summary>prevent a second instance and signal it to bring its mainwindow to foreground</summary>
    /// <seealso cref="https://mcmap.net/q/334590/-wpf-single-instance-best-practices-closed"/>
    private void SingleInstanceWatcher()
    {
        // check if it is already open.
        try
        {
            // try to open it - if another instance is running, it will exist , if not it will throw
            this.eventWaitHandle = EventWaitHandle.OpenExisting(UniqueEventName);

            // Notify other instance so it could bring itself to foreground.
            this.eventWaitHandle.Set();

            // Terminate this instance.
            this.Shutdown();
        }
        catch (WaitHandleCannotBeOpenedException)
        {
            // listen to a new event (this app instance will be the new "master")
            this.eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);
        }

        // if this instance gets the signal to show the main window
        new Task(() =>
        {
            while (this.eventWaitHandle.WaitOne())
            {
                Current.Dispatcher.BeginInvoke((Action)(() =>
                {
                    // could be set or removed anytime
                    if (!Current.MainWindow.Equals(null))
                    {
                        var mw = Current.MainWindow;

                        if (mw.WindowState == WindowState.Minimized || mw.Visibility != Visibility.Visible)
                        {
                            mw.Show();
                            mw.WindowState = WindowState.Normal;
                        }

                        // According to some sources these steps are required to be sure it went to foreground.
                        mw.Activate();
                        mw.Topmost = true;
                        mw.Topmost = false;
                        mw.Focus();
                    }
                }));
            }
        })
        .Start();
    }
}

This code as a drop in class, will be @ Selfcontained-C-Sharp-WPF-compatible-utility-classes / Utils.SingleInstance.cs

Chapeau answered 9/10, 2018 at 11:57 Comment(6)
Great solution. I like the way you use the EventWaitHandle instead of relying on a mutex or something similar. Only changes I made was to specifically catch WaitHandleCannotBeOpenedException, and I moved the creation of the wait handle to inside the task itself as it doesn't ever need to be global (it can be local in the 'try' block, and a second local inside the task. But again, best solution here.Denticulate
@MarqueIV Thanks - and only catching the specific exception makes sense. The reason I thought it would be good to have the handle synchronously created outside of the task, was to have the app quit before it spins off the async running task - being a tad more resource friendly and maybe keep any app stuff from flashing up or running (or any message box later in the code, debug etc = blocking code flow).Chapeau
Makes sense. But even if you create it outside the task, it can still be local to that function as the task should capture it, Shouldn’t it?Denticulate
:D oh sure - I just realised what you meant. It does not have to be in the global scope in that use case. Just left it there, to have accessible, in case there is another method to trigger anything on that eventWaitHandle object or is going to react to it ...Chapeau
I would suggest two improvements. Firstly that you use TryOpenExisting rather than using an Exception as part of your normal flow and secondly that you register the Task as a long running task - TaskCreationOptions.LongRunning - so you aren't using one of the Task pool spaces for the entire lifetime of your application.Win
Well, EventWaitHandle.OpenExisting, does create a synchronization object underneath so I don't understand why it should be better than a named mutex, but it is interesting to see another solution!Narbada
S
8

1) It looks like a standard Dispose implementation to me. It is not really necessary (see point 6) but it does not do any harm. (Cleanup on closing it's a bit like cleaning the house before burning it down, IMHO, but opinions on the matter differs..)

Anyway, why not using "Dispose" as the name of the cleanup method, even if it does not get called directly? You could have called it "Cleanup", but remember you also write code for humans, and Dispose looks familiar and anyone on .NET understands what is it for. So, go for "Dispose".

2) I have always seen m_Mutex = new Mutex(false, mutexName); I think it's more a convention that a technical advantage, however.

3) From MSDN:

If the message is successfully registered, the return value is a message identifier in the range 0xC000 through 0xFFFF.

So I would not worry. Usually, for this class of functions, UInt is not used for "it does not fit in Int, let's use UInt so we have something more" but to clarify a contract "function never returns a negative value".

4) I would avoid calling it if you will shutdown, same reason as #1

5) There are a couple of ways of doing it. The easiest way in Win32 is simply to have the second instance make the call to SetForegroundWindow (Look here: http://blogs.msdn.com/b/oldnewthing/archive/2009/02/20/9435239.aspx); however, I don't know if there is an equivalent WPF functionality or if you need to PInvoke it.

6)

For example... what happens if my application crashes between OnStartup and OnExit?

It's OK: when a process terminates, all handles owned by the process are released; the mutex is released as well.

In short, my recommendations:

  • I would used an approach based on named synchronization objects: it is the more established on the windows platform(s). (Be careful when considering a multi-user system, like terminal server! Name the synchronization object as a combination of, maybe, user name/SID and application name)
  • Use the Windows API to raise the previous instance (see my link at point #5), or the WPF equivalent.
  • You probably do not have to worry about crashes (kernel will decrease the ref counter for the kernel object for you; do a little test anyway), BUT If I may suggest an improvement: what if your first application instance does not crash but hangs? (Happens with Firefox.. I'm sure it happened to you too! No window, ff process, you cannot open a new one). In that case it may be good to combine another technique or two, to a) test if the application/window responds; b) find the hung instance and terminate it

For example, you can use your technique (trying to send/post a message to the window - if does not answer back it is stuck), plus MSK technique, to find and terminate the old process. Then start normally.

Snort answered 5/2, 2013 at 8:12 Comment(0)
O
5

The most straight forward way to handle that would be using a named semaphore. Try something like this...

public partial class App : Application
{
    Semaphore sema;
    bool shouldRelease = false;

    protected override void OnStartup(StartupEventArgs e)
    {

        bool result = Semaphore.TryOpenExisting("SingleInstanceWPFApp", out sema);

        if (result) // we have another instance running
        {
            App.Current.Shutdown();
        }
        else
        {
            try
            {
                sema = new Semaphore(1, 1, "SingleInstanceWPFApp");
            }
            catch
            {
                App.Current.Shutdown(); //
            }
        }

        if (!sema.WaitOne(0))
        {
            App.Current.Shutdown();
        }
        else
        {
            shouldRelease = true;
        }


        base.OnStartup(e);
    }

    protected override void OnExit(ExitEventArgs e)
    {
        if (sema != null && shouldRelease)
        {
            sema.Release();
        }
    }

}
Osier answered 2/2, 2013 at 19:24 Comment(3)
Umm... what's the difference from the OP code? He uses a named mutex, why a semaphore should be better?Narbada
@LorenzoDematté I realise this is an old question of yours, but for others, read this answer for a good analysis of the important differences.Kronfeld
@Kronfeld while the answer is an interesting reading, it is for a different OS. In the NT kernel mutexes and semaphores are both kernel dispatcher objects and they act in a pretty similar way. For sure in such a way that for the OP purpose using a semaphore with a count of 1 instead of a mutex has no effect whatsover.Narbada
W
5

My Solution for a .Net Core 3 Wpf Single Instance Application:

[STAThread]
public static void Main()
{
    StartSingleInstanceApplication<CntApplication>();
}

public static void StartSingleInstanceApplication<T>()
    where T : RichApplication
{
    DebuggerOutput.GetInstance();

    Assembly assembly = typeof(T).Assembly;
    string mutexName = $"SingleInstanceApplication/{assembly.GetName().Name}/{assembly.GetType().GUID}";

    Mutex mutex = new Mutex(true, mutexName, out bool mutexCreated);

    if (!mutexCreated)
    {
        mutex = null;

        var client = new NamedPipeClientStream(mutexName);
        client.Connect();

        using (StreamWriter writer = new StreamWriter(client))
            writer.Write(string.Join("\t", Environment.GetCommandLineArgs()));

        return;
    }
    else
    {
        T application = Activator.CreateInstance<T>();

        application.Exit += (object sender, ExitEventArgs e) =>
        {
            mutex.ReleaseMutex();
            mutex.Close();
            mutex = null;
        };

        Task.Factory.StartNew(() =>
        {
            while (mutex != null)
            {
                using (var server = new NamedPipeServerStream(mutexName))
                {
                    server.WaitForConnection();

                    using (StreamReader reader = new StreamReader(server))
                    {
                        string[] args = reader.ReadToEnd().Split("\t", StringSplitOptions.RemoveEmptyEntries).ToArray();
                        UIDispatcher.GetInstance().Invoke(() => application.ExecuteCommandLineArgs(args));
                    }
                }
            }
        }, TaskCreationOptions.LongRunning);

        typeof(T).GetMethod("InitializeComponent").Invoke(application, new object[] { });
        application.Run();
    }
}
Washburn answered 17/11, 2019 at 10:1 Comment(0)
H
4

I've used a simple TCP socket for this (in Java, 10 years ago).

  1. On startup connect to a predefined port, if the connection is accepted, another instance is running, if not, start a TCP Listener
  2. Once someone connects to you, popup the window and disconnect
Hibiscus answered 30/1, 2013 at 11:24 Comment(4)
if you do so, use a lock file instead of a portLanthanum
@Lanthanum I personally dislike lock files: unlike other resources (handles, sockets) they are not "deleted" automatically by the OS if the process crash. Some programs have this (Early versions of Firefox?) and they were incredibly annoying..Narbada
true, but opening an extra port also seems a strange way to solve this problem.Lanthanum
It may be strange, but it works, and it works on almost any system. The only thing I'm concerned about is, I've sometimes seen listening ports stay "open" for a short while even after the application has crashed.Seena
H
2

This is a simple solution, Open your startup file (View from where your application starts) in this case its MainWindow.xaml. Open your MainWindow.xaml.cs file. Go to the constructor and after intializecomponent() add this code:

Process Currentproc = Process.GetCurrentProcess();

Process[] procByName=Process.GetProcessesByName("notepad");  //Write the name of your exe file in inverted commas
if(procByName.Length>1)
{
  MessageBox.Show("Application is already running");
  App.Current.Shutdown();
 }

Don't forget to add System.Diagnostics

Harbourage answered 28/11, 2014 at 9:10 Comment(0)
A
2

Here is example that brings the old instance to foreground aswell:

public partial class App : Application
{
    [DllImport("user32", CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindow(string cls, string win);
    [DllImport("user32")]
    static extern IntPtr SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32")]
    static extern bool IsIconic(IntPtr hWnd);
    [DllImport("user32")]
    static extern bool OpenIcon(IntPtr hWnd);

    private static Mutex _mutex = null;

    protected override void OnStartup(StartupEventArgs e)
    {
        const string appName = "LinkManager";
        bool createdNew;

        _mutex = new Mutex(true, appName, out createdNew);

        if (!createdNew)
        {
            ActivateOtherWindow();
            //app is already running! Exiting the application  
            Application.Current.Shutdown();
        }

        base.OnStartup(e);
    }

    private static void ActivateOtherWindow()
    {
        var other = FindWindow(null, "!YOUR MAIN WINDOW TITLE HERE!");
        if (other != IntPtr.Zero)
        {
            SetForegroundWindow(other);
            if (IsIconic(other))
                OpenIcon(other);
        }
    }
}

But it will only work if your main window title do not change durig runtime.

Edit:

You can also use Startup event in App.xaml instead of overriding OnStartup.

// App.xaml.cs
private void Application_Startup(object sender, StartupEventArgs e)
{
    const string appName = "LinkManager";
    bool createdNew;

    _mutex = new Mutex(true, appName, out createdNew);

    if (!createdNew)
    {
        ActivateOtherWindow();
        //app is already running! Exiting the application  
        Application.Current.Shutdown();
    }
}

// App.xaml
<Application x:Class="MyApp.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:MyApp"
         StartupUri="MainWindow.xaml" Startup="Application_Startup"> //<- startup event

Remember to not call base.OnStartup(e) in this case!

Alfaro answered 22/4, 2016 at 22:18 Comment(1)
Solution for .NET 2 and win XP ... But my problem with it is going after titles. I see a lot of potential in getting this broken, hitting security restrictions, not working on future windows environments ...Chapeau
D
0

Just throwing my hat into the ring here. What I do is I create an ApplicationBase subclass of the regular Application class which I keep in a common library I use in all my WPF applications. Then I change the base class (from within the XAML and its code-behind) to use my base class. Finally, I use an EntryPoint.Main as the startup object for my app, which I then check the single instance status, and simply return if I'm not the first.

Note: I also show how to support a flag that lets you override that if you want to launch another instance. However, be careful with such an option. Only use it where it makes actual sense.

Here's the code:

ApplicationBase (Application Subclass)

public abstract class ApplicationBase : Application {

    public static string? SingleInstanceId { get; private set; }

    public static bool InitializeAsFirstInstance(string singleInstanceId){

        if(SingleInstanceId != null)
            throw new AlreadyInitializedException(singleInstanceId);

        SingleInstanceId = singleInstanceId;

        var waitHandleName = $"SingleInstanceWaitHandle:{singleInstanceId}";

        if(EventWaitHandle.TryOpenExisting(waitHandleName, out var waitHandle)){

            // An existing WaitHandle was successfuly opened which means we aren't the first so signal the other
            waitHandle.Set();

            // Then indicate we aren't the first instance by returning false
            return false;
        }

        // Welp, there was no existing WaitHandle with this name, so we're the first!
        // Now we have to set up the EventWaitHandle in a task to listen for other attempts to launch

        void taskBody(){

            var singleInstanceWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, waitHandleName);

            while (singleInstanceWaitHandle.WaitOne()) {

                if(Current is ApplicationBase applicationBase)
                    Current.Dispatcher.BeginInvoke(applicationBase.OtherInstanceLaunched);
            }
        }

        new Task(taskBody, TaskCreationOptions.LongRunning).Start();

        return true;
    }

    public static bool IsSingleInstance
        => SingleInstanceId != null;

    protected virtual void OtherInstanceLaunched()
        => Current.MainWindow?.BringToFront();
}

By marking OtherInstanceLaunched as virtual, I can customize that on a per-application basis by simply overriding it, or just let the default implementation do its thing, which here, is an extension method on Window that I added. (Essentially it makes sure it's visible, restored, then focuses it.)

EntryPoint.Main

public static class EntryPoint {

    public static class CommandLineArgs{
        public const string AllowMulti = "/AllowMulti";
        public const string NoSplash   = "/NoSplash";
    }

    [STAThread]
    public static int Main(string[] args) {

        var showSplashScreen = true;
        var allowMulti       = false;

        foreach (var arg in args) {

            if (arg.Equals(CommandLineArgs.AllowMulti, StringComparison.CurrentCultureIgnoreCase))
                allowMulti = true;

            if (arg.Equals(CommandLineArgs.NoSplash, StringComparison.CurrentCultureIgnoreCase))
                showSplashScreen = false;
        }

        // Try and initialize myself as the first instance. If I'm not and 'allowMulti' is false, exit with a return code of 1
        if (!ApplicationBase.InitializeAsFirstInstance(ApplicationInfo.ProductName) && !allowMulti)
            return 1;

        if (showSplashScreen) {
            var splashScreen = new SplashScreen("resources/images/splashscreen.png");
            splashScreen.Show(true, false);
        }

        _ = new App();

        return 0;
    }
}

The advantage of this approach is it hands over execution even before the application itself is instantiated as well as before the splash screen is shown. In other words, it bails out at the earliest possible place.

Note: If you don't even want multi-support, then you can remove that argument check and test. This was just added for illustrative purposes

Denticulate answered 4/12, 2020 at 5:5 Comment(0)
C
0

Although the GetProcessesByName().Length method works, Mutex is the computer-wide, C# lock needed. Since WPF does not auto-generate GUIDs in assemblyInfo as with WinForms, the app itself must self-generate a unique identifier. Also note that the Mutex must remain visible for entire application life-cycle; otherwise, it is automatically released on dispose. Call the Mutex.WaitOne() method to lock and Mutex.ReleaseMutex() to unlock. Reference: Mutex, Threading in C# by Joe Albahari ... www.albahari.com/threading/

private Mutex mutex = new Mutex(false, <Author> + <AppName>);
private void Application_Startup(object sender, StartupEventArgs e)
{
    if (!mutex.WaitOne()) { App.Current.Shutdown(<ExitCode>); }
    else                  { new MainWindow(e.Args); }
}
Coquetry answered 15/11, 2021 at 19:10 Comment(0)
N
-1

Best one go with process name,

Interaction logic for App.xaml

`

    [DllImport("user32.dll")]
    public static extern bool ShowWindowAsync(HandleRef hWnd, int nCmdShow);
    [DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr WindowHandle);
    public const int SW_RESTORE = 9;
    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)
        {                
            Process process = Process.GetProcessesByName(proc.ProcessName).FirstOrDefault();
            IntPtr hWnd = IntPtr.Zero;
            hWnd = process.MainWindowHandle;
            ShowWindowAsync(new HandleRef(null, hWnd), SW_RESTORE);
            SetForegroundWindow(process.MainWindowHandle);
            App.Current.Shutdown();
        }
    }

`

Noriega answered 11/1, 2023 at 16:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.