En utilisant C # et WPF sous .NET (plutôt que Windows Forms ou la console), quelle est la bonne façon de créer une application qui ne peut être exécutée que comme une seule instance?
Je sais que cela a quelque chose à voir avec quelque chose mythique appelé mutex, rarement je peux trouver quelqu'un qui prend la peine de s'arrêter et d'expliquer ce que c'est.
Le code doit également informer l'instance déjà en cours d'exécution que l'utilisateur a tenté d'en démarrer une seconde, et peut-être également transmettre les arguments de ligne de commande, le cas échéant.
Réponses:
Voici un très bon article concernant la solution Mutex. L'approche décrite par l'article est avantageuse pour deux raisons.
Tout d'abord, il ne nécessite pas de dépendance sur l'assembly Microsoft.VisualBasic. Si mon projet dépendait déjà de cet assemblage, je recommanderais probablement d'utiliser l'approche indiquée dans une autre réponse . Mais en l'état, je n'utilise pas l'assembly Microsoft.VisualBasic et je préfère ne pas ajouter une dépendance inutile à mon projet.
Deuxièmement, l'article montre comment mettre l'instance existante de l'application au premier plan lorsque l'utilisateur essaie de démarrer une autre instance. C'est une très belle touche que les autres solutions Mutex décrites ici n'abordent pas.
MISE À JOUR
Au 8/1/2014, l'article auquel j'ai lié ci-dessus est toujours actif, mais le blog n'a pas été mis à jour depuis un certain temps. Cela me fait craindre qu’elle finisse par disparaître et, avec elle, la solution préconisée. Je reproduis le contenu de l'article ici pour la postérité. Les mots appartiennent uniquement au propriétaire du blog de Sanity Free Coding .
la source
Mutex
constructeur nécessite simplement une chaîne, vous pouvez donc fournir n'importe quel nom de chaîne que vous voulez, par exemple, "This Is My Mutex". Étant donné qu'un «Mutex» est un objet système disponible pour d'autres processus, vous souhaitez généralement que le nom soit unique afin qu'il ne se heurte pas aux autres noms de «Mutex» sur le même système. Dans l'article, la chaîne d'apparence cryptique est un «Guid». Vous pouvez générer cela par programmation en appelantSystem.Guid.NewGuid()
. Dans le cas de l'article, l'utilisateur l'a probablement généré via Visual Studio comme indiqué ici: msdn.microsoft.com/en-us/library/ms241442(VS.80).aspxVous pouvez utiliser la classe Mutex, mais vous découvrirez bientôt que vous devrez implémenter le code pour passer les arguments et autres vous-même. Eh bien, j'ai appris une astuce lors de la programmation dans WinForms en lisant le livre de Chris Sell . Cette astuce utilise une logique qui est déjà à notre disposition dans le cadre. Je ne sais pas pour vous, mais quand j'apprends des choses que je peux réutiliser dans le cadre, c'est généralement la voie que je prends au lieu de réinventer la roue. À moins bien sûr qu'il ne fasse pas tout ce que je veux.
Quand je suis entré dans WPF, j'ai trouvé un moyen d'utiliser ce même code, mais dans une application WPF. Cette solution devrait répondre à vos besoins en fonction de votre question.
Tout d'abord, nous devons créer notre classe d'application. Dans cette classe, nous allons remplacer l'événement OnStartup et créer une méthode appelée Activate, qui sera utilisée plus tard.
Deuxièmement, nous devrons créer une classe capable de gérer nos instances. Avant de passer par là, nous allons réellement réutiliser du code qui se trouve dans l'assembly Microsoft.VisualBasic. Depuis, j'utilise C # dans cet exemple, j'ai dû faire une référence à l'assembly. Si vous utilisez VB.NET, vous n'avez rien à faire. La classe que nous allons utiliser est WindowsFormsApplicationBase et en héritera notre gestionnaire d'instances, puis tirera parti des propriétés et des événements pour gérer l'instanciation unique.
Fondamentalement, nous utilisons les bits VB pour détecter les instances uniques et les traiter en conséquence. OnStartup sera déclenché lors du chargement de la première instance. OnStartupNextInstance est déclenché lorsque l'application est réexécutée. Comme vous pouvez le voir, je peux accéder à ce qui s'est passé sur la ligne de commande via les arguments d'événement. J'ai défini la valeur sur un champ d'instance. Vous pouvez analyser la ligne de commande ici ou la transmettre à votre application via le constructeur et l'appel à la méthode Activate.
Troisièmement, il est temps de créer notre EntryPoint. Au lieu de renouveler l'application comme vous le feriez normalement, nous allons profiter de notre SingleInstanceManager.
Eh bien, j'espère que vous pourrez tout suivre et pouvoir utiliser cette implémentation et vous l'approprier.
la source
D' ici .
Une utilisation courante d'un Mutex inter-processus est de garantir que seule une instance d'un programme peut s'exécuter à la fois. Voici comment procéder:
Une bonne caractéristique de Mutex est que si l'application se termine sans que ReleaseMutex soit appelé pour la première fois, le CLR libérera automatiquement Mutex.
la source
MSDN a en fait un exemple d'application pour C # et VB pour faire exactement cela: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx
'Another instance of the app is running. Bye'
ça, il ne sera pas un utilisateur très heureux. Vous DEVEZ simplement (dans une application GUI) basculer vers cette application et passer les arguments fournis - ou si les paramètres de ligne de commande n'ont pas de sens, vous devez faire apparaître l'application qui peut avoir été minimisée.Le cadre a déjà un support pour cela - c'est juste qu'un idiot a nommé la DLL
Microsoft.VisualBasic
et il n'a pas été inséréMicrosoft.ApplicationUtils
ou quelque chose comme ça. Dépassez-le - ou ouvrez Reflector.Astuce: Si vous utilisez cette approche exactement telle quelle, et que vous avez déjà un App.xaml avec des ressources, etc., vous voudrez également y jeter un œil .
la source
Ce code doit aller à la méthode principale. Regardez ici pour plus d'informations sur la méthode principale dans WPF.
Méthode 2
Remarque: Les méthodes ci-dessus supposent que votre processus / application a un nom unique. Parce qu'il utilise le nom du processus pour trouver des processeurs existants. Donc, si votre application a un nom très courant (par exemple: Bloc-notes), l'approche ci-dessus ne fonctionnera pas.
la source
ProcessName
renvoie le nom du fichier exécutable moins leexe
. Si vous créez une application appelée "Bloc-notes" et que le bloc-notes Windows est en cours d'exécution, il le détectera comme votre application en cours d'exécution.Eh bien, j'ai une classe jetable pour cela qui fonctionne facilement pour la plupart des cas d'utilisation:
Utilisez-le comme ceci:
C'est ici:
la source
WPF Single Instance Application est un nouveau qui utilise des éléments Mutex et IPC et qui transmet également tous les arguments de ligne de commande à l'instance en cours d'exécution .
la source
Le code C # .NET Single Instance Application qui est la référence pour la réponse marquée est un bon début.
Cependant, j'ai trouvé qu'il ne gère pas très bien les cas où l'instance qui existe déjà a une boîte de dialogue modale ouverte, que cette boîte de dialogue soit gérée (comme un autre formulaire tel qu'une boîte de dialogue), ou non gérée (comme le OpenFileDialog même lors de l'utilisation de la classe .NET standard). Avec le code d'origine, le formulaire principal est activé, mais le modal reste inactif, ce qui semble étrange, et l'utilisateur doit cliquer dessus pour continuer à utiliser l'application.
J'ai donc créé une classe utilitaire SingleInstance pour gérer tout cela de manière assez automatique pour les applications Winforms et WPF.
Winforms :
1) modifiez la classe Program comme ceci:
2) modifiez la classe de fenêtre principale comme ceci:
WPF:
1) modifiez la page de l'application comme ceci (et assurez-vous de définir son action de génération sur page pour pouvoir redéfinir la méthode Main):
2) modifiez la classe de fenêtre principale comme ceci:
Et voici la classe d'utilité:
la source
Voici un exemple qui vous permet d'avoir une seule instance d'une application. Lorsqu'une nouvelle instance se charge, elle transmet ses arguments à l'instance principale en cours d'exécution.
la source
Quelques réflexions: dans certains cas, une seule instance d'une application n'est pas "boiteuse", comme certains voudraient vous le faire croire. Les applications de base de données, etc. sont un ordre de grandeur plus difficile si l'on permet à plusieurs instances de l'application pour un seul utilisateur d'accéder à une base de données (vous savez, tout cela met à jour tous les enregistrements ouverts dans plusieurs instances de l'application sur les utilisateurs machine, etc.). Tout d'abord, pour la "collision de noms", n'utilisez pas de nom lisible par l'homme - utilisez plutôt un GUID ou, mieux encore, un GUID + le nom lisible par l'homme. Les chances de collision de nom sont tombées du radar et le Mutex s'en fiche . Comme quelqu'un l'a souligné, une attaque DOS serait nulle, mais si la personne malveillante a pris la peine d'obtenir le nom du mutex et de l'intégrer dans son application, vous êtes à peu près une cible de toute façon et devrez faire BEAUCOUP plus pour vous protéger que de simplement jouer un nom de mutex. De plus, si l'on utilise la variante de: nouveau Mutex (vrai, "certains GUID plus Nom", out AIsFirstInstance), vous avez déjà votre indicateur pour savoir si le Mutex est la première instance ou non.
la source
Autant de réponses à une question aussi simple en apparence. Juste pour secouer un peu les choses, voici ma solution à ce problème.
La création d'un Mutex peut être gênante car le JIT-er ne vous voit que l'utiliser pour une petite partie de votre code et veut le marquer comme prêt pour la collecte des ordures. Il veut à peu près vous surpasser en pensant que vous n'utiliserez pas ce Mutex aussi longtemps. En réalité, vous souhaitez conserver ce Mutex aussi longtemps que votre application est en cours d'exécution. La meilleure façon de dire au ramasseur de déchets de vous laisser Mutex seul est de lui dire de le garder en vie à travers les différentes générations de collecte de garage. Exemple:
J'ai levé l'idée de cette page: http://www.ai.uga.edu/~mc/SingleInstance.html
la source
Il semble qu'il existe un très bon moyen de gérer cela:
Application à instance unique WPF
Cela fournit une classe que vous pouvez ajouter qui gère tous les problèmes de mutex et de messagerie pour simplifier votre implémentation au point où elle est tout simplement triviale.
la source
Le code suivant est ma solution de canaux nommés WCF pour enregistrer une application à instance unique. C'est agréable car il déclenche également un événement lorsqu'une autre instance tente de démarrer et reçoit la ligne de commande de l'autre instance.
Il est orienté vers WPF car il utilise le
System.Windows.StartupEventHandler
classe, mais cela pourrait être facilement modifié.Ce code nécessite une référence à
PresentationFramework
, etSystem.ServiceModel
.Usage:
Code source:
la source
Vous ne devez jamais utiliser un mutex nommé pour implémenter une application à instance unique (ou du moins pas pour le code de production). Un code malveillant peut facilement DoS ( Denial of Service ) votre cul ...
la source
Regardez le code suivant. Il s'agit d'une solution simple et efficace pour empêcher plusieurs instances d'une application WPF.
la source
Voici ce que j'utilise. Il a combiné l'énumération des processus pour effectuer la commutation et le mutex pour se protéger des "cliqueurs actifs":
la source
J'ai trouvé la solution la plus simple, similaire à celle de Dale Ragan, mais légèrement modifiée. Il fait pratiquement tout ce dont vous avez besoin et basé sur la classe Microsoft WindowsFormsApplicationBase standard.
Tout d'abord, vous créez la classe SingleInstanceController, que vous pouvez utiliser dans toutes les autres applications à instance unique, qui utilisent Windows Forms:
Ensuite, vous pouvez l'utiliser dans votre programme comme suit:
Le programme et la solution SingleInstanceController_NET doivent référencer Microsoft.VisualBasic. Si vous souhaitez simplement réactiver l'application en cours d'exécution en tant que fenêtre normale lorsque l'utilisateur essaie de redémarrer le programme en cours d'exécution, le deuxième paramètre du SingleInstanceController peut être nul. Dans l'exemple donné, la fenêtre est agrandie.
la source
Mise à jour 2017-01-25. Après avoir essayé peu de choses, j'ai décidé d'aller avec VisualBasic.dll c'est plus facile et fonctionne mieux (au moins pour moi). Je laisse ma réponse précédente comme référence ...
À titre de référence, voici comment j'ai fait sans passer d'arguments (ce que je ne trouve aucune raison de le faire ... Je veux dire une seule application avec des arguments qui doivent être transmis d'une instance à une autre). Si l'association de fichiers est requise, une application doit (selon l'attente standard des utilisateurs) être instanciée pour chaque document. Si vous devez passer des arguments à une application existante, je pense que j'utiliserais la DLL vb.
Ne passant pas d'arguments (juste une application à instance unique), je préfère ne pas enregistrer un nouveau message Windows et ne pas remplacer la boucle de message telle que définie dans Matt Davis Solution. Bien que ce ne soit pas un gros problème d'ajouter une DLL VisualBasic, mais je préfère ne pas ajouter une nouvelle référence juste pour faire une application à instance unique. De plus, je préfère instancier une nouvelle classe avec Main au lieu d'appeler Shutdown depuis App.Startup override pour m'assurer de quitter le plus tôt possible.
En espérant que tout le monde l'appréciera ... ou inspirera un peu :-)
La classe de démarrage du projet doit être définie comme «SingleInstanceApp».
WindowHelper:
la source
Ne pas utiliser Mutex cependant, réponse simple:
Mettez-le à l'intérieur du
Program.Main()
.Exemple :
Vous pouvez ajouter
MessageBox.Show
à laif
déclaration et mettre "Application déjà en cours d'exécution".Cela pourrait être utile à quelqu'un.
la source
Les approches basées sur les mutex nommés ne sont pas multiplateformes car les mutex nommés ne sont pas globaux dans Mono. Les approches basées sur l'énumération des processus n'ont aucune synchronisation et peuvent entraîner un comportement incorrect (par exemple, plusieurs processus démarrés en même temps peuvent tous se terminer automatiquement en fonction du moment). Les approches basées sur le système de fenêtrage ne sont pas souhaitables dans une application console. Cette solution, basée sur la réponse de Divin, répond à tous ces problèmes:
la source
J'utilise Mutex dans ma solution pour empêcher plusieurs instances.
la source
Utilisez une solution mutex:
la source
Voici une solution légère que j'utilise qui permet à l'application de mettre au premier plan une fenêtre déjà existante sans avoir recours à des messages Windows personnalisés ni à rechercher aveuglément des noms de processus.
Edit: Vous pouvez également stocker et initialiser le mutex et createdNew statiquement, mais vous devrez explicitement éliminer / libérer le mutex une fois que vous en aurez fini. Personnellement, je préfère garder le mutex local car il sera automatiquement éliminé même si l'application se ferme sans jamais atteindre la fin de Main.
la source
Vous pouvez également utiliser le Runtime CodeFluent qui est un ensemble gratuit d'outils. Il fournit une classe SingleInstance pour implémenter une application à instance unique.
la source
J'ai ajouté une méthode sendMessage à la classe NativeMethods.
Apparemment, la méthode post-message fonctionne correctement, si l'application n'est pas affichée dans la barre des tâches, cependant l'utilisation de la méthode sendmessage résout ce problème.
la source
Voici la même chose implémentée via Event.
la source
[J'ai fourni un exemple de code pour les applications console et wpf ci-dessous.]
Il vous suffit de vérifier la valeur du
createdNew
variable (exemple ci-dessous!), Après avoir créé l'instance Mutex nommée.Le booléen
createdNew
retournera false:Le booléen
createdNew
retournera vrai:Application console - Exemple:
Exemple WPF:
la source
Une solution de gain de temps pour C # Winforms ...
Program.cs:
la source
Veuillez vérifier la solution proposée à partir d' ici qui utilise un sémaphore pour déterminer si une instance existante est déjà en cours d'exécution, fonctionne pour une application WPF et peut passer des arguments de la deuxième instance à la première instance déjà en cours d'exécution en utilisant un TcpListener et un TcpClient:
Il fonctionne également pour .NET Core, pas seulement pour .NET Framework.
la source
Je ne trouve pas de solution courte ici, donc j'espère que quelqu'un aimera ceci:
MISE À JOUR 2018-09-20
Mettez ce code dans votre
Program.cs
:la source