Попередити кілька примірників певної програми в .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
Використання mutex також працює і для коду, що не належить .net (хоча синтаксис буде різним)
crashmstr

8
Ось трохи більш заповнена версія, з хорошими коментарями: stackoverflow.com/questions/229565/…
Річард Уотсон

2
@ClarkKent: Просто випадкова рядок, щоб ім'я Mutex не зіткнулося з ім'ям з іншої програми.
jgauffin

Чи може це обмежитися певною кількістю примірників?
Алехандро дель Ріо

3
Для того, щоб мати кращий контроль під час версії чи зміни вашої інструкції щодо програми, ви можете скористатися: 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 Як ви впевнені, що ця стаття правильна? це все ще суперечка між mutex та 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 (); Цей спосіб працює як шарм. Дякую Террапіну.
Джефф

Тут головне - ви зробите мютекс статичним. Як і в іншому випадку, GC збере його.
Олексій

Мені сподобалася простота і ясність названого Mutex. Цей код є стислим та ефективним.
Чад

7

Hanselman має пост про використання класу WinFormsApplicationBase з складання Microsoft.VisualBasic для цього.


Я використовую це вже пару років, але зараз хочу змінити рішення, засноване на Mutex. У мене є клієнти, які повідомляють про проблеми з цим, і я підозрюю, що для цього використовується Remoting.
Річард Уотсон

5

Здається, що до цих пір були запропоновані 3 основні методи.

  1. Отримайте з класу Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase і встановіть для властивості IsSingleInstance значення true. (Я вважаю, що тут заперечення полягає в тому, що це не буде працювати з додатками WPF, чи не так?)
  2. Використовуйте іменований mutex і перевірте, чи він уже створений.
  3. Отримайте список запущених процесів і порівняйте назви процесів. (Це має застереження вимагати того, щоб ваше ім'я процесу було унікальним щодо будь-яких інших процесів, що працюють на даній машині користувача.)

Будь-які застереження я пропустив?


3
Я не думаю, що 3 дуже ефективний. Я б голосував за Mutex, використовував його без проблем багато разів. Я ніколи не використовував предмет 1, не знаю, як це летить, коли ти в c #.
typemismatch

2
Варіант 1 все ще працює з WPF, він лише трохи більше задіяний. msdn.microsoft.com/en-us/library/ms771662.aspx
Graeme Bradbury

5

1 - Створіть посилання в program.cs ->

using System.Diagnostics;

2 - Введіть void Main()як перший рядок коду ->

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

Це воно.


Яка різниця в цьому Mutex? Чи є улов?
Вертоліт Harambe Attack

Він використовує назву процесу. Якщо ім'я буде повторене, воно створило б помилковий прапор. У будь-якому іншому випадку він чистіший за мутекс
magallanes

4

Використовуючи Visual Studio 2005 або 2008 під час створення проекту для виконуваного файлу, на вікнах властивостей на панелі "Програма" є прапорець під назвою "Зробити єдиний екземпляр програми", який можна активувати для перетворення програми в програму для одного примірника .

Ось захоплення вікна, про яке я говорю: введіть тут опис зображення Це проект додатків Visual Studio 2008.


3
Я шукав цей прапорець, який ви згадали, для мого додатка C # / WPF, і його немає.
HappyNomad

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

Також не у VS2005. Він повинен згадати стару студію VB.
nawfal

Так, опція існує, я змінив публікацію, щоб додати захоплення вікна, в якому ви можете знайти цю опцію.
Долівера

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_instan_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.
Кайл Делані

Це на зразок помилки за допомогою. Це працює, але зроблено не для цієї мети. Я б не використовував це як професіонала.
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

та: Одномоментний додаток Mutex

Відповідь така ж, як @Smink та @Imjustpondering з поворотом:

Поширені питання Джон Скіта на C #, щоб дізнатися, чому GC.KeepAlive має значення


-1, тому що ви не використовуєте використовуючий блок з mutex, що зробить 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 (використовуйте новий Mutex ("ваше ім'я програми", справжнє) і перевірте значення повернення), але в Microsoft.VisualBasic.dll також є кілька класів підтримки, які можуть зробити це за вас .


0

Це працювало для мене в чистому C #. try / catch - це, можливо, процес у списку закінчується під час циклу.

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

Ми використовуємо іменований mutex з фіксованим іменем, щоб виявити, чи працює інша копія програми. Але це також означає, що зловмисник може спершу створити мютекс, тим самим не даючи нашій програмі працювати взагалі! Як я можу запобігти атаці відмови у наданні послуги?

...

Якщо зловмисник працює в тому ж контексті безпеки, що і ваша програма (або була б) в ній, ви нічого не можете зробити. Яке б "секретне рукостискання" ви не визначили, чи працює інша копія вашої програми, зловмисник може це імітувати. Оскільки вона працює у правильному контексті безпеки, вона може робити все, що може зробити "реальна" програма.

...

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

Спробуйте встановити DACL на своєму mutex, ось .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.