VB.NET - Permitindo somente uma instância da sua aplicação


Como podemos fazer para saber se já existe uma instância da nossa aplicação em execução no VB.NET ?

Nota: No Visual Basic a tarefa era simples e o código usado com mais frequência era o seguinte:

Private Sub Form_Load()
Dim cap As String

    If App.PrevInstance Then
        ' Informe ao usuário que já existe uma instância rodando.
        MsgBox "Já existe uma instância desta aplicação em execução.", vbCritical

        ' ativa outra instância
        cap = Me.Caption
        Me.Caption = ""
        AppActivate cap

        ' Sai.
        Unload Me
    End If
End Sub

E no VB.NET , alguém se habilita a mostrar como fazer ??

Alguém rapidamente poderia propor a solução que utiliza o namespace System.Diagnostics. Ela funciona assim:

Utilizando a classe Process do citado namespace temos acesso ao método GetProcessByName que retorna uma array com o nome dos processos que estão sendo executados. O código é enxuto e é mostrado com duas possibilidades abaixo:


Dim emExecucao As Boolean

1- verificando quantos elementos o array possui , se possuir mais de um então existe duas instâncias
emExecucao = Process.GetProcessesByName(Process.GetCurrentProcess.ProcessName).Length > 1

2- verificando o limite superior do array , se for  maior que zero então existe duas instâncias
emExecucao = Process.GetProcessesByName(Process.GetCurrentProcess.ProcessName).GetUpperBound(0) > 0
 

Acima temos as duas formas de verificar se existe mais de uma instância da aplicação rodando:

1- Na primeira eu estou verificando se o tamanho do array retornado pelo método GetProcessesByName para o processo atual : GetCurrentProcess.ProcessName é maior que 1 , se for então temos duas instância da aplicação rodando.

2- No segundo eu verifico o limite superior do array retornado para o processo atual , se for maior que zero então eu tenho duas cópias da instância rodando.

Nota: Uma solução um pouco mais sofisticada pode ser obtida na função abaixo:

Public Shared Function VerificaInstancia() As Process

'obtem o nome do processo atual
Dim processoAtual As Process = Process.GetCurrentProcess()
Dim processosRodando As Process() = Process.GetProcessesByName(processoAtual.ProcessName)

'percorre os processos em execução com o mesmo nome
Dim processo As Process
  For Each processo In processosRodando
     'Ignora o processo atual
      If processo.Id <> processoAtual.Id Then
             'esteja certo que o processo esta rodando a partir de um arquivo EXE.
              If [Assembly].GetExecutingAssembly().Location.Replace("/", "\") =  processoAtual.MainModule.FileName Then
                  'Retorna a outra instância do processo
                   Return process
              End If
      End If
  Next processo
  'Se nenhuma outra instância estiver em execução retorna null.
  Return Nothing
End Function

Mas existe um problema com este método. Ele se baseia no nome do processo (nome do executável) ,  se o usuário mudar o nome do executável ele vai poder rodar a aplicação duas vezes. 

Chegamos a um impasse ?

A solução também é simples. Basta usar a classe Mutex do namespace System.Threading. O código é o seguinte :

 Dim objMutex As Mutex

objMutex = New Mutex(False, "SINGLE_INSTANCE_APP_MUTEX")

If objMutex.WaitOne(0, False) = False Then
     objMutex.Close()
     objMutex = Nothing
     MessageBox.Show("Já existe uma instância rodando desta aplicação.")
     End
End If

Uma solução mais elegante ainda seria implementar o pattern Singleton . O que é isto ??

O padrão de projeto Singleton é apenas uma estratégia para certificar de que existe somente uma única instância de um objeto particular.

Como implementar o padrão Singleton no VB.NET ?

Podemos fazer isto em duas etapas.

1- Certificar que cada construtor na classe que implementa o pattern Singleton não é do tipo Public. Todos os construtores devem ser protected ou private.

2- Implementar um método estático (shared) que cria somente uma única instância da classe.

Resolvido o problema. Como ?

Como um usuário não pode invocar construtores private ou protected eles não podem criar instâncias da classe exceto na forma que você definiu.

Os métodos membros podem no entanto invocar métodos private e protected e podem chamar seu construtor não Public. Você então entra em cena e controla o número de instâncias que deseja executar.

Abaixo um modelo:

Public Class Singleton

  Private Shared FInstance As Singleton = Nothing

  Private Sub New()

  End Sub

  Public Shared ReadOnly Property Instance()
    Get
      If (FInstance Is Nothing) Then
        FInstance = New Singleton()
      End If

      Return FInstance
    End Get
  End Property

End Class

E em C# como podemos fazer o mesmo serviço ?

1- Inicie um novo projeto do tipo Visual C# e Windows Application

2- Acrescente a seguinte linha de código no inicio do código : using System.Diagnostics;

3- Localize [STAThread]  na aplicação. Você deverá encontrar algo parecido com o código abaixo:

[STAThread]
static void Main()
{
        Application.Run(new Form1());
}

4- Copie o código abaixo antes da linha de código já existente:

[STAThread]
static void Main() 
{
   Process aProcess = Process.GetCurrentProcess();
   string aProcName = aProcess.ProcessName;
		
    if (Process.GetProcessesByName(aProcName).Length > 1)
    {
       MessageBox.Show("The application is already running!!","Test",MessageBoxButtons.OK,MessageBoxIcon.Stop);
      Application.ExitThread();
    }
     Application.Run(new Form1());
}

Pronto !

Eu sei ,  é  apenas VB.NET , mas eu gosto...

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

  Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti