Запретить несколько экземпляров данного приложения в .NET?

Как лучше всего предотвратить одновременный запуск нескольких экземпляров приложения в .NET? И если нет «лучшей» техники, какие предостережения следует учитывать при каждом решении?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
127
0
103 204
22
Перейти к ответу Данный вопрос помечен как решенный

Ответы 22

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

https://web.archive.org/web/20090205153420/http://www.openwinforms.com/single_instance_application.html

У Хансельмана есть Почта при использовании класса WinFormsApplicationBase из сборки Microsoft.VisualBasic для этого.

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

Richard Watson 18.06.2009 12:28

Вы должны использовать System.Diagnostics.Process.

Выезд: http://www.devx.com/tips/Tip/20044

Обычно это делается с помощью именованного Mutex (используйте new Mutex («имя вашего приложения», true) и проверьте возвращаемое значение), но в Microsoft.VisualBasic.dll есть также некоторые классы поддержки, которые могу сделать это за тебя.

if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
{
  AppLog.Write("Application XXXX already running. Only one instance of this application is allowed", AppLog.LogMessageType.Warn);
  return;
}

Я думаю, что должен увидеть это: odetocode.com/blogs/scott/archive/2004/08/20/…

SubmarineX 30.04.2014 12:26

@SubmarineX Как вы уверены, что эта статья верна? это все еще споры между мьютексом и codeproject.com/Articles/4975/…

John Nguyen 23.07.2014 11:48

Потрясающе Просто отлично, спасибо за ваше время и усилия

Ahmed Mahmoud 20.04.2015 11:52

-1 Хотя это быстрое решение, его также очень легко обойти, если a.exe можно переименовать в b.exe, и оба будут работать. + другие случаи, когда это просто не работает должным образом. Используйте с осторожностью!

Lars Nielsen 02.12.2015 12:18

Это не работает с моно-проявкой. Однако в среде Windows он отлично работает.

Tono Nam 11.02.2019 20:20

Используя Visual Studio 2005 или 2008, когда вы создаете проект для исполняемого файла, в окнах свойств на панели «Приложение» есть флажок «Создать приложение с одним экземпляром», который можно активировать, чтобы преобразовать приложение в приложение с одним экземпляром. .

Вот снимок окна, о котором я говорю: Это проект Windows-приложения Visual Studio 2008.

Я искал этот флажок, который вы упомянули, для моего приложения C# / WPF, но его нет.

HappyNomad 05.01.2010 19:14

Я также не вижу этого в свойствах моего приложения VS 2008 C# / WinForms.

Jesse McGrew 20.09.2010 22:25

Тоже не в VS2005. Он, должно быть, упоминает старую студию VB.

nawfal 24.08.2011 18:38

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

Doliveras 25.08.2011 11:18

Опция существует только для приложения VB.NET, но не для C#. Судя по всему, в самой опции используется класс WinFormsApplicationBase из сборки Microsoft.VisualBasic.

amolbk 30.12.2011 11:10
Ответ принят как подходящий

Используйте Mutex. В одном из приведенных выше примеров использования GetProcessByName есть много недостатков. Вот хорошая статья на эту тему:

http://odetocode.com/Blogs/scott/archive/2004/08/20/401.aspx

[STAThread]
static void Main() 
{
   using(Mutex mutex = new Mutex(false, "Global\" + appGuid))
   {
      if (!mutex.WaitOne(0, false))
      {
         MessageBox.Show("Instance already running");
         return;
      }

      Application.Run(new Form1());
   }
}

private static string appGuid = "c0a76b5a-12ab-45c5-b9d9-d693faa6e7b9";

Использование мьютекса также работает для кода, отличного от .net (хотя синтаксис может отличаться)

crashmstr 18.09.2008 22:28

Вот немного более полная версия с хорошими комментариями: stackoverflow.com/questions/229565/…

Richard Watson 18.06.2009 12:33

@ClarkKent: просто случайная строка, чтобы имя Mutex не совпадало с именем из другого приложения.

jgauffin 03.07.2013 19:23

Может ли это ограничиться определенным количеством экземпляров?

Alejandro del Río 20.01.2014 18:02

это не работает для меня, я нашел GUID из параметров проекта application1, который я хочу проверить. При запуске application1 я запускаю application2, которое должно проверить, запущено ли application1. Это не дает мне сообщения об ошибке.

CularBytes 20.01.2015 21:04

Что вы будете делать, если используете C# / WPF, где нет явного основного метода? Я не хочу явно объявлять main, поскольку он автоматически создается с некоторым дополнительным контентом.

yellavon 10.07.2015 18:11

Чтобы иметь лучший контроль при управлении версиями или изменении идентификатора вашего приложения, вы можете использовать: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAtt‌​ributes(typeof(GuidA‌​ttribute), true)[0]).Value;, который получит Guid исполняемой сборки

ciosoriog 17.06.2016 20:04

Я думаю, что важное предостережение для Mutex заключается в следующем: класс Mutex обеспечивает идентификацию потока, поэтому мьютекс может быть выпущен только тем потоком, который его получил. Напротив, класс Semaphore не обеспечивает идентификацию потока. Мьютекс также можно передавать через границы домена приложения.

Sam Rueby 15.11.2017 00:12

@yellavon создайте файл .cs, вставьте namespace your_app {public partial class App { private void ApplicationStart(object sender, StartupEventArgs e) { ... } } }, затем установите <Application ... Startup = "ApplicationStart"> в App.config

Jamesits 08.01.2018 07:19

Разве этот ответ не зависит от того, что Application.Run() не возвращается, пока приложение не выйдет? Я не знаю, как это работает, но я бы подумал, что многопоточное приложение не может зависеть от чего-то подобного. Application.Run() предназначен для Windows Forms, не так ли? В WPF мы даже не должны использовать метод «Main», верно?

Kyle Delaney 29.03.2018 17:36

Как это могло быть ответом !? Это вообще не работает.

zs2020 19.12.2018 19:30

Мне потребовалось время, чтобы понять, что приведенный выше код работает только в том случае, если мьютекс существует, пока запущен экземпляр. Я удалил блок using и изменил переменную мьютекса на статическую (или общую в vb.net). После этого все заработало как положено.

Sascha 08.04.2020 09:47

Почему GUID? Просто имя моего приложения довольно уникально на компьютере пользователя. Какой-нибудь особый случай для GUID?

kuklei 18.01.2021 20:26

Похоже, что на данный момент было предложено 3 основных метода.

  1. Унаследовать от класса Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase и установить для свойства IsSingleInstance значение true. (Я считаю, что здесь есть оговорка, что это не будет работать с приложениями WPF, не так ли?)
  2. Используйте именованный мьютекс и проверьте, был ли он уже создан.
  3. Получите список запущенных процессов и сравните названия процессов. (Здесь есть предостережение о том, что имя вашего процесса должно быть уникальным по сравнению с любыми другими процессами, запущенными на компьютере данного пользователя.)

Какие-то оговорки, которые я пропустил?

Я не думаю, что 3 очень эффективен. Проголосовал бы за Mutex, много раз пользовался им без проблем. Я никогда не использовал пункт 1, не знаю, как это работает, когда вы в C#.

typemismatch 18.09.2008 22:29

Вариант 1 по-прежнему работает с WPF, он просто немного сложнее. msdn.microsoft.com/en-us/library/ms771662.aspx

Graeme Bradbury 04.02.2009 14:01

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

public class Program
{
    static System.Threading.Mutex singleton = new Mutex(true, "My App Name");

    static void Main(string[] args)
    {
        if (!singleton.WaitOne(TimeSpan.Zero, true))
        {
            //there is already another instance running!
            Application.Exit();
        }
    }
}

Для приложения WPF используйте Application.Current.Shutdown (); Этот метод работает как шарм. Спасибо, Terrapin.

Jeff 08.11.2013 06:24

Здесь главное сделать мьютекс статичным. Как и в другом случае, сборщик мусора его заберет.

Oleksii 27.04.2015 15:11

Мне понравилась простота и ясность названного Mutex. Этот код краткий и эффективный.

Chad 01.10.2018 02:06

Используйте VB.NET! Нет: правда;)

с помощью Microsoft.VisualBasic.ApplicationServices;

WindowsFormsApplicationBase из VB.Net предоставляет вам свойство «SingleInstace», которое определяет другие экземпляры и позволяет запускать только один экземпляр.

[STAThread]
static void Main()                  // args are OK here, of course
{
    bool ok;
    m = new System.Threading.Mutex(true, "YourNameHere", out ok);

    if (! ok)
    {
        MessageBox.Show("Another instance is already running.");
        return;
    }

    Application.Run(new Form1());   // or whatever was there

    GC.KeepAlive(m);                // important!
}

От: Обеспечение единого экземпляра .NET-приложения

и: Мьютекс приложения с одним экземпляром

Тот же ответ, что и @Smink и @Imjustpondering, но с изюминкой:

Часто задаваемые вопросы Джона Скита по C#, чтобы узнать, почему GC.KeepAlive имеет значение

-1, потому что вы не можете использовать блок using с мьютексом, что сделало бы KeepAlive излишним. И да, я действительно думаю, что Джон Скит ошибся. Он не поясняет, почему в этом случае было бы неправильно использовать мьютекс.

user49572 09.02.2012 18:21

Это код для VB.Net

Private Shared Sub Main()
    Using mutex As New Mutex(False, appGuid)
        If Not mutex.WaitOne(0, False) Then
              MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If

        Application.Run(New Form1())
    End Using
End Sub

Это код для C#

private static void Main()
{
    using (Mutex mutex = new Mutex(false, appGuid)) {
        if (!mutex.WaitOne(0, false)) {
            MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        Application.Run(new Form1());
    }
}

(Примечание: это забавное решение! Оно работает, но для этого используется плохой дизайн GDI +.)

Поместите изображение в свое приложение и загрузите его при запуске. Удерживайте, пока приложение не закроется. Пользователь не сможет запустить второй экземпляр. (Конечно, решение мьютекса намного чище)

private static Bitmap randomName = new Bitmap("my_image.jpg");

Это действительно великолепно в своей простоте, и он работает практически с любыми типами файлов, а не только с изображениями. Мне кажется, что решение Mutex далеко не «чистое». Это чрезвычайно сложно, и, по-видимому, есть много способов потерпеть неудачу из-за неправильного выполнения. Также требуется метод Main(), который противоречит тому, как должен работать WPF.

Kyle Delaney 29.03.2018 19:53

Это что-то вроде использования ошибки. Это работает, но не для этой цели. Я бы не стал использовать это как профессионал.

Bitterblue 04.04.2018 17:32

Да, к сожалению, у нас нет такого эффективного и простого решения, которое бы не полагалось на исключения.

Kyle Delaney 04.04.2018 19:18

Хотя это не совсем ошибка. .NET по-прежнему ведет себя так, как задумано.

Kyle Delaney 04.04.2018 19:19

Я попробовал все решения здесь, и в моем проекте C# .net 4.0 ничего не получилось. Надеюсь помочь кому-то здесь решение, которое сработало для меня:

В качестве переменных основного класса:

private static string appGuid = "WRITE AN UNIQUE GUID HERE";
private static Mutex mutex;

Когда вам нужно проверить, запущено ли приложение:

bool mutexCreated;
mutex = new Mutex(true, "Global\" + appGuid, out mutexCreated);
if (mutexCreated)
    mutex.ReleaseMutex();

if (!mutexCreated)
{
    //App is already running, close this!
    Environment.Exit(0); //i used this because its a console app
}

Мне нужно было закрыть другие дела только с некоторыми условиями, это хорошо сработало для моей цели

Вам не нужно фактически получать мьютекс и освобождать его. Все, что вам нужно знать, это то, что другое приложение уже создало объект (то есть его счетчик ссылок ядра> = 1).

Michael Goldshteyn 12.12.2014 17:43

Это сработало для меня на чистом C#. попытка / улов - это когда, возможно, процесс в списке завершается во время вашего цикла.

using System.Diagnostics;
....
[STAThread]
static void Main()
{
...
        int procCount = 0;
        foreach (Process pp in Process.GetProcesses())
        {
            try
            {
                if (String.Compare(pp.MainModule.FileName, Application.ExecutablePath, true) == 0)
                {
                    procCount++;                        
                    if (procCount > 1) {
                       Application.Exit();
                       return;
                    }
                }
            }
            catch { }
        }
        Application.Run(new Form1());
}

Попробовав несколько решений, я вопрос. В итоге я использовал пример для WPF здесь: http://www.c-sharpcorner.com/UploadFile/f9f215/how-to-restrict-the-application-to-just-one-instance/

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

    }          
}  

В App.xaml:

x:Class = "*YourNameSpace*.App"
StartupUri = "MainWindow.xaml"
Startup = "App_Startup"

1 - Создать ссылку в program.cs ->

using System.Diagnostics;

2 - Поместите в void Main() в качестве первой строки кода ->

 if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length >1)
                return;

Вот и все.

Чем это отличается от Mutex? Есть ли подвох?

Harambe Attack Helicopter 10.10.2016 15:58

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

magallanes 26.02.2020 23:49

Как насчет этого, просто используя StreamWriter?

System.IO.File.StreamWriter OpenFlag = null;   //globally

и

try
{
    OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
    Environment.Exit(0);
}

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

Полная статья: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

We are using a named mutex with a fixed name in order to detect whether another copy of the program is running. But that also means an attacker can create the mutex first, thereby preventing our program from running at all! How can I prevent this type of denial of service attack?

...

If the attacker is running in the same security context as your program is (or would be) running in, then there is nothing you can do. Whatever "secret handshake" you come up with to determine whether another copy of your program is running, the attacker can mimic it. Since it is running in the correct security context, it can do anything that the "real" program can do.

...

Clearly you can't protect yourself from an attacker running at the same security privilege, but you can still protect yourself against unprivileged attackers running at other security privileges.

Попробуйте установить DACL на свой мьютекс, вот способ .NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx

Ни один из этих ответов не сработал для меня, потому что мне нужно, чтобы это работало под Linux с использованием monodevelop. Это отлично работает для меня:

Вызовите этот метод, передав ему уникальный идентификатор.

    public static void PreventMultipleInstance(string applicationId)
    {
        // Under Windows this is:
        //      C:\Users\SomeUser\AppData\Local\Temp\ 
        // Linux this is:
        //      /tmp/
        var temporaryDirectory = Path.GetTempPath();

        // Application ID (Make sure this guid is different accross your different applications!
        var applicationGuid = applicationId + ".process-lock";

        // file that will serve as our lock
        var fileFulePath = Path.Combine(temporaryDirectory, applicationGuid);

        try
        {
            // Prevents other processes from reading from or writing to this file
            var _InstanceLock = new FileStream(fileFulePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
            _InstanceLock.Lock(0, 0);
            MonoApp.Logger.LogToDisk(LogType.Notification, "04ZH-EQP0", "Aquired Lock", fileFulePath);

            // todo investigate why we need a reference to file stream. Without this GC releases the lock!
            System.Timers.Timer t = new System.Timers.Timer()
            {
                Interval = 500000,
                Enabled = true,
            };
            t.Elapsed += (a, b) =>
            {
                try
                {
                    _InstanceLock.Lock(0, 0);
                }
                catch
                {
                    MonoApp.Logger.Log(LogType.Error, "AOI7-QMCT", "Unable to lock file");
                }
            };
            t.Start();

        }
        catch
        {
            // Terminate application because another instance with this ID is running
            Environment.Exit(102534); 
        }
    }         

Другие вопросы по теме