Andy
Andy

Reputation: 2774

Detecting if another instance of the application is already running

My application needs to behave slightly differently when it loads if there is already an instance running.

I understand how to use a mutex to prevent additional instances loading, but that doesn't quite solve my problem.

For example:

Any ideas? Thankfully it doesn't need to deal with multiple user accounts or anything like that.

(C#, desktop application)

Edit: To clarify, the application doesn't need to be restricted to a single instance, just perform a slightly different start-up action if there's another instance already running. Multiple instances are fine (and expected).

Upvotes: 14

Views: 14156

Answers (5)

Francesco Martire
Francesco Martire

Reputation: 118

a good approach is to use the Sandor solution but use WMI to obtain the processes list, described here: C#: How to get the full path of running process? (Jeff's solution). that way, you can also check if the other running instances match by path and remote terminal session id:

    static bool EnsureSingleInstance()
    {
        Process currentProcess = Process.GetCurrentProcess();

        var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
        using (var searcher = new ManagementObjectSearcher(wmiQueryString))
        using (var results = searcher.Get())
        {
            var query = from p in Process.GetProcesses()
                        join mo in results.Cast<ManagementObject>()
                        on p.Id equals (int)(uint)mo["ProcessId"]
                        select new
                        {
                            Process = p,
                            Path = (string)mo["ExecutablePath"],
                            CommandLine = (string)mo["CommandLine"],
                        };

            var runningProcess = (from process in query
                                  where
                                    process.Process.Id != currentProcess.Id &&
                                    process.Process.ProcessName.Equals(
                                      currentProcess.ProcessName,
                                      StringComparison.Ordinal) &&
                                      process.Path == currentProcess.MainModule.FileName &&
                                      process.Process.SessionId == currentProcess.SessionId
                                  select process).FirstOrDefault();

            return runningProcess == null;
        }
    }

Upvotes: 0

Sandor Drie&#235;nhuizen
Sandor Drie&#235;nhuizen

Reputation: 6388

This will probably do just what you want. It has the nice additional feature of bringing the already running instance forward.

EDIT: updated the code to determine the application title automatically.

using System;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;

static void Main()
{
    if (!EnsureSingleInstance())
    {
        return;
    }

    //...
}

static bool EnsureSingleInstance()
{
    Process currentProcess = Process.GetCurrentProcess();

    var runningProcess = (from process in Process.GetProcesses()
                          where
                            process.Id != currentProcess.Id &&
                            process.ProcessName.Equals(
                              currentProcess.ProcessName,
                              StringComparison.Ordinal)
                          select process).FirstOrDefault();

    if (runningProcess != null)
    {
        ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
        SetForegroundWindow(runningProcess.MainWindowHandle);

        return false;
    }

    return true;
}

[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

private const int SW_SHOWMAXIMIZED = 3;

Upvotes: 14

GBegen
GBegen

Reputation: 6157

Could you simply check GetLastError() after creating the mutex with CreateMutex()? If it returns ERROR_ALREADY_EXISTS, then there is another running instance of your application.

According to http://msdn.microsoft.com/en-us/library/ms682411%28VS.85%29.aspx,

If the mutex is a named mutex and the object existed before this function call, the return value is a handle to the existing object, GetLastError returns ERROR_ALREADY_EXISTS, bInitialOwner is ignored, and the calling thread is not granted ownership. However, if the caller has limited access rights, the function will fail with ERROR_ACCESS_DENIED and the caller should use the OpenMutex function.

EDIT: Just realized this was a C#/.Net question, sorry.

In .Net, use the Mutex constructor that returns the createdNew flag, http://msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx:

public Mutex (
    bool initiallyOwned,
    string name,
    out bool createdNew
)

Upvotes: 0

ChrisF
ChrisF

Reputation: 137148

Another approach is to detect the running instance as detailed in Scott Hanselman's blog

His example activates the first instance when the second tries.

However, it wouldn't be hard to get the second instance to just stop if that's what you wanted.

Upvotes: 2

Panagiotis Kanavos
Panagiotis Kanavos

Reputation: 131493

Try using a Semaphore instead of a Mutex

Upvotes: 1

Related Questions