Empêcher plusieurs instances d'une application donnée dans .NET?

123

Dans .NET, quel est le meilleur moyen d'empêcher plusieurs instances d'une application de s'exécuter en même temps? Et s'il n'y a pas de «meilleure» technique, quelles sont certaines des mises en garde à prendre en compte avec chaque solution?

C. Dragon 76
la source

Réponses:

151

Utilisez Mutex. L'un des exemples ci-dessus utilisant GetProcessByName comporte de nombreuses mises en garde. Voici un bon article sur le sujet:

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
la source
1
L'utilisation d'un mutex fonctionne également pour le code non .net (bien que la syntaxe varie)
crashmstr
9
Voici une version un peu plus complète, avec quelques bons commentaires: stackoverflow.com/questions/229565/...
Richard Watson
2
@ClarkKent: Juste une chaîne aléatoire pour que le nom Mutex n'entre pas en collision avec le nom d'une autre application.
jgauffin
Cela peut-il également se limiter à un certain nombre d'instances?
Alejandro del Río
4
Afin d'avoir un meilleur contrôle lors de la gestion des versions ou de la modification de votre guide d'application, vous pouvez utiliser: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;qui obtiendra le guide de l'assembly en cours d'exécution
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
la source
1
Je pense que cela devrait voir ceci: odetocode.com/blogs/scott/archive/2004/08/20/…
SubmarineX
@SubmarineX Comment êtes-vous sûr que cet article est correct? c'est toujours une controverse entre mutex et codeproject.com/Articles/4975/…
John Nguyen
Incroyable Juste parfait merci pour votre temps et vos efforts
Ahmed Mahmoud
2
-1 Bien que ce soit la solution rapide, il est également très facile de contourner un.exe pourrait être renommé en b.exe et les deux fonctionneront. + d'autres cas où cela ne fonctionnera tout simplement pas comme prévu. Utiliser avec précaution!
Lars Nielsen
Cela ne fonctionne pas avec mono-develop. Sous les environnements Windows, cela fonctionne très bien.
Tono Nam
20

Voici le code dont vous avez besoin pour vous assurer qu'une seule instance est en cours d'exécution. C'est la méthode d'utilisation d'un mutex nommé.

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
la source
2
Pour une application WPF, utilisez Application.Current.Shutdown (); Cette méthode fonctionne comme un charme. Merci Terrapin.
Jeff
Ici, l'essentiel est de rendre le mutex statique. De même que dans le cas contraire, GC le récupérera.
Oleksii
J'ai apprécié la simplicité et la clarté du nom Mutex. Ce code est concis et efficace.
Chad
7

Hanselman a publié un article sur l'utilisation de la classe WinFormsApplicationBase de l'assembly Microsoft.VisualBasic pour ce faire.

bdukes
la source
J'utilise cela depuis quelques années, mais je cherche maintenant à passer à une solution basée sur Mutex. J'ai des clients qui signalent des problèmes avec cela et je soupçonne qu'il utilise Remoting pour le faire.
Richard Watson
5

Il semble que 3 techniques fondamentales ont été suggérées jusqu'à présent.

  1. Dérivez de la classe Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase et définissez la propriété IsSingleInstance sur true. (Je pense qu'une mise en garde ici est que cela ne fonctionnera pas avec les applications WPF, n'est-ce pas?)
  2. Utilisez un mutex nommé et vérifiez s'il a déjà été créé.
  3. Obtenez une liste des processus en cours d'exécution et comparez les noms des processus. (Cela a pour inconvénient d'exiger que le nom de votre processus soit unique par rapport à tout autre processus en cours d'exécution sur la machine d'un utilisateur donné.)

Des mises en garde que j'ai manquées?

C. Dragon 76
la source
3
Je ne pense pas que 3 soit très efficace. Je voterais pour le Mutex, je l'utilisais sans problème plusieurs fois. Je n'ai jamais utilisé l'élément 1 je ne sais pas comment cela vole lorsque vous êtes en c #.
typemismatch
2
L'option 1 fonctionne toujours avec WPF, elle est juste un peu plus impliquée. msdn.microsoft.com/en-us/library/ms771662.aspx
Graeme Bradbury
5

1 - Créer une référence dans program.cs ->

using System.Diagnostics;

2 - Mettre en void Main()première ligne de code ->

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

C'est tout.

user5289350
la source
Quelle est la différence avec cela Mutex? Y a-t-il un hic?
Hélicoptère d'attaque Harambe
Il utilise le nom du processus. Si le nom est répété, cela générerait un faux drapeau. Dans tous les autres cas, c'est plus propre que le mutex
magallanes le
4

À l'aide de Visual Studio 2005 ou 2008, lorsque vous créez un projet pour un exécutable, dans les fenêtres de propriétés du panneau "Application", il y a une case à cocher intitulée "Créer une application d'instance unique" que vous pouvez activer pour convertir l'application sur une application à instance unique .

Voici une capture de la fenêtre dont je parle: entrez la description de l'image ici Il s'agit d'un projet d'application Windows Visual Studio 2008.

Doliveras
la source
3
J'ai cherché cette case à cocher que vous mentionnez, pour mon application C # / WPF, et il n'y en a pas.
HappyNomad
3
Je ne le vois pas non plus dans les propriétés de mon application VS 2008 C # / WinForms.
Jesse McGrew
Pas dans VS2005 également. Il doit mentionner l'ancien studio VB.
nawfal
Oui, l'option existe, j'ai modifié le post pour ajouter une capture de la fenêtre dans laquelle vous pouvez trouver cette option.
Doliveras
6
L'option existe uniquement pour l'application VB.NET, pas pour C #. Apparemment, l'option elle-même utilise la classe WinFormsApplicationBase de l'assembly Microsoft.VisualBasic.
amolbk
4

J'ai essayé toutes les solutions ici et rien n'a fonctionné dans mon projet C # .net 4.0. En espérant aider quelqu'un ici la solution qui a fonctionné pour moi:

En tant que variables de classe principales:

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

Lorsque vous devez vérifier si l'application est déjà en cours d'exécution:

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
}

J'avais besoin de fermer d'autres istances uniquement avec certaines conditions, cela a bien fonctionné pour mon objectif

HypeZ
la source
Vous n'avez pas besoin d'acquérir le mutex et de le libérer. Tout ce que vous devez savoir est si une autre application a déjà créé l'objet (c'est-à-dire, son nombre de références de noyau> = 1).
Michael Goldshteyn
3

Après avoir essayé plusieurs solutions, je pose la question. J'ai fini par utiliser l'exemple pour WPF ici: 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();  
        }  

    }          
}  

Dans App.xaml:

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

Cet article explique simplement comment vous pouvez créer une application Windows avec un contrôle sur le nombre de ses instances ou exécuter une seule instance. Il s'agit d'un besoin très typique d'une application métier. Il existe déjà de nombreuses autres solutions possibles pour contrôler cela.

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

Jorge Ferreira
la source
Le lien est rompu.
Chad
2

Utilisez VB.NET! Pas vraiment ;)

en utilisant Microsoft.VisualBasic.ApplicationServices;

Le WindowsFormsApplicationBase de VB.Net vous fournit une propriété "SingleInstace", qui détermine d'autres instances et ne laisse qu'une seule instance s'exécuter.

MADMap
la source
2

Ceci est le code pour 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

Ceci est le code pour 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
la source
1

(Remarque: c'est une solution amusante! Cela fonctionne mais utilise une mauvaise conception GDI + pour y parvenir.)

Mettez une image avec votre application et chargez-la au démarrage. Maintenez-le enfoncé jusqu'à ce que l'application se ferme. L'utilisateur ne pourra pas démarrer une deuxième instance. (Bien sûr, la solution mutex est beaucoup plus propre)

private static Bitmap randomName = new Bitmap("my_image.jpg");
Bleu amer
la source
C'est en fait génial dans sa simplicité et cela fonctionne avec à peu près n'importe quel type de fichier et pas seulement des images. J'ai l'impression que la solution Mutex est loin d'être «propre». C'est extrêmement compliqué et il y a apparemment de nombreuses façons d'échouer parce qu'il ne le fait pas «correctement». Cela nécessite également une Main()méthode qui va à l'encontre de la façon dont WPF est censé fonctionner.
Kyle Delaney
C'est un peu comme l'utilisation d'un bug. Cela fonctionne mais ce n'est pas fait dans ce but. Je ne l'utiliserais pas en tant que professionnel.
Bitterblue
Ouais, c'est dommage que nous n'ayons pas de solution aussi efficace et simple sans compter sur des exceptions.
Kyle Delaney
Bien que ce ne soit pas exactement un bug. C'est toujours .NET qui se comporte comme prévu.
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: Assurer une seule instance de l'application .NET

et: Mutex d'application à instance unique

Même réponse que @Smink et @Imjustpondering avec une torsion:

FAQ de Jon Skeet sur C # pour découvrir pourquoi GC.KeepAlive est important

Ric Tokyo
la source
-1, car vous ne parvenez pas à utiliser un bloc using avec le mutex, ce qui rendrait KeepAlive superflu. Et oui, je pense que John Skeet s'est trompé. Il n'explique pas pourquoi il serait faux de se débarrasser du mutex dans ce cas.
1

En utilisant simplement un StreamWriter, que diriez-vous de cela?

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

et

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

Normalement, cela est fait avec un Mutex nommé (utilisez le nouveau Mutex ("votre nom d'application", true) et vérifiez la valeur de retour), mais il existe également des classes de support dans Microsoft.VisualBasic.dll qui peuvent le faire pour vous .

Tomer Gabel
la source
0

Cela a fonctionné pour moi en pur C #. le try / catch se produit lorsqu'un processus de la liste se termine pendant votre boucle.

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
la source
0

Veillez à prendre en compte la sécurité lors de la restriction d'une application à une seule instance:

Article complet: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813

Nous utilisons un mutex nommé avec un nom fixe afin de détecter si une autre copie du programme est en cours d'exécution. Mais cela signifie également qu'un attaquant peut créer le mutex en premier, empêchant ainsi notre programme de s'exécuter du tout! Comment puis-je empêcher ce type d'attaque par déni de service?

...

Si l'attaquant s'exécute dans le même contexte de sécurité que votre programme est (ou serait) en cours d'exécution, alors vous ne pouvez rien faire. Quelle que soit la "poignée de main secrète" que vous proposez pour déterminer si une autre copie de votre programme est en cours d'exécution, l'attaquant peut l'imiter. Puisqu'il s'exécute dans le bon contexte de sécurité, il peut faire tout ce que le «vrai» programme peut faire.

...

Il est clair que vous ne pouvez pas vous protéger contre un attaquant exécutant le même privilège de sécurité, mais vous pouvez toujours vous protéger contre des attaquants non privilégiés exécutant d'autres privilèges de sécurité.

Essayez de définir un DACL sur votre mutex, voici la méthode .NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx

Aardvark
la source
0

Aucune de ces réponses n'a fonctionné pour moi car j'avais besoin de cela pour fonctionner sous Linux en utilisant monodevelop. Cela fonctionne très bien pour moi:

Appelez cette méthode en lui passant un identifiant unique

    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
la source