1

Currently I'm using the below function to stop my exe if the exe is already running

public static bool IsAlreadyRunning()
{
    string strLoc = Assembly.GetExecutingAssembly().Location;
    FileSystemInfo fileInfo = new FileInfo(strLoc);
    string sExeName = fileInfo.Name;
    bool bCreatedNew;

    Mutex mutex = new Mutex(true, "Global\\" + sExeName, out bCreatedNew);
    if (bCreatedNew)
        mutex.ReleaseMutex();

    return !bCreatedNew;
}

but my exe can run with different arguments, so I need to stop the exe only if there is another instance of my exe running with same arguments.

So is there a way to get the arguments from above code or any pointers to get this done ?

Pரதீப்
  • 91,748
  • 19
  • 131
  • 172
  • 4
    Only if you use the arguments to generate the mutex name. The mutex only needs to have a unique name per instance. It doesn't have to include the executable name or the arguments themselves, it would be a hash generated from them. – Panagiotis Kanavos Nov 27 '19 at 10:36
  • Arguments are passed to `Main` method, you can check them – Pavel Anikhouski Nov 27 '19 at 10:38
  • The nice and clean method would be to pass the arguments to the method. The quick&dirty method is to use `[Environment.CommandLine](https://learn.microsoft.com/en-us/dotnet/api/system.environment.commandline?view=netframework-4.8) to grab the entire command line or [GetCommandLineArgs](https://learn.microsoft.com/en-us/dotnet/api/system.environment.getcommandlineargs?view=netframework-4.8) to get just the arguments – Panagiotis Kanavos Nov 27 '19 at 10:39
  • Just a suggestion, you can store the process status information in a binary file. Everytime when a user tries to run a new exe, you can check this file first. If there is a conflict, you can kill the previous process by its id (which can be stored in the document too), then you can run the fresh one. It will not prevent users to run the same exe with the same parameters because the program should start first to check the file, but at least you can prevent the rest of the process with this approach. – ycansener Nov 27 '19 at 10:41
  • @ycansener that's what the mutex is about. And lock files (what you suggest) have to be deleted even if the process crashes – Panagiotis Kanavos Nov 27 '19 at 10:42
  • [You *can* read another process's command-line arguments](https://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c), but an alternative approach could be to encode a hash code of the arguments into the mutex name when you create it in your exe. Then you could just check the mutex name (although there's a 1 in 2^32 chance that the code could incorrectly detect the exe as running with the same args when it in fact didn't, assuming you use a 32-bit hash code) – Matthew Watson Nov 27 '19 at 10:45

1 Answers1

2

You can use the arguments to generate the mutex name. The mutex only needs to have a unique name to work, it doesn't have to include the executable name.

The nice and clean method would be to pass the arguments to the method. The quick&dirty method is to use Environment.CommandLine to grab the entire command line or GetCommandLineArgs to get just the arguments.

For example :

var args=String.Join("-",Environment.GetArguments());
var mutexName=$@"Global\{sExeName}-{args}";
var mutex = new Mutex(true, mutexName, out bCreatedNew);

A better idea would be to hash the arguments first, and use the hash as the name. Using a hash method like the one in this answer, you can calculate the arguments has from the joined string :

static string GetSha256Hash(string input)
{
    var hasher=SHA256.Create();
    byte[] data = hasher.ComputeHash(Encoding.UTF8.GetBytes(input));

    StringBuilder sBuilder = new StringBuilder();

    // Loop through each byte of the hashed data 
    // and format each one as a hexadecimal string.
    for (int i = 0; i < data.Length; i++)
    {
        sBuilder.Append(data[i].ToString("x2"));
    }

    // Return the hexadecimal string.
    return sBuilder.ToString();
}

...

var args=String.Join("-",Environment.GetArguments());
var argHash=GetSha256Hash(args);
var mutexName=$@"Global\{sExeName}-{argHash}";
var mutex = new Mutex(true, mutexName, out bCreatedNew);

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • It is working when I check it by running two visual studio instances. But when I call the exe from two command prompts its isn't identifying that there is another instance of exe running with same arguments. Any idea what could be the issue here ? – Pரதீப் Nov 27 '19 at 12:48
  • @Pரதீப் what's the actual mutex name? Are the names the same for both instances? Apart from that, the code is the same as the one you started with – Panagiotis Kanavos Nov 27 '19 at 13:03
  • mutex name - realtimefeed.exe-20191126 || Are the names the same for both instances - yes. I tried the hashcode version as well, same issue, works in Visual studio but won't work when call the `exe`(taken from bin\release folder). – Pரதீப் Nov 27 '19 at 13:12
  • Never mind found the issue. https://social.msdn.microsoft.com/Forums/vstudio/en-US/d7e9120a-f48a-4d3a-aaab-addc8eb6e0da/single-instance-application-in-c?forum=csharpgeneral – Pரதீப் Nov 27 '19 at 14:01