Mehrere Instanzen einer bestimmten App in .NET verhindern?

123

Wie kann in .NET am besten verhindert werden, dass mehrere Instanzen einer App gleichzeitig ausgeführt werden? Und wenn es keine "beste" Technik gibt, welche Einschränkungen sind bei jeder Lösung zu beachten?

C. Drache 76
quelle

Antworten:

151

Verwenden Sie Mutex. Eines der obigen Beispiele für die Verwendung von GetProcessByName weist viele Einschränkungen auf. Hier ist ein guter Artikel zu diesem Thema:

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";
ImJustPondering
quelle
1
Die Verwendung eines Mutex funktioniert auch für Nicht-.net-Code (obwohl die Syntax variieren würde)
crashmstr
8
Hier ist eine etwas vollständigere
Richard Watson
2
@ClarkKent: Nur eine zufällige Zeichenfolge, damit der Mutex-Name nicht mit dem Namen einer anderen Anwendung kollidiert.
Jgauffin
Kann dies auch auf eine bestimmte Anzahl von Instanzen beschränkt werden?
Alejandro del Río
3
Um eine bessere Kontrolle zu haben , während die Versionierung oder Ihre App guid ändern, können Sie verwenden: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;die die ausführende Assembly ist guid zu bekommen
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;
}
Thomas Wagner
quelle
1
Ich denke, das sollte das sehen: odetocode.com/blogs/scott/archive/2004/08/20/…
SubmarineX
@SubmarineX Wie sicher bist du, dass dieser Artikel korrekt ist? Es ist immer noch eine Kontroverse zwischen Mutex und Codeproject.com/Articles/4975/…
John Nguyen
Erstaunlich Einfach perfekt, danke für deine Zeit und Mühe
Ahmed Mahmoud
2
-1 Obwohl dies die schnelle Lösung ist, ist es auch sehr einfach zu umgehen. A.exe könnte in b.exe umbenannt werden und beide werden ausgeführt. + andere Fälle, in denen dies einfach nicht wie beabsichtigt funktioniert. Mit Vorsicht verwenden!
Lars Nielsen
Dies funktioniert nicht mit Mono-Develop. In Windows-Umgebungen funktioniert es jedoch hervorragend.
Tono Nam
20

Hier ist der Code, den Sie benötigen, um sicherzustellen, dass nur eine Instanz ausgeführt wird. Dies ist die Methode zur Verwendung eines benannten Mutex.

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();
        }
    }
}
Seibar
quelle
2
Verwenden Sie für eine WPF-Anwendung Application.Current.Shutdown (); Diese Methode funktioniert wie ein Zauber. Danke Terrapin.
Jeff
Hier ist die Hauptsache, dass Sie Mutex statisch machen. Wie im anderen Fall wird GC es sammeln.
Oleksii
Ich habe die Einfachheit und Klarheit des genannten Mutex genossen. Dieser Code ist präzise und effektiv.
Chad
7

Hanselman hat einen Beitrag zur Verwendung der WinFormsApplicationBase-Klasse aus der Microsoft.VisualBasic-Assembly, um dies zu tun.

bdukes
quelle
Ich benutze das seit ein paar Jahren, möchte aber jetzt auf eine Mutex-basierte Lösung umsteigen. Ich habe Kunden, die Probleme damit melden, und ich vermute, dass Remoting verwendet wird, um dies zu tun.
Richard Watson
5

Es hört sich so an, als ob bisher drei grundlegende Techniken vorgeschlagen wurden.

  1. Leiten Sie von der Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase-Klasse ab und setzen Sie die IsSingleInstance-Eigenschaft auf true. (Ich glaube, eine Einschränkung hier ist, dass dies mit WPF-Anwendungen nicht funktioniert, oder?)
  2. Verwenden Sie einen benannten Mutex und prüfen Sie, ob er bereits erstellt wurde.
  3. Holen Sie sich eine Liste der laufenden Prozesse und vergleichen Sie die Namen der Prozesse. (Dies hat die Einschränkung, dass Ihr Prozessname im Vergleich zu anderen Prozessen, die auf dem Computer eines bestimmten Benutzers ausgeführt werden, eindeutig sein muss.)

Irgendwelche Vorbehalte, die ich verpasst habe?

C. Drache 76
quelle
3
Ich denke nicht, dass 3 sehr effizient ist. Ich würde für den Mutex stimmen und ihn viele Male ohne Probleme verwenden. Ich habe noch nie Punkt 1 verwendet und bin mir nicht sicher, wie das funktioniert, wenn Sie in c # sind.
Typemismatch
2
Option 1 funktioniert immer noch mit WPF, es ist nur etwas komplizierter. msdn.microsoft.com/en-us/library/ms771662.aspx
Graeme Bradbury
5

1 - Erstellen Sie eine Referenz in program.cs ->

using System.Diagnostics;

2 - void Main()Als erste Codezeile eingeben ->

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

Das ist es.

user5289350
quelle
Was ist der Unterschied dazu Mutex? Gibt es einen Haken?
Harambe Angriffshubschrauber
Es wird der Name des Prozesses verwendet. Wenn der Name wiederholt wird, wird ein falsches Flag generiert. In jedem anderen Fall ist es sauberer als Mutex
magallanes
4

Wenn Sie Visual Studio 2005 oder 2008 verwenden, um ein Projekt für eine ausführbare Datei zu erstellen, befindet sich in den Eigenschaftenfenstern im Bereich "Anwendung" ein Kontrollkästchen mit dem Namen "Einzelinstanzanwendung erstellen", das Sie aktivieren können, um die Anwendung in eine Einzelinstanzanwendung zu konvertieren .

Hier ist eine Aufnahme des Fensters, von dem ich spreche: Geben Sie hier die Bildbeschreibung ein Dies ist ein Windows Studio-Anwendungsprojekt von Visual Studio 2008.

Doliveras
quelle
3
Ich habe nach diesem von Ihnen erwähnten Kontrollkästchen für meine C # / WPF-App gesucht, und es gibt keine.
HappyNomad
3
Ich sehe es auch nicht in den Eigenschaften meiner VS 2008 C # / WinForms-App.
Jesse McGrew
Nicht auch in VS2005. Er muss das alte VB-Studio erwähnen.
Nawfal
Ja, die Option ist vorhanden. Ich habe den Beitrag geändert, um eine Erfassung des Fensters hinzuzufügen, in dem Sie diese Option finden.
Doliveras
6
Die Option ist nur für VB.NET-Anwendungen verfügbar, nicht für C #. Anscheinend verwendet die Option selbst die WinFormsApplicationBase-Klasse aus der Microsoft.VisualBasic-Assembly.
Amolbk
4

Ich habe alle Lösungen hier ausprobiert und in meinem C # .net 4.0-Projekt hat nichts funktioniert. In der Hoffnung, hier jemandem die Lösung zu helfen, die für mich funktioniert hat:

Als Hauptklassenvariablen:

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

Wenn Sie überprüfen müssen, ob die App bereits ausgeführt wird:

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
}

Ich musste andere Stellen nur unter bestimmten Bedingungen schließen, dies funktionierte gut für meinen Zweck

HypeZ
quelle
Sie müssen den Mutex nicht tatsächlich erwerben und freigeben. Alles, was Sie wissen müssen, ist, ob eine andere App das Objekt bereits erstellt hat (dh die Kernel-Referenzanzahl> = 1).
Michael Goldshteyn
3

Nach dem Ausprobieren mehrerer Lösungen stelle ich die Frage. Am Ende habe ich das Beispiel für WPF hier verwendet: 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();  
        }  

    }          
}  

In App.xaml:

x:Class="*YourNameSpace*.App"
StartupUri="MainWindow.xaml"
Startup="App_Startup"
Kasper Halvas Jensen
quelle
2

In diesem Artikel wird lediglich erläutert, wie Sie eine Windows-Anwendung mit Kontrolle über die Anzahl ihrer Instanzen erstellen oder nur eine einzelne Instanz ausführen können. Dies ist ein sehr typischer Bedarf einer Geschäftsanwendung. Es gibt bereits viele andere mögliche Lösungen, um dies zu kontrollieren.

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

Jorge Ferreira
quelle
Link ist unterbrochen.
Tschad
2

Verwenden Sie VB.NET! Nicht wirklich ;)

Verwenden von Microsoft.VisualBasic.ApplicationServices;

Die WindowsFormsApplicationBase von VB.Net bietet Ihnen eine "SingleInstace" -Eigenschaft, die andere Instanzen ermittelt und nur eine Instanz ausführen lässt.

MADMap
quelle
2

Dies ist der Code für 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

Dies ist der Code für 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());
    }
}
Shruti
quelle
1

(Hinweis: Dies ist eine unterhaltsame Lösung! Sie funktioniert, verwendet jedoch schlechtes GDI + -Design, um dies zu erreichen.)

Fügen Sie ein Bild in Ihre App ein und laden Sie es beim Start. Halten Sie die Taste gedrückt, bis die App beendet wird. Der Benutzer kann keine zweite Instanz starten. (Natürlich ist die Mutex-Lösung viel sauberer)

private static Bitmap randomName = new Bitmap("my_image.jpg");
Bitter blau
quelle
Dies ist in seiner Einfachheit tatsächlich brillant und funktioniert mit so ziemlich jeder Art von Datei und nicht nur mit Bildern. Ich denke, die Mutex-Lösung ist alles andere als "sauber". Es ist extrem kompliziert und es gibt anscheinend viele Möglichkeiten, wie es scheitern kann, weil es nicht "richtig" gemacht wird. Es erfordert auch eine Main()Methode, die gegen die Funktionsweise von WPF verstößt.
Kyle Delaney
Dies ist eine Art Fehler bei der Verwendung. Es funktioniert, aber es ist nicht für diesen Zweck gemacht. Ich würde es nicht als Profi benutzen.
Bitterblue
Ja, es ist bedauerlich, dass wir keine so effektive und einfache Lösung haben, ohne uns auf Ausnahmen zu verlassen.
Kyle Delaney
Obwohl es nicht gerade ein Fehler ist. Es ist immer noch .NET, das sich wie beabsichtigt verhält.
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!
}

Von: Sicherstellen einer einzelnen Instanz von .NET Application

und: Single Instance Application Mutex

Gleiche Antwort wie @Smink und @Imjustpondering mit einem Twist:

Jon Skeets FAQ zu C # , um herauszufinden, warum GC.KeepAlive wichtig ist

Ric Tokyo
quelle
-1, weil Sie keinen using-Block mit dem Mutex verwenden, was das KeepAlive überflüssig machen würde. Und ja, ich glaube, John Skeet hat das falsch verstanden. Er geht nicht näher darauf ein, warum es in diesem Fall falsch wäre, den Mutex zu entsorgen.
1

Einfach mit a StreamWriter, wie wäre es damit?

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

und

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

Normalerweise wird dies mit einem benannten Mutex durchgeführt (verwenden Sie einen neuen Mutex ("Ihr App-Name", true) und überprüfen Sie den Rückgabewert), aber es gibt auch einige Support-Klassen in Microsoft.VisualBasic.dll, die dies für Sie tun können .

Tomer Gabel
quelle
0

Das hat bei mir in reinem C # funktioniert. Der Versuch / Fang ist, wenn möglicherweise ein Prozess in der Liste während Ihrer Schleife beendet wird.

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());
}
Derek Wade
quelle
0

Berücksichtigen Sie unbedingt die Sicherheit, wenn Sie eine Anwendung auf eine einzelne Instanz beschränken:

Vollständiger Artikel: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

Wir verwenden einen benannten Mutex mit einem festen Namen, um festzustellen, ob eine andere Kopie des Programms ausgeführt wird. Das bedeutet aber auch, dass ein Angreifer zuerst den Mutex erstellen kann, wodurch verhindert wird, dass unser Programm überhaupt ausgeführt wird! Wie kann ich diese Art von Denial-of-Service-Angriff verhindern?

...

Wenn der Angreifer im selben Sicherheitskontext ausgeführt wird, in dem Ihr Programm ausgeführt wird (oder werden würde), können Sie nichts tun. Unabhängig davon, welchen "geheimen Handschlag" Sie erstellen, um festzustellen, ob eine andere Kopie Ihres Programms ausgeführt wird, kann der Angreifer dies nachahmen. Da es im richtigen Sicherheitskontext ausgeführt wird, kann es alles tun, was das "echte" Programm kann.

...

Natürlich können Sie sich nicht vor einem Angreifer schützen, der mit demselben Sicherheitsrecht ausgeführt wird, aber Sie können sich trotzdem vor nicht privilegierten Angreifern schützen, die mit anderen Sicherheitsberechtigungen ausgeführt werden.

Versuchen Sie, eine DACL für Ihren Mutex festzulegen. Gehen Sie dazu wie folgt vor: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx

Erdferkel
quelle
0

Keine dieser Antworten funktionierte für mich, da ich diese brauchte, um unter Linux mit Monodevelop zu arbeiten. Das funktioniert gut für mich:

Nennen Sie diese Methode eine eindeutige ID

    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); 
        }
    }         
Tono Nam
quelle