No .NET, qual é a melhor maneira de impedir que várias instâncias de um aplicativo sejam executadas ao mesmo tempo? E se não houver uma "melhor" técnica, quais são algumas das ressalvas a serem consideradas em cada solução?
No .NET, qual é a melhor maneira de impedir que várias instâncias de um aplicativo sejam executadas ao mesmo tempo? E se não houver uma "melhor" técnica, quais são algumas das ressalvas a serem consideradas em cada solução?
Respostas:
Use o Mutex. Um dos exemplos acima, usando GetProcessByName, possui muitas ressalvas. Aqui está um bom artigo sobre o assunto:
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;
o que irá obter a execução de montagem é guid
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;
}
Aqui está o código que você precisa para garantir que apenas uma instância esteja em execução. Este é o método de usar um mutex nomeado.
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 tem uma postagem sobre o uso da classe WinFormsApplicationBase do assembly Microsoft.VisualBasic para fazer isso.
Parece que existem três técnicas fundamentais que foram sugeridas até agora.
Alguma ressalva que eu perdi?
1 - Crie uma referência em program.cs ->
using System.Diagnostics;
2 - Coloque void Main()
como a primeira linha de código ->
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length >1)
return;
É isso aí.
Mutex
? Existe um problema?
Usando o Visual Studio 2005 ou 2008 quando você cria um projeto para um executável, nas janelas de propriedades dentro do painel "Aplicativo" existe uma caixa de seleção chamada "Criar aplicativo de instância única" que você pode ativar para converter o aplicativo em um aplicativo de instância única .
Aqui está uma captura da janela da qual estou falando: Este é um projeto de aplicativo do Windows do Visual Studio 2008.
Eu tentei todas as soluções aqui e nada funcionou no meu projeto C # .net 4.0. Na esperança de ajudar alguém aqui a solução que funcionou para mim:
Como principais variáveis de classe:
private static string appGuid = "WRITE AN UNIQUE GUID HERE";
private static Mutex mutex;
Quando você precisa verificar se o aplicativo já está em execução:
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
}
Eu precisava fechar outras instâncias apenas com algumas condições, isso funcionou bem para o meu propósito
http://en.csharp-online.net/Application_Architecture_in_Windows_Forms_2.0 —Single-Instance_Detection_and_Management
Depois de tentar várias soluções na pergunta. Acabei usando o exemplo do WPF aqui: 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();
}
}
}
No App.xaml:
x:Class="*YourNameSpace*.App"
StartupUri="MainWindow.xaml"
Startup="App_Startup"
Este artigo simplesmente explica como você pode criar um aplicativo Windows com controle sobre o número de instâncias ou executar apenas uma instância. Essa é uma necessidade muito típica de um aplicativo de negócios. Já existem muitas outras soluções possíveis para controlar isso.
http://www.openwinforms.com/single_instance_application.html
Este é o código para 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
Este é o código para 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());
}
}
Você precisa usar System.Diagnostics.Process.
Confira: http://www.devx.com/tips/Tip/20044
(Nota: esta é uma solução divertida! Ela funciona, mas usa um design GDI + ruim para isso.)
Coloque uma imagem no seu aplicativo e carregue-a na inicialização. Segure até o aplicativo sair. O usuário não poderá iniciar uma segunda instância. (Claro que a solução mutex é muito mais limpa)
private static Bitmap randomName = new Bitmap("my_image.jpg");
Main()
método que contraria o modo como o WPF deve funcionar.
[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: garantir uma única instância do .NET Application
e: Aplicativo de Instância Única Mutex
Mesma resposta que @Smink e @Imjustpondering com um toque:
Perguntas frequentes de Jon Skeet sobre C # para descobrir por que o GC.KeepAlive é importante
Simplesmente usando a StreamWriter
, que tal isso?
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, isso é feito com um Mutex nomeado (use o novo Mutex ("nome do seu aplicativo", true) e verifique o valor de retorno), mas também existem algumas classes de suporte no Microsoft.VisualBasic.dll que podem fazer isso por você .
Isso funcionou para mim em c # puro. o try / catch é quando, possivelmente, um processo na lista termina durante o loop.
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());
}
Lembre-se de considerar a segurança ao restringir um aplicativo a uma única instância:
Artigo completo: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813
Estamos usando um mutex nomeado com um nome fixo para detectar se outra cópia do programa está em execução. Mas isso também significa que um invasor pode criar o mutex primeiro, impedindo a execução do nosso programa! Como posso evitar esse tipo de ataque de negação de serviço?
...
Se o invasor estiver sendo executado no mesmo contexto de segurança que o seu programa (ou estaria) sendo executado, não haverá nada que você possa fazer. Qualquer que seja o "aperto de mão secreto" criado para determinar se outra cópia do seu programa está sendo executada, o invasor pode imitá-lo. Como está sendo executado no contexto de segurança correto, ele pode fazer qualquer coisa que o programa "real" possa fazer.
...
Claramente, você não pode se proteger de um invasor executando o mesmo privilégio de segurança, mas ainda pode se proteger contra invasores sem privilégios executando outros privilégios de segurança.
Tente definir uma DACL no seu mutex, aqui está o caminho do .NET: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx
Nenhuma dessas respostas funcionou para mim porque eu precisava que isso funcionasse no Linux usando o monodesenvolvimento. Isso funciona muito bem para mim:
Chame esse método passando um ID exclusivo
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);
}
}