2010-07-06 6 views
13

Meine Anwendung muss sich beim Laden etwas anders verhalten, wenn bereits eine Instanz ausgeführt wird.Erkennen, ob eine andere Instanz der Anwendung bereits ausgeführt wird

Ich verstehe, wie man einen Mutex verwendet, um zu verhindern, dass zusätzliche Instanzen geladen werden, aber das löst mein Problem nicht ganz.

Zum Beispiel:

  • Instanz 1 lädt, bekommt den Mutex.
  • Instanz 2 lädt, kann den Mutex nicht bekommen, weiß, dass es eine andere Instanz gibt. So weit, ist es gut.
  • Instanz 1 schließt, gibt den Mutex frei.
  • Instanz 3 lädt, ruft den Mutex, weiß nicht, dass Instanz 2 noch läuft.

Irgendwelche Ideen? Zum Glück braucht es nicht mit mehreren Benutzerkonten oder ähnlichem zu tun zu haben.

(C#, Desktop-Anwendung)

Edit: Um zu klären, die Anwendung muss nicht auf eine einzelne Instanz beschränkt werden, führt nur eine etwas andere Start-up-Aktion, wenn es eine andere Instanz bereits ausgeführt wird. Mehrere Instanzen sind in Ordnung (und erwartet).

+0

in dem Szenario, das Sie erwähnten, was ist das gewünschte Verhalten? sollte Instanz 3 tun, was Instanz 1 getan hat und Instanz 2 weiterhin wie zuvor verhalten? (vorausgesetzt, Sie möchten keine Einzelinstanz sicherstellen, da dies durch den Mutex gelöst wird. Instanz 2 wird schließlich trotzdem beendet) –

+0

In diesem Szenario verhält sich Instanz 1 in einer Richtung, Instanz 2 und 3 müssen das alternative Verhalten verwenden. Wenn eine Instanz bereits ausgeführt wird, unabhängig davon, wann sie gestartet wurde oder was seither passiert ist, verhält sich eine neue Instanz leicht - wenn auch nicht signifikant - anders. Der Unterschied ist nur eine einmalige Aktion beim Laden. – Andy

Antwort

12

Dies wird wahrscheinlich genau das tun, was Sie wollen. Es hat die nette zusätzliche Eigenschaft, die bereits laufende Instanz nach vorne zu bringen.

EDIT: aktualisiert den Code, um den Anwendungstitel automatisch zu bestimmen.

using System; 
using System.Diagnostics; 
using System.Linq; 
using System.Reflection; 
using System.Runtime.InteropServices; 

static void Main() 
{ 
    if (!EnsureSingleInstance()) 
    { 
     return; 
    } 

    //... 
} 

static bool EnsureSingleInstance() 
{ 
    Process currentProcess = Process.GetCurrentProcess(); 

    var runningProcess = (from process in Process.GetProcesses() 
          where 
          process.Id != currentProcess.Id && 
          process.ProcessName.Equals(
           currentProcess.ProcessName, 
           StringComparison.Ordinal) 
          select process).FirstOrDefault(); 

    if (runningProcess != null) 
    { 
     ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED); 
     SetForegroundWindow(runningProcess.MainWindowHandle); 

     return false; 
    } 

    return true; 
} 

[DllImport("user32.dll", EntryPoint = "SetForegroundWindow")] 
private static extern bool SetForegroundWindow(IntPtr hWnd); 

[DllImport("user32.dll")] 
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow); 

private const int SW_SHOWMAXIMIZED = 3; 
+0

Ich würde hinzufügen, dass APPLICATION-TITLE der ausführbare Dateiname ohne die Erweiterung ist (wenn ich mich richtig erinnere). –

+0

@sztomi: danke für die Erinnerung, dass Code herumliegt, um das automatisch zu bestimmen. –

+0

Haha, schön. Ich glaube, ich habe es fest programmiert, als ich dieses Problem hatte. Das ist viel besser :) –

1

Versuchen Sie, eine Semaphore anstelle eines Mutex

2

Ein weiterer Ansatz ist es, die laufenden Instanz zu erfassen, wie die erste in Scott Hanselman's blog

Sein Beispiel Beispiel, wenn die zweiten Versuche detailliert aktiviert.

Es wäre jedoch nicht schwer, die zweite Instanz einfach zu stoppen, wenn es das ist, was Sie wollten.

+1

Warum wird niemand den vorhandenen Mechanismus in [WindowsFormsApplicationBase] (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.windowsformsapplicationbase.aspx) verwenden? Nur weil sein Namespace VisualBasic enthält! Wenn die Autoren es in einen Namensraum Foo gebracht hätten, würde es von viel mehr Leuten benutzt werden. – Oliver

0

Könnten Sie einfach GetLastError() überprüfen, nachdem die Mutex mit CreateMutex() zu schaffen? Wenn es ERROR_ALREADY_EXISTS zurückgibt, gibt es eine andere laufende Instanz Ihrer Anwendung.

Nach http://msdn.microsoft.com/en-us/library/ms682411%28VS.85%29.aspx,

Wenn der Mutex ein benanntes Mutex ist und das Objekt existierte, bevor diese Funktion Aufruf, der Rückgabewert ist ein Handle zu das vorhandene Objekt, GetLastError kehrt ERROR_ALREADY_EXISTS, bInitialOwner wird ignoriert und der aufrufende Thread erhält kein Eigentumsrecht. Wenn jedoch der Anrufer begrenzte Zugriffsrechte hat, die Funktion mit ERROR_ACCESS_DENIED fehlschlagen und die Anrufer sollten die OpenMutex Funktion verwenden.

EDIT: Gerade realisiert dies war eine C#/Net Frage, sorry..

In .NET, verwenden Sie den Mutex Konstruktor, die createdNew Flag zurückgibt, http://msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx:

public Mutex (
    bool initiallyOwned, 
    string name, 
    out bool createdNew 
) 
0

ein guter Ansatz ist es, die Sandor Lösung zu verwenden, aber WMI verwenden, um die Prozessliste zu erhalten, hier beschrieben: C#: How to get the full path of running process? (Jeffs Lösung). Auf diese Weise können Sie auch überprüfen, ob die anderen laufenden Instanzen nach Pfad und Remote-Terminal Sitzung ID übereinstimmen:

static bool EnsureSingleInstance() 
    { 
     Process currentProcess = Process.GetCurrentProcess(); 

     var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process"; 
     using (var searcher = new ManagementObjectSearcher(wmiQueryString)) 
     using (var results = searcher.Get()) 
     { 
      var query = from p in Process.GetProcesses() 
         join mo in results.Cast<ManagementObject>() 
         on p.Id equals (int)(uint)mo["ProcessId"] 
         select new 
         { 
          Process = p, 
          Path = (string)mo["ExecutablePath"], 
          CommandLine = (string)mo["CommandLine"], 
         }; 

      var runningProcess = (from process in query 
            where 
            process.Process.Id != currentProcess.Id && 
            process.Process.ProcessName.Equals(
             currentProcess.ProcessName, 
             StringComparison.Ordinal) && 
             process.Path == currentProcess.MainModule.FileName && 
             process.Process.SessionId == currentProcess.SessionId 
            select process).FirstOrDefault(); 

      return runningProcess == null; 
     } 
    }