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


123

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

Ответы:


151

Используйте 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";

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

9
Вот немного более заполненная версия с хорошими комментариями: stackoverflow.com/questions/229565/…
— Ричард Уотсон

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

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

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

22
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;
}

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

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

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

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

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

20

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

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

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

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

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

7

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


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

5

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

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

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


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

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

5

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

using System.Diagnostics;

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

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

Вот и все.


Какая в этом разница Mutex? Есть ли подвох?
— Ударный вертолет Harambe

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

4

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

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


3
Я искал этот флажок для моего приложения C # / WPF, но его нет.
— HappyNomad 05

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

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

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

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

4

Я попробовал все решения здесь, и в моем проекте 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).
— Михаил Гольдштейн


3

Попробовав несколько решений, я вопрос. В итоге я использовал пример для 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"

2

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

http://www.openwinforms.com/single_instance_application.html


Ссылка не работает.
— Чад

2

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

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

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


2

Это код для 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());
    }
}



1

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

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

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

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

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

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

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

1
[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 излишним. И да, я действительно думаю, что Джон Скит ошибся. Он не поясняет, почему в этом случае было бы неправильно использовать мьютекс.

1

Просто используя a 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);
}

0

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


0

Это сработало для меня на чистом 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());
}

0

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

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

Мы используем именованный мьютекс с фиксированным именем, чтобы определить, запущена ли другая копия программы. Но это также означает, что злоумышленник может сначала создать мьютекс, тем самым предотвратив запуск нашей программы! Как я могу предотвратить этот тип атаки типа «отказ в обслуживании»?

...

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

...

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

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


0

Ни один из этих ответов не помог мне, потому что мне нужно, чтобы это работало под 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); 
        }
    }         
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.