Подтвердить что ты не робот

Как определить, работает ли предыдущий экземпляр моего приложения?

У меня есть консольное приложение на С#, в котором я запускаю различные тайные задачи автоматизации. Мне хорошо известно, что это действительно должно быть Служба Windows, поскольку она должна работать непрерывно, но я не хочу делать это в это время. (Итак, не предлагайте это в качестве ответа).

Тем временем мне нужен образец кода С#, который позволит мне определить, есть ли уже экземпляр приложения.

В старые дни VB6.0 я бы использовал App.PrevInstance()

Я хочу иметь возможность сделать это в моем основном методе:

static void Main()
{
  if(!MyApp.IsAlreadyRunning())
  {
    while(true)
    {
      RockAndRollAllNightAndPartyEveryDay();
    }
  }
}
4b9b3361

Ответ 1

Jeroen уже ответил на это, но наилучшим способом является использование Mutex... не по процессу. Здесь более полный ответ с кодом.

Mutex mutex;

try
{
   mutex = Mutex.OpenExisting("SINGLEINSTANCE");
   if (mutex!= null)
   {
      Console.WriteLine("Error : Only 1 instance of this application can run at a time");
      Application.Exit();
   }
}
catch (WaitHandleCannotBeOpenedException ex)
{
   mutex  = new Mutex(true, "SINGLEINSTANCE");
}

Также имейте в виду, что вам нужно запустить приложение в какой-то части Try {} finally {}. В противном случае, если вы сбой приложений, не отпуская Mutex, вы можете не перезапустить его позже.

Ответ 2

Правильный способ использования мьютекса для этой цели:

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
}

Вышеуказанное не будет работать для обнаружения, если несколько пользователей на одном компьютере запускают приложение под разными учетными записями пользователей. Аналогичный случай - это процесс, который может выполняться как под хостом, так и отдельно. Чтобы сделать эти работы, создайте мьютекс следующим образом:

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

Здесь есть две отличия: во-первых, необходимо создать мьютекс с правами безопасности, которые позволяют другим учетным записям пользователей открывать/приобретать их. Во-вторых, имя должно иметь префикс "Глобальный" в случае служб, запущенных под узлом службы (не уверен, что другие пользователи работают локально на одной машине).

Ответ 3

Самый простой (и надежный) способ сделать это - использовать Mutex. Используйте метод WaitOne класса Mutex, чтобы подождать, пока мьютекс станет доступным. Дополнительное преимущество, это не потребует бесконечных циклов

Ответ 4

Вы можете искать имена процессов существующего системного процесса. Например, код, см. Это сообщение в блоге.

Вы также можете использовать именованную систему Mutex, чтобы узнать, запущен ли ваш процесс.
Вот пример кода. Это, как правило, более надежное в моем опыте и гораздо более простой и понятный код.

Ответ 5

В этой статье рассказывается об этом: Предотвратить запуск второго экземпляра процесса. Это в VB.net, но вы можете его преобразовать.

Проблема в написании общей функции, которая проверяет, работает ли текущее приложение, исходит из того факта, что свойство ProcessName объекта Process ограничено 15 символами, поэтому более длинные имена процессов усечены.
Безопасный способ для получения имени процесса - получить имя файла его основного модуля и сбросить расширение. Следующая процедура повторного использования использует этот подход:

Function AppIsAlreadyRunning() As Boolean
    ' get the filename of the main module
    Dim moduleName As String = Process.GetCurrentProcess.MainModule.ModuleName

    ' discard the extension to get the process name
    Dim procName As String = System.IO.Path.GetFileNameWithoutExtension(moduleName)

    ' return true if there are 2 or more processes with that name
    If Process.GetProcessesByName(procName).Length > 1 Then
        Return True
    End If
End Function

Ответ 6

Вы можете использовать Process.GetProcessesByName("MyProcessName"); в пространстве имен System.Diagnostics, чтобы проверить, есть ли экземпляр вашего процесса.

EDIT: Очень хорошие наблюдения в комментариях! Это (очень) упрощенный способ сделать это и, конечно, не охватывает все базы.

Ответ 7

    // Allow running single instance
    string processName = Process.GetCurrentProcess().ProcessName;
    Process[] instances = Process.GetProcessesByName(processName);

    if (instances.Length > 1)
    {
        MessageBox.Show("Application already Running", "Error 1001 - Application Running");
        return;
    }

Изящно выйти из приложения с почтовым ящиком, как показано выше, если приложение уже запущено

Ответ 8

Использование объекта kernal - единственный правильный способ реализации защиты отдельных экземпляров в Windows.

Это утверждение:

mutex = Mutex.OpenExisting( "SINGLEINSTANCE" );

не будет работать, если кто-то еще копирует эту строку из Stackoverflow и запускает свою программу перед вашей программой, так как этот другой парень схватил "SINGLEINSTANCE", прежде чем вы это сделали. Вы хотите включить GUID в свое имя мьютекса:

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

Этот метод не позволит текущему пользователю запускать более одного экземпляра вашей программы, но не помешает другому пользователю этого сделать.

Чтобы гарантировать, что на локальном компьютере может работать только один экземпляр вашего приложения, вам необходимо сделать следующее:

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

См. справку для CreateMutex api.

Ответ 10

Другой способ сделать это - привязка к адресу на локальном компьютере (как прослушиватель TCP). Только один процесс за один раз может привязываться к комбинации портов/адресов. Поэтому выберите порт на адаптере loopback и получите его.

У этого есть хорошие побочные эффекты:

  • Работает, даже если кто-то переименовывает исполняемый файл
  • Сброс самого себя при сбое приложения
  • Этот метод переносится в других операционных системах.

В нижней части он может выйти из строя, если есть другое приложение, которое привязывается к этому конкретному порту.


В соответствии с запросом, код, привязанный к адресу/порту, приведен ниже. Это вырывается из чего-то другого. Он неполный, но необходимые биты здесь.

using System.Net;
using System.Net.Sockets;

[...]

// Make up a number that 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);

До тех пор, пока слушатель не собирает мусор (выпадает из области) или программа не заканчивается: этот порт на этом адаптере является вашим и только вашим. Если что-то произойдет с listener, тогда он станет доступен для кого-то другого. Для целей блокировки, вероятно, у вас должно быть listener.