How to determine if a previous instance of my application is running?

后端 未结 10 1491
梦谈多话
梦谈多话 2020-12-05 10:31

I have a console application in C# in which I run various arcane automation tasks. I am well aware that this should really be a Windows Service since it nee

相关标签:
10条回答
  • 2020-12-05 10:32

    Another way to do it is to bind to an address on the local machine (as a TCP listener would). Only one process at a time can bind to a port/address combination. So pick a port on the loopback adapter and have at it.

    This has the nice side-effects of:

    • Working even if someone renames the executable
    • Resetting itself when the application crashes
    • The technique is portable across other operating systems

    On the down-side, it can fail if there's another application that binds to that particular port.


    As requested, some code to bind to a address/port is below. This is ripped out of something else. It is incomplete, but the necessary bits are here.

    using System.Net;
    using System.Net.Sockets;
    
    [...]
    
    // Make up a number that's currently unused by you, or any 
    // well-known service. i.e. 80 for http, 22 for ssh, etc..
    int portNum = 2001;  
    
    // This binds to any (meaning all) adapters on this system 
    IPAddress ipAddress = IPAddress.Any;
    IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNum);
    Socket listener = new Socket(AddressFamily.InterNetwork,
        SocketType.Stream, ProtocolType.Tcp );
    
    // The next statement will throw an exception if anyone has done this Bind!
    listener.Bind(localEndPoint);
    

    As long as listener is not garbage collected (falls out of scope) or the program doesn't terminate: that port on that adapter is yours and only yours. If anything should happen to listener then it becomes available for someone else to use. For purposes of a lock, you should probably have listener be static somewhere.

    0 讨论(0)
  • 2020-12-05 10:39

    You can use Process.GetProcessesByName("MyProcessName"); in the System.Diagnostics namespace to check if there is an instance of your process running.

    EDIT: Very good observations in the comments! This is a (very) simplistic way of doing it, and certainly doesn't cover all the bases.

    0 讨论(0)
  • 2020-12-05 10:42

    The proper way to use a mutex for this purpose:

    private static Mutex mutex;
    
    static void Main()
    {
        // STEP 1: Create and/or check mutex existence in a race-free way
        bool created;
        mutex = new Mutex(false, "YourAppName-{add-your-random-chars}", out created);
        if (!created)
        {
            MessageBox.Show("Another instance of this application is already running");
            return;
        }
    
        // STEP 2: Run whatever the app needs to do
        Application.Run(new Form1());
    
        // No need to release the mutex because it was never acquired
    }
    

    The above won't work for detecting if several users on the same machine are running the app under different user accounts. A similar case is where a process can run both under the service host and standalone. To make these work, create the mutex as follows:

            var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
            var mutexsecurity = new MutexSecurity();
            mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow));
            mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny));
            mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny));
            _mutex = new Mutex(false, "Global\\YourAppName-{add-your-random-chars}", out created, mutexsecurity);
    

    Two differences here - firstly, the mutex needs to be created with security rights that allow other user accounts to open/acquire it. Second, the name must be prefixed with "Global" in the case of services running under the service host (not sure about other users running locally on the same machine).

    0 讨论(0)
  • 2020-12-05 10:43

    The most simple (and reliable) way to do this, is using a Mutex. Use the WaitOne method of the Mutex class to wait until the mutex becomes available. An added advantage, this will not require any infinite loops

    0 讨论(0)
  • 2020-12-05 10:44

    Using a kernal object is the only correct way to implement single instance protection in Windows.

    This statement:

    mutex = Mutex.OpenExisting("SINGLEINSTANCE");

    won't work if someone else copies this line from Stackoverflow and runs their program before your program, since that other guy grabbed "SINGLEINSTANCE" before you did. You want to include a GUID in your mutex name:

    mutex = Mutex.OpenExisting("MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");

    This technique will prevent the current user from running more than one instance of your program, but will not prevent another user from doing so.

    To ensure that only one instance of your application can run on the local computer, you need to do this:

    mutex = Mutex.OpenExisting("Global\MyApp{AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");

    See the help for the CreateMutex api.

    0 讨论(0)
  • 2020-12-05 10:52

    In one of my projects I used SingleInstance Component

    0 讨论(0)
提交回复
热议问题