2009-04-22 17 views
24

Tengo una aplicación de consola en C# en la que ejecuto varias tareas de automatización arcanas. Soy muy consciente de que esto realmente debería ser un Servicio de Windows ya que necesita ejecutarse continuamente, pero I no desea desea hacer eso en este momento. (Entonces, no sugiera eso como una respuesta).¿Cómo determinar si se está ejecutando una instancia previa de mi aplicación?

Mientras tanto, necesito un ejemplo de código C# que me permita determinar si ya hay una instancia de la aplicación ejecutándose.

En los viejos días VB6.0, habría utilizado App.PrevInstance()

Quiero ser capaz de hacer esto en mi método principal:

static void Main() 
{ 
    if(!MyApp.IsAlreadyRunning()) 
    { 
    while(true) 
    { 
     RockAndRollAllNightAndPartyEveryDay(); 
    } 
    } 
} 

Respuesta

18

Jeroen ya ha respondido esto, pero la mejor manera, con mucho, es el uso de un mutex ... no por el Proceso. Aquí hay una respuesta más completa con el código.

Mutex mutex; 

try 
{ 
    mutex = Mutex.OpenExisting("SINGLEINSTANCE"); 
    if (mutex!= null) 
    { 
     Console.WriteLine("Error : Only 1 instance of this application can run at a time"); 
     Application.Exit(); 
    } 
} 
catch (WaitHandleCannotBeOpenedException ex) 
{ 
    mutex = new Mutex(true, "SINGLEINSTANCE"); 
} 

También tenga en cuenta que debe ejecutar su Aplicación en algún tipo de bloque de Prueba {} Por último {}. De lo contrario, si la aplicación se bloquea sin soltar el Mutex, es posible que no pueda volver a reiniciarlo más tarde.

+0

Según tengo entendido, en los sistemas operativos basados ​​en NT después de que un proceso termina por algún motivo, el SO libera todos los objetos kernal creados por ese proceso. ¿Es eso correcto? –

+0

Probablemente puedas probarlo fácilmente ... Acabo de encontrar esto. http://stackoverflow.com/questions/646480/is-using-a-mutex-to-prevent-multipule-instances-of-the-same-program-from-running/646500 – Ian

+4

-1: hay una carrera condición en la adquisición del mutex; ver mi respuesta alternativa. Además, mencionar que "SINGLEINSTANCE" debe ser exclusivo de la aplicación es obligatorio. –

1

Usted puede utilizar Process.GetProcessesByName("MyProcessName"); en el espacio de nombres a System.Diagnostics verifica si hay una instancia de tu proceso ejecutándose.

EDIT: Muy buenas observaciones en los comentarios! Esta es una forma (muy) simplista de hacerlo, y ciertamente no cubre todas las bases.

+0

Del artículo en mi comentario ... El problema al escribir una función genérica que verifica si la aplicación actual ya se está ejecutando proviene del hecho de que la propiedad ProcessName del objeto Process parece estar limitada a 15 caracteres, por lo que es más larga los nombres de proceso están truncados. Por ejemplo, cree la aplicación ConsoleApplication1 predeterminada y ejecute este código: – Ron

+0

Esto también devuelve el proceso actual, así que asegúrese de filtrar también la identificación de su Proceso actual de esta lista (o asegúrese de que la longitud de la matriz devuelta sea> 1, no> 0) –

+1

Esto no funciona si existen dos copias de la aplicación y se ha cambiado el nombre de una. – Murray

2

Puede buscar nombres de proceso del proceso del sistema existente. Por ejemplo código, see this blog post.

También puede usar un sistema denominado Mutex para ver si su proceso ya se está ejecutando.
Here is some sample code. Esto tiende a ser más confiable en mi experiencia, y es un código mucho más simple y comprensible.

4

La manera más simple (y confiable) de hacer esto, es usar un Mutex. Use el método WaitOne de la clase Mutex para esperar hasta que el mutex esté disponible. Una ventaja adicional, esto no requerirá ningún bucle infinito

2

Este artículo habla de ello: Prevent a second process instance from running. Está en VB.net pero puedes convertirlo.

El problema de escribir una función genérica que comprueba si la aplicación actual ya se está ejecutando viene del hecho de que la propiedad ProcessName del objeto del proceso parece estar limitado a 15 caracteres, por lo que los nombres de procesos más largos se truncan.
Una forma más segura de recuperar un nombre de proceso es obtener el nombre de archivo de su módulo principal y soltar la extensión. La siguiente rutina reutilizable utiliza este enfoque:

Function AppIsAlreadyRunning() As Boolean 
    ' get the filename of the main module 
    Dim moduleName As String = Process.GetCurrentProcess.MainModule.ModuleName 

    ' discard the extension to get the process name 
    Dim procName As String = System.IO.Path.GetFileNameWithoutExtension(moduleName) 

    ' return true if there are 2 or more processes with that name 
    If Process.GetProcessesByName(procName).Length > 1 Then 
     Return True 
    End If 
End Function 
1
// Allow running single instance 
    string processName = Process.GetCurrentProcess().ProcessName; 
    Process[] instances = Process.GetProcessesByName(processName); 

    if (instances.Length > 1) 
    { 
     MessageBox.Show("Application already Running", "Error 1001 - Application Running"); 
     return; 
    } 

aplicación de salida con gracia con el cuadro de mensaje como se muestra arriba si la aplicación ya está en ejecución

-3

Otra manera de hacerlo es enlazar a una dirección en la máquina local (como lo haría escucha TCP). Solo un proceso a la vez puede vincularse a una combinación de puerto/dirección. Así que elija un puerto en el adaptador de bucle invertido y hágalo.

Este tiene los bonitos efectos secundarios de:

  • de trabajo, incluso si alguien cambia el nombre del ejecutable
  • Restablecimiento sí cuando la aplicación se bloquea
  • La técnica es portable a través de otros sistemas operativos

En el lado negativo, puede fallar si hay otra aplicación que se una a ese puerto en particular.


Según lo solicitado, a continuación se incluye un código para enlazar a una dirección/puerto. Esto está arrancado de algo más. Está incompleto, pero los bits necesarios están aquí.

using System.Net; 
using System.Net.Sockets; 

[...] 

// Make up a number that's currently unused by you, or any 
// well-known service. i.e. 80 for http, 22 for ssh, etc.. 
int portNum = 2001; 

// This binds to any (meaning all) adapters on this system 
IPAddress ipAddress = IPAddress.Any; 
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNum); 
Socket listener = new Socket(AddressFamily.InterNetwork, 
    SocketType.Stream, ProtocolType.Tcp); 

// The next statement will throw an exception if anyone has done this Bind! 
listener.Bind(localEndPoint); 

Mientras escucha no es basura recogida (cae fuera de alcance) o el programa no termina: el puerto en que el adaptador es suya y sólo suya. Si algo le sucede a listener, entonces estará disponible para que otra persona lo use. A los efectos de un bloqueo, probablemente debería tener listener ser estático en alguna parte.

+0

¿Tiene algún código de muestra sobre cómo hacer esto? Además, ¿a qué te refieres con "Restablecerse cuando la aplicación falla"? –

+0

Vea el ejemplo anterior. –

+3

Este techinque es bastante frágil, la técnica mutex es mucho mejor. –

1

El uso de un objeto kernal es la única forma correcta de implementar la protección de instancia única en Windows.

Esta afirmación:

mutex = Mutex.OpenExisting ("SingleInstance");

no funcionará si alguien más copia esta línea de Stackoverflow y ejecuta su programa antes de su programa, ya que ese otro tipo tomó "SINGLEINSTANCE" antes que usted. Desea incluir un GUID en su nombre mutex:

mutex = Mutex.OpenExisting ("MyApp {AD52DAF0-C3CF-4cc7-9EDD-03812F82557E}");

Esta técnica evitará que el usuario actual ejecute más de una instancia de su programa, pero no impedirá que otro usuario lo haga.

Para asegurarse de que sólo una instancia de la aplicación se puede ejecutar en el equipo local, que tiene que hacer esto:

mutex = Mutex.OpenExisting ("Global \ MiApl {AD52DAF0-C3CF-4cc7-9EDD-03812F82557E } ");

Consulte la ayuda para la API CreateMutex.

25

La forma correcta de utilizar un mutex para este propósito:

private static Mutex mutex; 

static void Main() 
{ 
    // STEP 1: Create and/or check mutex existence in a race-free way 
    bool created; 
    mutex = new Mutex(false, "YourAppName-{add-your-random-chars}", out created); 
    if (!created) 
    { 
     MessageBox.Show("Another instance of this application is already running"); 
     return; 
    } 

    // STEP 2: Run whatever the app needs to do 
    Application.Run(new Form1()); 

    // No need to release the mutex because it was never acquired 
} 

Lo anterior no va a funcionar para detectar si varios usuarios en la misma máquina se están ejecutando la aplicación en diferentes cuentas de usuario.Un caso similar es donde un proceso puede ejecutar ambos bajo el host de servicio y independiente. Para realizar estos trabajos, crear la exclusión mutua de la siguiente manera:

 var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); 
     var mutexsecurity = new MutexSecurity(); 
     mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow)); 
     mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny)); 
     mutexsecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny)); 
     _mutex = new Mutex(false, "Global\\YourAppName-{add-your-random-chars}", out created, mutexsecurity); 

dos diferencias aquí - en primer lugar, la exclusión mutua tiene que ser creado con derechos de seguridad que permiten a otras cuentas de usuario para abrir/adquirirla. En segundo lugar, el nombre debe con el prefijo "Global" en el caso de los servicios que se ejecutan bajo el host de servicio (no estoy seguro de que otros usuarios se ejecuten localmente en la misma máquina).

+0

No está claro a partir de la pregunta original si "instancia anterior" significa en esta sesión, para este usuario o en esta máquina. Los nombres de Mutex son predeterminados por sesión, por lo que una solución por máquina debe usar un nombre como @ "Global \ YourAppName- {add-your-random-chars}" http://msdn.microsoft.com/en-us/library/ aa382954% 28VS.85% 29.aspx – adrianm

+0

@adrianm - Solo estaba publicando una edición con esa información :) –

+0

http://stackoverflow.com/questions/2415984/is-it-a-good-idea-to-use -la-existencia-de-un-nombre-mutex-como-un-indicador - mi pregunta para discutir si es apropiado simplemente verificar la existencia del mutex sin adquirirlo. –

Cuestiones relacionadas