Ich möchte sicherstellen, dass dies da draußen ist, weil es so schwer ist, es richtig zu machen:
using System.Runtime.InteropServices; //GuidAttribute
using System.Reflection; //Assembly
using System.Threading; //Mutex
using System.Security.AccessControl; //MutexAccessRule
using System.Security.Principal; //SecurityIdentifier
static void Main(string[] args)
{
// get application GUID as defined in AssemblyInfo.cs
string appGuid =
((GuidAttribute)Assembly.GetExecutingAssembly().
GetCustomAttributes(typeof(GuidAttribute), false).
GetValue(0)).Value.ToString();
// unique id for global mutex - Global prefix means it is global to the machine
string mutexId = string.Format( "Global\\{{{0}}}", appGuid );
// Need a place to store a return value in Mutex() constructor call
bool createdNew;
// edited by Jeremy Wiebe to add example of setting up security for multi-user usage
// edited by 'Marc' to work also on localized systems (don't use just "Everyone")
var allowEveryoneRule =
new MutexAccessRule( new SecurityIdentifier( WellKnownSidType.WorldSid
, null)
, MutexRights.FullControl
, AccessControlType.Allow
);
var securitySettings = new MutexSecurity();
securitySettings.AddAccessRule(allowEveryoneRule);
// edited by MasonGZhwiti to prevent race condition on security settings via VanNguyen
using (var mutex = new Mutex(false, mutexId, out createdNew, securitySettings))
{
// edited by acidzombie24
var hasHandle = false;
try
{
try
{
// note, you may want to time out here instead of waiting forever
// edited by acidzombie24
// mutex.WaitOne(Timeout.Infinite, false);
hasHandle = mutex.WaitOne(5000, false);
if (hasHandle == false)
throw new TimeoutException("Timeout waiting for exclusive access");
}
catch (AbandonedMutexException)
{
// Log the fact that the mutex was abandoned in another process,
// it will still get acquired
hasHandle = true;
}
// Perform your work here.
}
finally
{
// edited by acidzombie24, added if statement
if(hasHandle)
mutex.ReleaseMutex();
}
}
}
using
das ÜberprüfencreatedNew
und Hinzufügen immutex.Dispose()
Inneren weglassenfinally
. Ich kann es nicht eindeutig erklären (ich weiß nicht den Grund) im Augenblick , aber ich habe mich in eine Situation geraten , wennmutex.WaitOne
wiedertrue
nachcreatedNew
wurdefalse
(I erwerben die Mutex im aktuellenAppDomain
und geladen dann ein neuenAppDomain
und der gleichen Code ausgeführt von darin).exitContext = false
etwas inmutex.WaitOne(5000, false)
? Es sieht aus wie es nur zu einer Assertion in CoreCLR verursachen könnte , 2. Wenn jemand fragt sich , inMutex
‚s - Konstruktor der Grund, waruminitiallyOwned
istfalse
erklärt teilweise von diesem MSDN - Artikel .Mit der akzeptierten Antwort erstelle ich eine Hilfsklasse, damit Sie sie auf ähnliche Weise wie mit der Lock-Anweisung verwenden können. Ich dachte nur, ich würde teilen.
Verwenden:
Und die Helferklasse:
quelle
< 0
statt<= 0
._mutex.Close()
anstelle_mutex.Dispose()
der Dispose-Methode für mich funktioniert. Der Fehler wurde durch den Versuch verursacht, das zugrunde liegende WaitHandle zu entsorgen.Mutex.Close()
verfügt über die zugrunde liegenden Ressourcen.Die akzeptierte Antwort enthält eine Race-Bedingung, wenn 2 Prozesse unter 2 verschiedenen Benutzern ausgeführt werden, die gleichzeitig versuchen, den Mutex zu initialisieren. Nachdem der erste Prozess den Mutex initialisiert hat und der zweite Prozess versucht, den Mutex zu initialisieren, bevor der erste Prozess die Zugriffsregel für alle festlegt, wird vom zweiten Prozess eine nicht autorisierte Ausnahme ausgelöst.
Siehe unten für korrigierte Antwort:
quelle
Dieses Beispiel wird nach 5 Sekunden beendet, wenn bereits eine andere Instanz ausgeführt wird.
quelle
Weder Mutex noch WinApi CreateMutex () funktionieren bei mir.
Eine alternative Lösung:
Und die
SingleApplicationDetector
:Grund für die Verwendung von Semaphore anstelle von Mutex:
Ref: Semaphore.OpenExisting ()
quelle
Semaphore.OpenExisting
undnew Semaphore
.Manchmal hilft das Lernen am Beispiel am meisten. Führen Sie diese Konsolenanwendung in drei verschiedenen Konsolenfenstern aus. Sie werden sehen, dass die Anwendung, die Sie zuerst ausgeführt haben, zuerst den Mutex erhält, während die anderen beiden darauf warten, dass sie an die Reihe kommen. Drücken Sie dann in der ersten Anwendung die Eingabetaste. Sie werden sehen, dass Anwendung 2 jetzt weiter ausgeführt wird, indem Sie den Mutex abrufen. Anwendung 3 wartet jedoch darauf, dass sie an die Reihe kommt. Nachdem Sie in Anwendung 2 die Eingabetaste gedrückt haben, wird Anwendung 3 fortgesetzt. Dies veranschaulicht das Konzept eines Mutex, der einen Codeabschnitt schützt, der nur von einem Thread (in diesem Fall einem Prozess) ausgeführt werden soll, wie beispielsweise das Schreiben in eine Datei.
quelle
Ein globaler Mutex soll nicht nur sicherstellen, dass nur eine Instanz einer Anwendung vorhanden ist. Ich persönlich bevorzuge die Verwendung von Microsoft.VisualBasic, um sicherzustellen, dass eine Einzelinstanzanwendung wie unter Was ist der richtige Weg zum Erstellen einer Einzelinstanz-WPF-Anwendung beschrieben?(Antwort von Dale Ragan) ... Ich fand, dass es einfacher ist, beim Start einer neuen Anwendung empfangene Argumente an die erste Einzelinstanzanwendung zu übergeben.
In Bezug auf einen früheren Code in diesem Thread würde ich es jedoch vorziehen, nicht jedes Mal einen Mutex zu erstellen, wenn ich eine Sperre dafür haben möchte. Es könnte für eine einzelne Instanzanwendung in Ordnung sein, aber in einer anderen Verwendung scheint es mir übertrieben zu sein.
Deshalb schlage ich stattdessen diese Implementierung vor:
Verwendungszweck:
Mutex Global Wrapper:
Ein Kellner
quelle
Eine Lösung (für WPF) ohne WaitOne, da sie eine AbandonedMutexException verursachen kann. Diese Lösung verwendet den Mutex-Konstruktor, der den booleschen Wert createdNew zurückgibt, um zu überprüfen, ob der Mutex bereits erstellt wurde. Außerdem wird die GUID GetType () verwendet, sodass beim Umbenennen einer ausführbaren Datei nicht mehrere Instanzen zulässig sind.
Globaler und lokaler Mutex siehe Hinweis in: https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=netframework-4.8
Da Mutex IDisposable implementiert, wird es automatisch freigegeben. Der Vollständigkeit halber rufen Sie Folgendes auf:
Verschieben Sie alles in eine Basisklasse und fügen Sie die allowEveryoneRule aus der akzeptierten Antwort hinzu. Außerdem wurde ReleaseMutex hinzugefügt, obwohl es nicht wirklich benötigt wird, da es automatisch vom Betriebssystem freigegeben wird (was wäre, wenn die Anwendung abstürzt und niemals ReleaseMutex aufruft, müssten Sie neu starten?).
quelle