|
BackgroundWorker
Dopo aver analizzato nel dettaglio la struttura e il funzionamento del sistema di threading, veniamo ora a vedere alcuni controlli che
implementano questo meccanismo "dietro le quinte". Il primo di questi è BackgroundWorker, un controllo senza interfaccia grafica, che
nelle Windows Application rende molto più facile l'utilizzo di thread separati per compiti diversi: infatti fornisce metodi e proprietà
che fanno da wrapper a un thread separato. Ecco una lista dei membri più usati:
- CancelAsync : annulla le operazioni che il controllo sta svolgendo. Al pari di Thread.Abort, l'azione non è immediata e possono anche
essere eseguite istruzioni che evitino l'aborto del thread
- CancellationPending : determina se è stato richiesto l'annullamento delle operazioni con CancelAsync
- IsBusy : determina se il controllo è in fase di esecuzione
- ReportProgress(I) : se richiamato della procedura principale del thread separato, genera un evento ProgressChanged contenente informazioni
sullo stato dell'operazione. I è la percentuale di completamento del lavoro
- RunWorkerAsync : dà inizio alle operazioni tramite il controllo BackgroundWorker. Il suo overload accetta un solo parametro di
tipo Object contenente i parametri che opzionalmente si devono passare alla procedura principale del thread separato
- WorkerReportProgress : determina se il controllo possa generare eventi ProgressChanged
- WorkerSupportCancellation : determina se il controllo supporta la cancellazione delle operazioni
Ora, il BackgroundWorker lavora come descritto di seguito. La procedura principale che deve essere eseguita nel thread separato va posta
in un evento speciale del controllo, chiamato DoWork: attraverso il parametro "e" è possibile anche ottenere altri dati necessari
alle operazioni da svolgere. Una volta che tutto il codice in DoWork è stato completato, viene lanciato l'evento RunWorkerCompleted.
Tale evento viene comunque generato anche nel caso in cui il sorgente abbia dato esito negativo (ad esempio a causa del verificarsi di
eccezioni gestite e non), oppure si sia richiamata la procedura CancelAsync. Sempre all'interno di DoWork si può usare il metodo ReportProgress
per comunicare all'applicazione principale un avanzamento del livello di completamento del lavoro.
Chi ha familiarità con i thread, saprà che se si tenta di accedere a qualsiasi controllo dell'applicazione principale da un thread
separato, viene generata un'eccezione di tipo CrossThreadException. Anche sotto questo punto di vista BackgroundWorker fornisce un aiuto
non di poco conto poichè i suoi eventi sono predisposti in modo tale da evitare errori di questo tipo. Infatti DoWork viene effettivamente
eseguito in un diverso contesto, ma gli eventi sono prodotti nel thread principale, in modo da poter accedere a qualsiasi controllo senza
problemi. Ecco un esempio commentato.
L'interfaccia dovrebbe presentarsi come quella che segue. I nomi sono facilmente intuibili dal sorgente. Bisogna invece aggiungere, ovviamente,
il controllo BackgroundWorker (che non ha interfaccia), con WorkerReportProgress = True e WorkerSupportCancellation = True. Io l'ho chiamato
bgwScan.

E il codice:
Imports System.ComponentModel
'In System.ComponentModel
Class Form1
Private Sub txtDir_Click(ByVal sender As Object, _
ByVal e As EventArgs) Handles txtDir.Click
Dim Open As New FolderBrowserDialog
Open.Description = "Scegliere la cartella da analizzare:"
If Open.ShowDialog = Windows.Forms.DialogResult.OK Then
txtDir.Text = Open.SelectedPath
End If
End Sub
Private Sub cmdAnalyze_Click(ByVal sender As Object, _
ByVal e As EventArgs) Handles cmdAnalyze.Click
If cmdAnalyze.Text = "Analizza" Then
'Controlla che la cartella esista
If Not IO.Directory.Exists(txtDir.Text) Then
MessageBox.Show("Cartella inesistente!", "Sizing", _
MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End If
'Fa partire il BackgroundWorker, passandogli come unico
'argomento il percorso della cartella da analizzare
bgwScan.RunWorkerAsync(txtDir.Text)
'Modifica il testo del pulsante, così da potergli
'assegnare anche un altro compito
cmdAnalyze.Text = "Ferma"
Else
'Il testo non è "Analizza". Deve per forza essere
'"Ferma", quindi termina l'operazione forzatamente
bgwScan.CancelAsync()
cmdAnalyze.Text = "Analizza"
End If
End Sub
Private Sub bgwScan_DoWork(ByVal sender As Object, _
ByVal e As DoWorkEventArgs) Handles bgwScan.DoWork
'Ottiene tutti i files presenti nella cartella:
'- e.Argument ottiene lo stesso valore passato a
' RunWorkerAsync: in questo caso contiene una stringa
'- il pattern *.* specifica di cercare files di ogni
' estensione
'- l'ultimo argomento comunica di eseguire una ricerca
' ricorsiva analizzando anche tutte le sottocartelle
Dim Files() As String = _
IO.Directory.GetFiles(e.Argument, "*.*", _
IO.SearchOption.AllDirectories)
'Dimensione totale
Dim Size As Double = 0
'Files analizzati
Dim Index As Int32 = 0
'Calcola la dimensiona totale della cartella sommando tutte
'le dimensioni parziali
For Each File As String In Files
'FileLen è una funzione di VB6, ma il VB.NET
'implicherebbe di creare un nuovo oggetto FileInfo e
'quindi richiamarne la proprietà Length. In
'questo modo è molto più comodo, anche
'se non proprio conforme alle direttive .NET
Size += FileLen(File)
Index += 1
'Riporta la percentuale e genera un evento
'ProgressChanged
bgwScan.ReportProgress(Index * 100 / Files.Length)
'Controlla se ci sono richieste di cancellazione.
'Se ce ne sono, termina qui la procedura
If bgwScan.CancellationPending Then
e.Cancel = True
Exit Sub
End If
Next
'Il valore Result di e rappresenta il valore da restituire.
'In questo caso è come se DoWork fosse una funzione.
'Dato che si può passare solo un valore Object,
'mettiamo in quel valore un array di Double contenente
'il numero di files trovati e la loro dimensione complessiva
e.Result = New Double() {Files.Length, Size}
End Sub
Private Sub bgwScan_ProgressChanged(ByVal sender As Object, _
ByVal e As ProgressChangedEventArgs) Handles bgwScan.ProgressChanged
'Visualizza la percentuale sulla barra
prgProgress.Value = e.ProgressPercentage
End Sub
Private Sub bgwScan_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As RunWorkerCompletedEventArgs) Handles bgwScan.RunWorkerCompleted
'Controlla la causa che ha scatenato questo evento
If e.Cancelled Then
'Una cancellazione?
MessageBox.Show("Operazione annullata!", "Sizing", _
MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
ElseIf e.Error IsNot Nothing Then
'Un'eccezione?
MessageBox.Show("Si è verificato un errore!", "Sizing", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
'O semplicemente la fine delle operazioni
'COnverte il risultato ancora in un array di Double
Dim Values() As Double = e.Result
lblInfo.Text = String.Format("Sono stati trovati {0} files." & _
"{1}La dimensione totale della cartella è {2:N0} bytes.", _
Values(0), vbCrLf, Values(1))
End If
'Per sicurezza, reimposta il testo del pulsante
cmdAnalyze.Text = "Analizza"
End Sub
End Class
FileSystemWatcher
L'oggetto FileSystemWatcher serve per monitorare files o cartelle in modo da sapere in tempo reale quando vengono modificati, cancellati,
aperti o spostati, ed eseguire azioni di conseguenza. Si potrebbe, ad esempio, controllare un file speciale e visualizzare un messaggio di
warning se l'utente cerca di aprirlo, o chiedere una password, oppure addirittura spegnere il computer (!!). Questo controllo, come si pu&ograv;
intuire, non ha interfaccia grafica. Le sue proprietà interessanti sono:
- EnableRisingEvent : determina se il controllo generi gli eventi; dato che il suo utilizzo è basato su questi, impostare a True
False tale valore equivale ad "accendere" o "spegnere" il controllo
- Filter : specifica il filtro di monitoraggio e si stanno controllando dei files. Non è altro che l'estensione dei files
- IncludeSubdirectories : determina se includere nel monitoraggio anche le sottocartelle
- NotifyFilter : proprietà enumerata codificata a bit che descrive cosa monitorare. Può assumere questi valori: DirectoryName
(cambiamenti nel nome della/delle cartella/e), FileName (cambiamenti nel nome dei files), Attributes (cambiamenti degli attributi di un file),
Size (dimensione di file o cartelle), LastAccess (ultimo accesso), LastWrite (ultima modifica), CreationTime (data di creazione) e Security
(parametri di sicurezza). I valori possono essere sommati con l'operatore su bit Or
- Path : il percorso del file/cartella da monitorare
Bisogna anche dire, però, che nel 99% dei casi questo controllo fa cilecca... infatti non genera nessun evento anche quando dovrebbe.
Misteri del .NET Framework! |
|