In .NET, qual è il modo migliore per impedire l'esecuzione contemporanea di più istanze di un'app? E se non esiste una tecnica "migliore", quali sono alcuni degli avvertimenti da considerare con ciascuna soluzione?
In .NET, qual è il modo migliore per impedire l'esecuzione contemporanea di più istanze di un'app? E se non esiste una tecnica "migliore", quali sono alcuni degli avvertimenti da considerare con ciascuna soluzione?
Risposte:
Usa Mutex. Uno degli esempi precedenti utilizzando GetProcessByName ha molti avvertimenti. Ecco un buon articolo sull'argomento:
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";
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;che otterrà il guid dell'assembly in esecuzione
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;
}
Ecco il codice di cui hai bisogno per assicurarti che sia in esecuzione una sola istanza. Questo è il metodo per utilizzare un mutex denominato.
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();
}
}
}
Hanselman ha un post sull'utilizzo della classe WinFormsApplicationBase dall'assembly Microsoft.VisualBasic per eseguire questa operazione.
Sembra che ci siano 3 tecniche fondamentali che sono state suggerite finora.
Qualche avvertimento che ho perso?
1 - Crea un riferimento in program.cs ->
using System.Diagnostics;
2 - Metti in void Main()come prima riga di codice ->
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length >1)
return;
Questo è tutto.
Mutex? C'è un problema?
Utilizzando Visual Studio 2005 o 2008 quando crei un progetto per un eseguibile, nelle finestre delle proprietà all'interno del pannello "Applicazione" è presente una casella di controllo denominata "Crea applicazione a istanza singola" che puoi attivare per convertire l'applicazione in un'applicazione a istanza singola .
Ecco un'acquisizione della finestra di cui sto parlando:
Questo è un progetto di applicazione Windows di Visual Studio 2008.
Ho provato tutte le soluzioni qui e niente ha funzionato nel mio progetto C # .net 4.0. Spero di aiutare qualcuno qui la soluzione che ha funzionato per me:
Come variabili di classe principali:
private static string appGuid = "WRITE AN UNIQUE GUID HERE";
private static Mutex mutex;
Quando è necessario verificare se l'app è già in esecuzione:
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
}
Avevo bisogno di chiudere altre istanze solo con alcune condizioni, questo ha funzionato bene per il mio scopo
http://en.csharp-online.net/Application_Architecture_in_Windows_Forms_2.0 —Single-Instance_Detection_and_Management
Dopo aver provato più soluzioni ho la domanda. Ho finito per usare l'esempio per WPF qui: 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();
}
}
}
In App.xaml:
x:Class="*YourNameSpace*.App"
StartupUri="MainWindow.xaml"
Startup="App_Startup"
Questo articolo spiega semplicemente come creare un'applicazione Windows con controllo sul numero delle sue istanze o come eseguire solo una singola istanza. Questa è un'esigenza molto tipica di un'applicazione aziendale. Esistono già molte altre possibili soluzioni per controllare questo problema.
http://www.openwinforms.com/single_instance_application.html
Questo è il codice per 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
Questo è il codice per 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());
}
}
Devi usare System.Diagnostics.Process.
Controlla: http://www.devx.com/tips/Tip/20044
(Nota: questa è una soluzione divertente! Funziona ma utilizza un cattivo design GDI + per raggiungere questo obiettivo.)
Inserisci un'immagine nella tua app e caricala all'avvio. Tienilo premuto fino alla chiusura dell'app. L'utente non sarà in grado di avviare una seconda istanza. (Ovviamente la soluzione mutex è molto più pulita)
private static Bitmap randomName = new Bitmap("my_image.jpg");
Main()metodo che va contro il modo in cui dovrebbe funzionare WPF.
[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!
}
Da: garantire una singola istanza dell'applicazione .NET
e: Mutex dell'applicazione a istanza singola
Stessa risposta di @Smink e @Imjustpondering con una svolta:
Domande frequenti di Jon Skeet su C # per scoprire perché GC.KeepAlive è importante
Semplicemente usando un StreamWriter, che ne dici di questo?
System.IO.File.StreamWriter OpenFlag = null; //globally
e
try
{
OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
Environment.Exit(0);
}
Normalmente è fatto con un Mutex denominato (usa il nuovo Mutex ("nome della tua app", true) e controlla il valore restituito), ma ci sono anche alcune classi di supporto in Microsoft.VisualBasic.dll che possono farlo per te .
Questo ha funzionato per me in puro C #. il try / catch è quando forse un processo nell'elenco esce durante il tuo 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());
}
Assicurati di considerare la sicurezza quando limiti un'applicazione a una singola istanza:
Articolo completo: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813
Stiamo utilizzando un mutex denominato con un nome fisso per rilevare se è in esecuzione un'altra copia del programma. Ma ciò significa anche che un utente malintenzionato può creare prima il mutex, impedendo in tal modo l'esecuzione del nostro programma! Come posso prevenire questo tipo di attacco Denial of Service?
...
Se l'aggressore è in esecuzione nello stesso contesto di sicurezza in cui è (o sarebbe) in esecuzione il tuo programma, non c'è niente che puoi fare. Qualunque sia la "stretta di mano segreta" che si crea per determinare se è in esecuzione un'altra copia del programma, l'aggressore può imitarla. Dal momento che è in esecuzione nel contesto di sicurezza corretto, può fare tutto ciò che il programma "reale" può fare.
...
Chiaramente non puoi proteggerti da un utente malintenzionato in esecuzione con lo stesso privilegio di sicurezza, ma puoi comunque proteggerti da aggressori senza privilegi che eseguono altri privilegi di sicurezza.
Prova a impostare un DACL sul tuo mutex, ecco il modo .NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx
Nessuna di queste risposte ha funzionato per me perché avevo bisogno che funzionasse sotto Linux usando monodevelop. Questo funziona alla grande per me:
Chiama questo metodo passandogli un ID univoco
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);
}
}