Single Instance Application con WPF
Scritto da
Massimo Bonanni
il
mercoledì 29 dicembre 2010
Linguaggio:
•
Framework:
•
Livello: 100
Download sorgenti
In questo articolo cercheremo di mostrare due possibili metodi
per far si che la nostra applicazione WPF sia di tipo "single
instance application" ovvero che possa esistere, al massimo, una
sola istanza della stessa attiva.
Coloro che sviluppano in Windows Forms hanno la possibilità di
definire, nelle proprietà dell'applicazione, se l'utente può
eseguire più istanze della stessa come mostrato dalla seguente
figura:

Nelle applicazioni WPF non esiste questa proprietà e
l'equivalente pagina mostrata in figura1 è la seguente:

Per poter controllare che sia in esecuzione, al massimo, una
sola istanza della nostra applicazione WPF dobbiamo implementare,
quindi, del codice.
La prima soluzione proposta si basa sull'utilizzo di un Mutex di
sistema, cioè un oggetto di sincronizzazione globale, a livello di
sistema, che può essere controllato da processi diversi.
Quando un'applicazione WPF viene eseguita, si verifica la
presenza di un Mutex di sistema con un ben determinato nome (ad
esempio quello dell'applicazione che si sta eseguendo) secondo il
seguente algoritmo:
- se il mutex non esiste ne istanziamo uno e procederemo
all'esecuzione dell'applicazione;
- se il mutex esiste (cioè un'altra applicazione è già in
esecuzione) allora chiudiamo l'istanza dell'applicazione.
Cominciamo con il definire una function che si occupa di
controllare l'esistenza del mutex:
Protected Function MutexExists(ByVal mutexName As String) As Boolean
Dim _mutexExists As Boolean = False
Try
Dim _mutex = Mutex.OpenExisting(mutexName)
If _mutex IsNot Nothing Then
_mutexExists = True
End If
Catch ex As WaitHandleCannotBeOpenedException
_mutexExists = False
Catch ex As UnauthorizedAccessException
_mutexExists = True
End Try
Return _mutexExists
End Function
Utilizziamo il metodo statico OpenExisting()
della classe Mutex per cercare di aprire l'oggetto
di sincronizzazione. Nel caso in cui il mutex esista, il metodo non
provocherà eccezioni mentre nel caso in cui il mutex non esiste
(cioè non è possibile aprirlo) verrà sollevata l'eccezione
WaitHandleCannotOpenedException.
Definita questa function possiamo utilizzarla all'interno
dell'evento Statup della classe
Application:
Private Sub Application_Startup(ByVal sender As Object,
ByVal e As System.Windows.StartupEventArgs) Handles Me.Startup
Dim mutexName = Me.Info.AssemblyName
If Not MutexExists(mutexName) Then
_instanceMutex = New Mutex(True, mutexName)
Else
MessageBox.Show("Esiste già un'applicazione attiva")
Me.Shutdown(1)
End If
End Sub
L'attributo _instanceMutex è di tipo Mutex
(namespace System.Threading):
Private _instanceMutex As Mutex = Nothing
Se il mutex esiste, chiudiamo anticipatamente l'applicazione
dopo aver visualizzato un messaggio (il messaggio potrà essere
sostituito da un qualsiasi pezzo di codice a nostro piacimento)
mentre se l'istanza dell'applicazione è la prima istanziamo
l'attributo privato con un nuovo oggetto Mutex.
Per pulizia gestiamo anche l'evento di chiusura
dell'applicazione rilasciando il mutex (altrimenti non potranno più
essere eseguite istanze dell'applicazione stessa):
Private Sub Application_Exit(ByVal sender As Object,
ByVal e As System.Windows.ExitEventArgs) Handles Me.Exit
If _instanceMutex IsNot Nothing Then
_instanceMutex.ReleaseMutex()
End If
End Sub
Un'altra soluzione al problema si basa sull'utilizzo della
classe WindowsFormsApplicationBase contenuta
nel namespace
Microsoft.VisualBasic.ApplicationServices, la
quale espone dei metodi per la gestione di un'applicazione Windows
Form.
Il concetto di fondo è quello di sostituire, per la gestione del
ciclo di vita dell'applicazione WPF, la classe Application con la
classe WindowsFormsApplicationBase, o per meglio dire con una sua
derivata.
Per implementare tutto ciò, definiamo la classe:
Public Class SingleInstanceApplication
Inherits Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
Friend Sub New()
Me.IsSingleInstance = True
End Sub
Private _app As WPFApplication.Application
Private _counter As Integer
Protected Overrides Function OnStartup(ByVal eventArgs As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) As Boolean
_app = New WPFApplication.Application()
_app.StartupUri = New Uri("MainWindow.xaml", UriKind.Relative)
_app.Run()
Return False
End Function
Protected Overrides Sub OnStartupNextInstance(ByVal eventArgs As Microsoft.VisualBasic.ApplicationServices.StartupNextInstanceEventArgs)
_counter += 1
UpdateWindow()
Me.OnShutdown()
End Sub
Protected Sub UpdateWindow()
Dim window = TryCast(_app.MainWindow, MainWindow)
If window IsNot Nothing Then
window.txtInstanceNumber.Text = _counter.ToString()
End If
End Sub
End Class
Nella precedente classe, WPFApplication.Application è la nostra
classe applicazione WPF (il code behind del file Application.xaml)
e MainWindow.xaml è la finestra principale dell'applicazione.
Se riuscissimo a far partire la classe SingleInstanceApplication
all'avvio dell'applicazione WPF, allora saremmo in grado di gestire
lo Startup e lo StartupNextInstance e gestire, in questo modo,
l'avvio di più istanze della nostra applicazione.
Per risolvere questo ultimo problema costruiamo una classe di
Startup:
Public Class Startup
Public Shared Sub Main(ByVal args As String())
Dim singleInstance = New SingleInstanceApplication()
singleInstance.Run(args)
End Sub
End Class
e modifichiamo le proprietà dell'applicazione WPF per far
eseguire la classe Startup all'avvio.

Per ottenere la possibilità di impostare lo startup object è
necessario:
- Aprire la pagina delle proprietà dell'applicazione WPF
cliccando con il tasto destro del mouse sul progetto e scegliendo
il menù Proprietà;
- Disattivare il checkbox per l'attivazione dell'application
framework;
- Selezionare la classe Startup come oggetto di avvio.
In questo modo, nel momento in cui l'applicazione viene avviata
la prima volta, viene istanziata la classe
SingleInstanceApplication, eseguito il metodo
Run() e, di conseguenza, richiamato il metodo
OnStartup, mentre tutte le volte che viene
eseguita un'ulteriore istanza dell'applicazione (senza aver chiuso
la prima), viene richiamato il metodo
OnStartupNextInstance.
Questi due metodi, in particolare il secondo, ci consentono di
decidere cosa fare dalla seconda istanza in poi. Inoltre, la classe
SingleInstanceApplication è, di fatto, una classe
singleton (cioè unica nella nostra applicazione) e ci permette
di gestire gli scenari in cui il lancio di una successiva
istanza dell'applicazione deve far aprire una finestra figlia
dell'applicazione principale (ad esempio un nuovo documento di
word).
Tramite l'attributo _app della classe SingleInstanceApplication
possiamo, infatti, interagire con l'applicazione in esecuzione.
Nell'esempio che trovate allegato, è stata realizzata
un'applicazione che conta il numero di istanze che l'utente tenta
di avviare successivamente alla prima.
Tags: WPF,windows presentation,presentation framework