Quiero asegurarme de que esto esté ahí afuera, porque es muy difícil hacerlo bien:
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
para verificarcreatedNew
y agregarmutex.Dispose()
dentrofinally
. No puedo explicarlo con claridad (no sé la razón), pero en este momento me he llegado a una situación en la quemutex.WaitOne
regresótrue
despuéscreatedNew
se convirtió enfalse
(adquirí la exclusión mutua en la corrienteAppDomain
y después se carga un nuevoAppDomain
y ejecutado el mismo código de dentro de ella).exitContext = false
algo enmutex.WaitOne(5000, false)
? Parece que sólo podría causar una aserción en CoreCLR , 2. Si alguien se pregunta, en elMutex
constructor 's, la razón por la cualinitiallyOwned
esfalse
se explica en parte por este artículo de MSDN .Usando la respuesta aceptada, creo una clase auxiliar para que pueda usarla de la misma manera que usaría la instrucción Lock. Solo pensé en compartir.
Utilizar:
Y la clase auxiliar:
fuente
< 0
lugar de<= 0
._mutex.Close()
lugar del_mutex.Dispose()
método Dispose funcionó para mí. El error fue causado al intentar deshacerse de WaitHandle subyacente.Mutex.Close()
dispone de los recursos subyacentes.Hay una condición de carrera en la respuesta aceptada cuando 2 procesos se ejecutan bajo 2 usuarios diferentes que intentan inicializar el mutex al mismo tiempo. Después de que el primer proceso inicialice el mutex, si el segundo proceso intenta inicializar el mutex antes de que el primer proceso establezca la regla de acceso para todos, el segundo proceso generará una excepción no autorizada.
Vea a continuación la respuesta corregida:
fuente
Este ejemplo saldrá después de 5 segundos si ya se está ejecutando otra instancia.
fuente
Ni Mutex ni WinApi CreateMutex () funcionan para mí.
Una solución alternativa:
Y el
SingleApplicationDetector
:Razón para usar Semaphore en lugar de Mutex:
Ref: Semaphore.OpenExisting ()
fuente
Semaphore.OpenExisting
ynew Semaphore
.A veces, aprender con el ejemplo ayuda más. Ejecute esta aplicación de consola en tres ventanas de consola diferentes. Verá que la aplicación que ejecutó primero adquiere el mutex primero, mientras que los otros dos esperan su turno. Luego presione Intro en la primera aplicación, verá que la aplicación 2 ahora continúa ejecutándose adquiriendo el mutex, sin embargo, la aplicación 3 está esperando su turno. Después de presionar enter en la aplicación 2, verá que la aplicación 3 continúa. Esto ilustra el concepto de un mutex que protege una sección de código que se ejecutará solo por un hilo (en este caso, un proceso) como escribir en un archivo como ejemplo.
fuente
Un Mutex global no es solo para asegurarse de tener solo una instancia de una aplicación. Personalmente prefiero usar Microsoft.VisualBasic para garantizar una aplicación de instancia única como se describe en ¿Cuál es la forma correcta de crear una aplicación WPF de instancia única? (Respuesta de Dale Ragan) ... descubrí que es más fácil pasar los argumentos recibidos en el inicio de la nueva aplicación a la aplicación de instancia única inicial.
Pero con respecto a algún código anterior en este hilo, preferiría no crear un Mutex cada vez que quiera tener un bloqueo. Podría estar bien para una aplicación de instancia única, pero en otro uso me parece que es excesivo.
Es por eso que sugiero esta implementación en su lugar:
Uso:
Mutex Global Wrapper:
Un mesero
fuente
Una solución (para WPF) sin WaitOne porque puede causar una excepción AbandonedMutexException. Esta solución utiliza el constructor Mutex que devuelve el booleano createdNew para verificar si el mutex ya está creado. También utiliza el GUID GetType (), por lo que renombrar un ejecutable no permite múltiples instancias.
Mutex global vs local ver nota en: https://docs.microsoft.com/en-us/dotnet/api/system.threading.mutex?view=netframework-4.8
Debido a que Mutex implementa IDisposable, se libera automáticamente, pero para completar, llame a disponer:
Mueva todo a una clase base y agregue allowEveryoneRule de la respuesta aceptada. También agregó ReleaseMutex, aunque no parece que sea realmente necesario porque el sistema operativo lo lanza automáticamente (¿qué sucede si la aplicación se bloquea y nunca llama a ReleaseMutex, necesitaría reiniciar?).
fuente