Zapobiegać wielu wystąpieniom danej aplikacji w .NET?


123

W przypadku platformy .NET, jaki jest najlepszy sposób zapobiegania jednoczesnemu uruchamianiu wielu wystąpień aplikacji? A jeśli nie ma „najlepszej” techniki, jakie zastrzeżenia należy wziąć pod uwagę przy każdym rozwiązaniu?

Odpowiedzi:


151

Użyj Mutex. Jeden z powyższych przykładów użycia GetProcessByName ma wiele zastrzeżeń. Oto dobry artykuł na ten temat:

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
Używanie mutexa działa również w przypadku kodu innego niż .net (chociaż składnia byłaby różna)
crashmstr

8
Oto nieco bardziej wypełniona wersja, z kilkoma dobrymi komentarzami: stackoverflow.com/questions/229565/ ...
Richard Watson

2
@ClarkKent: Tylko losowy ciąg, aby nazwa Mutex nie kolidowała z nazwą z innej aplikacji.
jgauffin

Czy może to również ograniczać się do określonej liczby przypadków?
Alejandro del Río

3
W celu uzyskania lepszej kontroli podczas wersjonowania lub zmieniając aplikacji GUID, można użyć: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;który dostanie wykonującemu Zgromadzenia guid
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
Myślę, że powinno to zobaczyć: odetocode.com/blogs/scott/archive/2004/08/20/…
SubmarineX

@SubmarineX Skąd masz pewność, że ten artykuł jest poprawny? nadal istnieje kontrowersja między muteksem
John Nguyen

Niesamowite Po prostu doskonałe dzięki za poświęcony czas i wysiłek
Ahmed Mahmoud

2
-1 Chociaż jest to szybkie rozwiązanie, bardzo łatwo jest ominąć plik .exe, którego nazwę można zmienić na b.exe i oba będą działać. + inne przypadki, w których to po prostu nie zadziała zgodnie z przeznaczeniem. Używaj ostrożnie!
Lars Nielsen

To nie działa w przypadku monoprodukcji. Jednak w środowisku Windows działa świetnie.
Tono Nam

20

Oto kod, którego potrzebujesz, aby upewnić się, że działa tylko jedna instancja. Jest to metoda korzystania z nazwanego muteksu.

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
W przypadku aplikacji WPF użyj Application.Current.Shutdown (); Ta metoda działa jak urok. Dzięki Terrapin.
Jeff

Tutaj najważniejsze jest to, że mutex jest statyczny. Podobnie jak w drugim przypadku GC go odbierze.
Oleksii

Podobała mi się prostota i przejrzystość nazwanego Mutexu. Ten kod jest zwięzły i skuteczny.
Czad

7

Hanselman ma post dotyczący używania klasy WinFormsApplicationBase z zestawu Microsoft.VisualBasic do tego.


Używam tego od kilku lat, ale teraz zamierzam zmienić na rozwiązanie oparte na Mutex. Mam klientów, którzy zgłaszają problemy z tym i podejrzewam, że do tego celu używa Remotingu.
Richard Watson

5

Wygląda na to, że do tej pory zasugerowano 3 podstawowe techniki.

  1. Pochodzą z klasy Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase i ustaw właściwość IsSingleInstance na true. (Uważam, że zastrzeżeniem jest to, że to nie zadziała z aplikacjami WPF, prawda?)
  2. Użyj nazwanego muteksu i sprawdź, czy został już utworzony.
  3. Uzyskaj listę uruchomionych procesów i porównaj nazwy procesów. (Ma to zastrzeżenie, że nazwa procesu musi być unikalna w stosunku do innych procesów uruchomionych na komputerze danego użytkownika).

Jakieś zastrzeżenia, które przegapiłem?


3
Myślę, że 3 nie jest zbyt wydajne. Głosowałbym na Mutexa, korzystałem z niego wiele razy bez problemów. Nigdy nie korzystałem z elementu 1, nie jestem pewien, jak leci, gdy jesteś w C #.
typemismatch

2
Opcja 1 nadal działa z WPF, jest tylko trochę bardziej zaangażowana. msdn.microsoft.com/en-us/library/ms771662.aspx
Graeme Bradbury

5

1 - Utwórz odniesienie w program.cs ->

using System.Diagnostics;

2 - Wstaw void Main()jako pierwszą linię kodu ->

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

Otóż ​​to.


Jaka jest różnica między tym Mutex? Czy jest jakiś haczyk?
Helikopter szturmowy Harambe

Używa nazwy procesu. Jeśli nazwa się powtórzy, wygeneruje fałszywą flagę. W każdym innym przypadku jest czystszy niż mutex
magallanes

4

Korzystając z programu Visual Studio 2005 lub 2008 podczas tworzenia projektu dla pliku wykonywalnego, w oknach właściwości w panelu „Aplikacja” znajduje się pole wyboru o nazwie „Utwórz aplikację z pojedynczą instancją”, które można aktywować, aby przekonwertować aplikację na aplikację z pojedynczym wystąpieniem .

Oto zrzut okna, o którym mówię: wprowadź opis obrazu tutaj To jest projekt aplikacji Windows Visual Studio 2008.


3
Szukałem tego pola wyboru, o którym wspomniałeś, dla mojej aplikacji C # / WPF i nie ma żadnego.
HappyNomad,

3
Nie widzę go również we właściwościach mojej aplikacji VS 2008 C # / WinForms.
Jesse McGrew

Również nie w VS2005. Musi wspomnieć o starym studiu VB.
nawfal

Tak, opcja istnieje, zmodyfikowałem post, aby dodać zrzut okna, w którym można znaleźć tę opcję.
Doliveras,

6
Ta opcja istnieje tylko dla aplikacji VB.NET, a nie dla języka C #. Najwyraźniej sama opcja używa klasy WinFormsApplicationBase z zestawu Microsoft.VisualBasic.
amolbk

4

Wypróbowałem wszystkie rozwiązania tutaj i nic nie działało w moim projekcie C # .net 4.0. Mam nadzieję, że pomogę komuś w rozwiązaniu, które zadziałało dla mnie:

Jako główne zmienne klasowe:

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

Kiedy musisz sprawdzić, czy aplikacja już działa:

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
}

Musiałem zamknąć inne poziomy tylko pod pewnymi warunkami, to działało dobrze w moim celu


Nie musisz faktycznie nabywać muteksu i go wypuszczać. Wszystko, co musisz wiedzieć, to to, czy inna aplikacja już utworzyła obiekt (tj. Liczba odwołań do jej jądra> = 1).
Michael Goldshteyn


3

Po wypróbowaniu wielu rozwiązań zadaję pytanie. Skończyło się na tym, że użyłem przykładu WPF tutaj: 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();  
        }  

    }          
}  

W załączniku xaml:

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


2

Użyj VB.NET! Nie naprawdę ;)

using Microsoft.VisualBasic.ApplicationServices;

Baza danych WindowsFormsApplicationBase z VB.Net udostępnia Właściwość „SingleInstace”, która określa inne Instancje i pozwala na uruchomienie tylko jednej Instancji.


2

To jest kod dla 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

To jest kod dla 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

(Uwaga: to fajne rozwiązanie! Działa, ale wykorzystuje zły projekt GDI +, aby to osiągnąć).

Umieść obraz w aplikacji i załaduj go podczas uruchamiania. Przytrzymaj, aż aplikacja zostanie zamknięta. Użytkownik nie będzie mógł uruchomić drugiej instancji. (Oczywiście rozwiązanie mutex jest znacznie czystsze)

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

Jest to naprawdę genialne w swojej prostocie i działa z prawie każdym rodzajem pliku, a nie tylko z obrazami. Wydaje mi się, że rozwiązanie Mutex jest dalekie od „czystego”. Jest to niezwykle skomplikowane i najwyraźniej istnieje wiele sposobów, w jakie może się to nie powieść z powodu niewłaściwego wykonania. Wymaga również Main()metody, która jest sprzeczna z tym, jak powinien działać WPF.
Kyle Delaney

To trochę jak używanie błędów. Działa, ale nie jest do tego stworzona. Nie używałbym tego jako profesjonalista.
Bitterblue

Tak, to niefortunne, że nie mamy rozwiązania tak skutecznego i prostego bez polegania na wyjątkach.
Kyle Delaney

Chociaż to nie jest dokładnie błąd. Wciąż .NET działa zgodnie z zamierzeniami.
Kyle Delaney

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!
}

Z: zapewnienie pojedynczego wystąpienia aplikacji .NET

i: Mutex aplikacji pojedynczej instancji

Ta sama odpowiedź co @Smink i @Imjustpondering z niespodzianką:

Jon Skeet's FAQ on C #, aby dowiedzieć się, dlaczego GC.KeepAlive ma znaczenie


-1, ponieważ nie możesz użyć bloku using z muteksem, co uczyniłoby KeepAlive zbytecznym. I tak, myślę, że John Skeet się pomylił. Nie wyjaśnia, dlaczego w tym przypadku pozbycie się muteksu byłoby niewłaściwe.

1

Po prostu używając, a co StreamWriterpowiesz na to?

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

i

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

0

Zwykle odbywa się to za pomocą nazwanego Mutex (użyj nowego Mutexu („nazwa Twojej aplikacji”, prawda) i sprawdź wartość zwracaną), ale jest też kilka klas obsługi w Microsoft.VisualBasic.dll, które mogą to zrobić za Ciebie .


0

To działało dla mnie w czystym C #. try / catch ma miejsce, gdy prawdopodobnie proces z listy kończy działanie podczas pętli.

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

Pamiętaj, aby wziąć pod uwagę bezpieczeństwo podczas ograniczania aplikacji do jednej instancji:

Cały artykuł: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

Używamy nazwanego muteksu o stałej nazwie, aby wykryć, czy działa inna kopia programu. Ale oznacza to również, że osoba atakująca może najpierw utworzyć muteks, uniemożliwiając w ten sposób działanie naszego programu! Jak mogę zapobiec tego typu atakom typu „odmowa usługi”?

...

Jeśli osoba atakująca działa w tym samym kontekście bezpieczeństwa, w którym działa (lub miałby) Twój program, nie możesz nic zrobić. Niezależnie od tego, jaki „tajny uścisk dłoni” wymyślisz w celu ustalenia, czy działa inna kopia programu, osoba atakująca może ją naśladować. Ponieważ działa w odpowiednim kontekście bezpieczeństwa, może zrobić wszystko, co potrafi „prawdziwy” program.

...

Oczywiście nie możesz zabezpieczyć się przed intruzem działającym z tym samym przywilejem bezpieczeństwa, ale nadal możesz chronić się przed nieuprzywilejowanymi atakującymi korzystającymi z innych uprawnień bezpieczeństwa.

Spróbuj ustawić DACL w swoim muteksie, oto sposób .NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx


0

Żadna z tych odpowiedzi nie zadziałała dla mnie, ponieważ potrzebowałem tego do pracy pod Linuksem przy użyciu monodevelop. To działa świetnie dla mnie:

Wywołaj tę metodę, przekazując jej unikalny identyfikator

    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); 
        }
    }         
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.