2010-07-06 23 views
13

Mi aplicación debe comportarse de forma ligeramente diferente cuando se carga si ya hay una instancia ejecutándose.Detectando si ya se está ejecutando otra instancia de la aplicación

Entiendo cómo usar un mutex para evitar la carga de instancias adicionales, pero eso no soluciona completamente mi problema.

Por ejemplo:

  • Instancia 1 cargas, obtiene el mutex.
  • Instance 2 cargas, no puede obtener el mutex, sabe que hay otra instancia. Hasta aquí todo bien.
  • La instancia 1 se cierra, libera el mutex.
  • Instance 3 cargas, obtiene el mutex, no sabe que la Instancia 2 todavía está ejecutándose.

¿Alguna idea? Afortunadamente, no necesita lidiar con cuentas de usuario múltiples ni nada de eso.

(C#, aplicación de escritorio)

Editar: Para aclarar, la aplicación no necesita estar restringida a una sola instancia, sólo realizar una acción de puesta en marcha ligeramente diferente si hay otra instancia en ejecución. Múltiples instancias están bien (y se esperan).

+0

en el escenario que mencionó, ¿cuál es el comportamiento deseado? ¿Debería la instancia 3 hacer qué instancia 1 estaba haciendo y la instancia 2 seguir comportándose como antes? (suponiendo que no desea garantizar una sola instancia, ya que esto se soluciona con el mutex. La instancia 2 saldrá eventualmente) –

+0

En este escenario, la Instancia 1 se comporta de una manera, las Instancias 2 y 3 necesitan usar el comportamiento alternativo. Si ya hay una instancia ejecutándose, independientemente de cuándo comenzó o qué sucedió desde entonces, una nueva instancia se comportará ligeramente, aunque no significativamente, de manera diferente. La diferencia es solo una acción única en la carga. – Andy

Respuesta

12

Esto probablemente hará justo lo que quiera. Tiene la característica adicional agradable de reenviar la instancia en ejecución.

EDIT: actualizó el código para determinar el título de la aplicación automáticamente.

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

Agregaría que APPLICATION-TITLE es el nombre de archivo ejecutable sin la extensión (si no recuerdo mal). –

+0

@sztomi: gracias por despertar la memoria de tener un código para determinar eso automáticamente. –

+0

Haha, agradable. Creo que lo codifiqué cuando tuve este problema. Esto es mucho mejor :) –

1

Trate de usar un semáforo en lugar de un objeto mutex

2

Otro enfoque es detectar la instancia en ejecución como se detalla en Scott Hanselman's blog

Su ejemplo activa la primera instancia cuando el segundo intentos.

Sin embargo, no sería difícil conseguir que la segunda instancia simplemente se detenga si eso es lo que quería.

+1

¿Por qué nadie utilizará el mecanismo existente en [WindowsFormsApplicationBase] (http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.windowsformsapplicationbase.aspx)? ¡Solo porque su espacio de nombres contiene VisualBasic! Si los autores lo hubieran puesto en un espacio de nombres Foo, lo usaría mucha más gente. – Oliver

0

Podría simplemente marque GetLastError() después de crear el mutex con CreateMutex()? Si devuelve ERROR_ALREADY_EXISTS, entonces hay otra instancia en ejecución de su aplicación.

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

Si el mutex es un mutex llamado y el objeto existía antes de esta llamada de función , el valor de retorno es un manipulador el objeto existente, GetLastError devuelve ERROR_ALREADY_EXISTS, bInitialOwner se ignora y la cadena de llamada no recibe la propiedad de . Sin embargo, si la persona que llama tiene derechos de acceso limitado, la función fallará con ERROR_ACCESS_DENIED y la persona que llama debe usar la función OpenMutex .

EDIT: di cuenta de que esto era un C#/Net pregunta, lo siento..

En NET, utilice el constructor objeto mutex que devuelve la bandera createdNew, http://msdn.microsoft.com/en-us/library/bwe34f1k%28VS.80%29.aspx:

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

un buen enfoque es utilizar la solución Sandor pero el uso de WMI para obtener la lista procesos, que se describe aquí: C#: How to get the full path of running process? (La solución de Jeff). De esta forma, también puede verificar si las otras instancias en ejecución coinciden con la ruta y la ID de sesión del terminal remoto:

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; 
     } 
    } 
Cuestiones relacionadas