Impedir várias instâncias de um determinado aplicativo no .NET?


123

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:


151

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";

1
Usando uma extensão mútua também funciona para codificar não NET, bem como (embora a sintaxe variaria)
crashmstr

8
Aqui está uma versão um pouco mais cheio de fora, com alguns bons comentários: stackoverflow.com/questions/229565/...
Richard Watson

2
@ ClarkKent: Apenas uma string aleatória para que o nome do Mutex não colide com o nome de outro aplicativo.
jgauffin

Isso também pode se restringir a um certo número de instâncias?
Alejandro del Río

3
A fim de ter um melhor controle enquanto versionamento ou alterar a sua guid aplicativo, você pode usar: string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;o que irá obter a execução de montagem é guid
ciosoriog

22
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;
}

1
Eu acho que deveria ver o seguinte: odetocode.com/blogs/scott/archive/2004/08/20/…
SubmarineX:

@SubmarineX Como você tem certeza de que este artigo está correto? ainda é polêmica entre mutex e codeproject.com/Articles/4975/...
John Nguyen

Surpreendentes graças simplesmente perfeito para o seu tempo e esforços
Ahmed Mahmoud

2
-1 Embora essa seja a solução rápida, também é muito fácil contornar o arquivo a.exe pode ser renomeado para b.exe e ambos serão executados. + outros casos em que isso simplesmente não funcionará como pretendido. Use com cuidado!
Lars Nielsen

Isso não funciona com o mono-desenvolvimento. Em ambientes Windows, ele funciona muito bem.
Tono Nam

20

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();
        }
    }
}

2
Para um aplicativo WPF, use Application.Current.Shutdown (); Este método funciona como um encanto. Obrigado Terrapin.
8113 Jeff

Aqui, o principal é tornar o mutex estático. Assim como no outro caso, o GC o coletará.
27615 Oleksii

Gostei da simplicidade e clareza do Mutex nomeado. Este código é conciso e eficaz.
Chad

7

Hanselman tem uma postagem sobre o uso da classe WinFormsApplicationBase do assembly Microsoft.VisualBasic para fazer isso.


Uso isso há alguns anos, mas agora estou procurando mudar para uma solução baseada em Mutex. Tenho clientes que relatam problemas e suspeito que esteja usando o Remoting para fazer isso.
21139 Richard Watson

5

Parece que existem três técnicas fundamentais que foram sugeridas até agora.

  1. Derive da classe Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase e defina a propriedade IsSingleInstance como true. (Acredito que uma ressalva aqui é que isso não funcionará com aplicativos WPF, não é?)
  2. Use um mutex nomeado e verifique se ele já foi criado.
  3. Obtenha uma lista dos processos em execução e compare os nomes dos processos. (Isso tem a ressalva de exigir que o nome do seu processo seja exclusivo em relação a qualquer outro processo em execução na máquina de um determinado usuário.)

Alguma ressalva que eu perdi?


3
Eu não acho que 3 é muito eficiente. Eu votaria no Mutex, usei sem problemas muitas vezes. Eu nunca usei o item 1, não sei como isso voa quando você está em c #.
typemismatch 18/09/08

2
A opção 1 ainda funciona com o WPF, é apenas um pouco mais envolvida. msdn.microsoft.com/pt-BR/library/ms771662.aspx
Graeme Bradbury

5

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í.


Qual é a diferença disso Mutex? Existe um problema?
Harambe Attack Helicopter 10/10

Está usando o nome do processo. Se o nome for repetido, isso gerará um sinalizador falso. Em qualquer outro caso, é mais limpo que o mutex
magallanes 26/02

4

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: insira a descrição da imagem aqui Este é um projeto de aplicativo do Windows do Visual Studio 2008.


3
Procurei essa caixa de seleção que você mencionou, no meu aplicativo C # / WPF, e não há nenhuma.
HappyNomad

3
Também não o vejo nas propriedades do meu aplicativo VS 2008 C # / WinForms.
Jesse McGrew

Não no VS2005 também. Ele deve estar mencionando o antigo estúdio VB.
Nawfal

Sim, a opção existe, modifiquei a postagem para adicionar uma captura da janela na qual você pode encontrar essa opção.
Doliveras

6
A opção existe apenas para o aplicativo VB.NET, não para C #. Aparentemente, a opção em si usa a classe WinFormsApplicationBase do assembly Microsoft.VisualBasic.
amolbk

4

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


Você não precisa realmente adquirir o mutex e liberá-lo. Tudo que você precisa saber é se outro aplicativo já criou o objeto (ou seja, sua contagem de referências do kernel> = 1).
Michael Goldshteyn


3

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"


2

Use VB.NET! Não mesmo ;)

using Microsoft.VisualBasic.ApplicationServices;

O WindowsFormsApplicationBase do VB.Net fornece uma propriedade "SingleInstace", que determina outras instâncias e permite que apenas uma instância seja executada.


2

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());
    }
}



1

(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");

Isso é realmente brilhante em sua simplicidade e funciona com praticamente qualquer tipo de arquivo e não apenas com imagens. Eu sinto que a solução Mutex está longe de ser "limpa". É extremamente complicado e, aparentemente, existem várias maneiras de falhar por não fazê-lo "corretamente". Também requer um Main()método que contraria o modo como o WPF deve funcionar.
Kyle Delaney

Isso é como um bug usando. Funciona, mas não foi feito para esse fim. Eu não o usaria como profissional.
Bitterblue

Sim, é uma pena que não tenhamos uma solução tão eficaz e simples sem depender de exceções.
Kyle Delaney

Embora não seja exatamente um bug. Ele ainda está se comportando como o esperado.
Kyle Delaney

1
[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


-1, porque você falha ao usar um bloco usando com o mutex, o que tornaria o KeepAlive supérfluo. E sim, acho que John Skeet entendeu errado. Ele não explica por que seria errado Dispose o mutex neste caso.

1

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);
}

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ê .


0

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());
}

0

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


0

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); 
        }
    }         
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.