¿Prevenir múltiples instancias de una aplicación determinada en .NET?

123

En .NET, ¿cuál es la mejor manera de evitar que se ejecuten varias instancias de una aplicación al mismo tiempo? Y si no hay una "mejor" técnica, ¿cuáles son algunas de las advertencias a considerar con cada solución?

C. Dragon 76
fuente

Respuestas:

151

Utiliza Mutex. Uno de los ejemplos anteriores que usa GetProcessByName tiene muchas advertencias. Aquí hay un buen artículo sobre el tema:

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
fuente
1
El uso de un mutex también funciona para código que no sea .net (aunque la sintaxis podría variar)
crashmstr
8
Aquí hay una versión un poco más completa, con algunos buenos comentarios: stackoverflow.com/questions/229565/…
Richard Watson
2
@ClarkKent: solo una cadena aleatoria para que el nombre Mutex no choque con el nombre de otra aplicación.
jgauffin
¿Puede esto restringirse a un cierto número de instancias también?
Alejandro del Río
3
Para tener un mejor control mientras versionas o cambias el guid de tu aplicación, puedes usar: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;que obtendrá el guid del ensamblaje ejecutor
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
fuente
1
Creo que debería ver esto: odetocode.com/blogs/scott/archive/2004/08/20/…
SubmarineX
@SubmarineX ¿Cómo está seguro de que este artículo es correcto? sigue siendo controversia entre mutex y codeproject.com/Articles/4975/…
John Nguyen
Increíble Simplemente perfecto gracias por su tiempo y esfuerzo
Ahmed Mahmoud
2
-1 Si bien esta es la solución rápida, también es muy fácil evitar que a.exe se pueda renombrar a b.exe y ambos se ejecutarán. + otros casos en los que esto simplemente no funcionará según lo previsto. ¡Usar con precaución!
Lars Nielsen
Esto no funciona con monodesarrollo. Sin embargo, en entornos Windows funciona muy bien.
Tono Nam
20

Aquí está el código que necesita para asegurarse de que solo se esté ejecutando una instancia. Este es el método de usar un mutex con nombre.

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
fuente
2
Para una aplicación WPF, use Application.Current.Shutdown (); Este método funciona a las mil maravillas. Gracias Terrapin.
Jeff
Aquí lo principal es hacer mutex estático. Así como en el otro caso, GC lo recogerá.
Oleksii
Disfruté de la simplicidad y claridad del Mutex nombrado. Este código es conciso y efectivo.
Chad
7

Hanselman tiene una publicación sobre el uso de la clase WinFormsApplicationBase del ensamblado Microsoft.VisualBasic para hacer esto.

bdukes
fuente
Lo he estado usando durante un par de años, pero ahora estoy buscando cambiar a una solución basada en Mutex. Tengo clientes que informan problemas con esto y sospecho que está usando Remoting para hacerlo.
Richard Watson
5

Parece que hay 3 técnicas fundamentales que se han sugerido hasta ahora.

  1. Derive de la clase Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase y establezca la propiedad IsSingleInstance en true. (Creo que una advertencia aquí es que esto no funcionará con las aplicaciones WPF, ¿verdad?)
  2. Use un mutex con nombre y verifique si ya se ha creado.
  3. Obtenga una lista de los procesos en ejecución y compare los nombres de los procesos. (Esto tiene la advertencia de requerir que el nombre de su proceso sea único en relación con cualquier otro proceso que se ejecute en la máquina de un usuario determinado).

¿Alguna advertencia que me haya perdido?

C. Dragon 76
fuente
3
No creo que 3 sea muy eficiente. Yo votaría por el Mutex, lo usé sin problemas muchas veces. Nunca he usado el elemento 1, no estoy seguro de cómo vuela cuando estás en C #.
typemismatch
2
La opción 1 todavía funciona con WPF, es un poco más complicada. msdn.microsoft.com/en-us/library/ms771662.aspx
Graeme Bradbury
5

1 - Crear una referencia en program.cs ->

using System.Diagnostics;

2 - Poner en void Main()como la primera línea de código ->

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

Eso es.

usuario5289350
fuente
¿Cuál es la diferencia de esto Mutex? ¿Hay alguna trampa?
Helicóptero de ataque Harambe
Está usando el nombre del proceso. Si el nombre se repite, generaría una bandera falsa. En cualquier otro caso, es más limpio que mutex
magallanes
4

Usando Visual Studio 2005 o 2008 cuando crea un proyecto para un ejecutable, en las ventanas de propiedades dentro del panel "Aplicación" hay una casilla de verificación llamada "Crear aplicación de instancia única" que puede activar para convertir la aplicación en una aplicación de instancia única .

Aquí hay una captura de la ventana de la que estoy hablando: ingrese la descripción de la imagen aquí Este es un proyecto de aplicación de Windows de Visual Studio 2008.

Doliveras
fuente
3
Busqué esta casilla de verificación que mencionas para mi aplicación C # / WPF, y no hay ninguna.
HappyNomad
3
Tampoco lo veo en las propiedades de mi aplicación VS 2008 C # / WinForms.
Jesse McGrew
No en VS2005 también. Debe mencionar el antiguo estudio de VB.
nawfal
Sí, la opción existe, modifiqué la publicación para agregar una captura de la ventana en la que puede encontrar esta opción.
Doliveras
66
La opción existe solo para la aplicación VB.NET, no para C #. Aparentemente, la opción en sí usa la clase WinFormsApplicationBase del ensamblado Microsoft.VisualBasic.
amolbk
4

Probé todas las soluciones aquí y nada funcionó en mi proyecto C # .net 4.0. Con la esperanza de ayudar a alguien aquí, la solución que funcionó para mí:

Como principales variables de clase:

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

Cuando necesite verificar si la aplicación ya se está ejecutando:

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
}

Necesitaba cerrar otras istances solo con algunas condiciones, esto funcionó bien para mi propósito

HypeZ
fuente
No necesita adquirir realmente el mutex y liberarlo. Todo lo que necesita saber es si otra aplicación ya ha creado el objeto (es decir, su número de referencia del núcleo> = 1).
Michael Goldshteyn
3

Después de probar varias soluciones, la pregunta. Terminé usando el ejemplo para WPF aquí: 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();  
        }  

    }          
}  

En App.xaml:

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

Este artículo simplemente explica cómo puede crear una aplicación de Windows con control sobre el número de instancias o ejecutar solo una instancia. Esta es una necesidad muy típica de una aplicación comercial. Ya hay muchas otras posibles soluciones para controlar esto.

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

Jorge Ferreira
fuente
El enlace está roto.
Chad
2

Use VB.NET! No realmente ;)

usando Microsoft.VisualBasic.ApplicationServices;

WindowsFormsApplicationBase de VB.Net le proporciona una propiedad "SingleInstace", que determina otras instancias y permite que solo se ejecute una instancia.

MADMapa
fuente
2

Este es el código para 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

Este es el código para 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
fuente
1

(Nota: ¡esta es una solución divertida! Funciona pero utiliza un mal diseño GDI + para lograrlo).

Coloque una imagen con su aplicación y cárguela al inicio. Sosténgalo hasta que salga la aplicación. El usuario no podrá iniciar una segunda instancia. (Por supuesto, la solución mutex es mucho más limpia)

private static Bitmap randomName = new Bitmap("my_image.jpg");
Azul amargo
fuente
Esto es realmente brillante en su simplicidad, y funciona con casi cualquier tipo de archivo y no solo con imágenes. Siento que la solución Mutex está lejos de ser "limpia". Es extremadamente complicado y aparentemente hay muchas formas de que falle por no hacerlo "bien". También requiere un Main()método que vaya en contra de cómo se supone que funciona WPF.
Kyle Delaney
Esto es algo así como el uso de errores. Funciona pero no está hecho para ese propósito. No lo usaría como profesional.
Bitterblue
Sí, es lamentable que no tengamos una solución tan efectiva y simple sin depender de excepciones.
Kyle Delaney
Aunque no es exactamente un error. Sigue siendo .NET comportándose según lo previsto.
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!
}

De: Asegurar una única instancia de aplicación .NET

y: Aplicación de instancia única Mutex

Misma respuesta que @Smink y @Imjustpondering con un toque:

Preguntas frecuentes de Jon Skeet sobre C # para descubrir por qué GC.KeepAlive es importante

Ric Tokyo
fuente
-1, porque no puede usar un bloque de uso con el mutex, lo que haría que KeepAlive sea superfluo. Y sí, creo que John Skeet se equivocó. No explica por qué sería un error deshacerse del mutex en este caso.
1

Simplemente usando a StreamWriter, ¿qué tal esto?

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

y

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

Normalmente se realiza con un Mutex con nombre (use un nuevo Mutex ("nombre de su aplicación", verdadero) y verifique el valor de retorno), pero también hay algunas clases de soporte en Microsoft.VisualBasic.dll que pueden hacerlo por usted .

Tomer Gabel
fuente
0

Esto funcionó para mí en C # puro. el intento / captura es cuando posiblemente un proceso en la lista salga durante su ciclo.

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
fuente
0

Asegúrese de tener en cuenta la seguridad al restringir una aplicación a una sola instancia:

Artículo completo: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

Estamos utilizando un mutex con nombre con un nombre fijo para detectar si se está ejecutando otra copia del programa. Pero eso también significa que un atacante puede crear el mutex primero, ¡evitando así que nuestro programa se ejecute! ¿Cómo puedo evitar este tipo de ataque de denegación de servicio?

...

Si el atacante se ejecuta en el mismo contexto de seguridad en el que se ejecuta (o estaría) su programa, entonces no hay nada que pueda hacer. Cualquiera sea el "apretón de manos secreto" que se le ocurra para determinar si se está ejecutando otra copia de su programa, el atacante puede imitarlo. Como se ejecuta en el contexto de seguridad correcto, puede hacer cualquier cosa que el programa "real" pueda hacer.

...

Claramente, no puede protegerse de un atacante que se ejecuta con el mismo privilegio de seguridad, pero aún puede protegerse contra los atacantes no privilegiados que se ejecutan con otros privilegios de seguridad.

Intente configurar un DACL en su mutex, aquí está la forma .NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx

Cerdo hormiguero
fuente
0

Ninguna de estas respuestas funcionó para mí porque necesitaba que esto funcionara bajo Linux usando monodevelop. Esto funciona muy bien para mí:

Llame a este método pasándole una identificación única

    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
fuente