Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - BackgroundWorker e FileSystemWatcher

Guida al Visual Basic .NET

Capitolo 89° - BackgroundWorker e FileSystemWatcher

<< Precedente Prossimo >>
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.

BackgroundWorker.jpg
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!

<< Precedente Prossimo >>
A proposito dell'autore

Programmatore e analista .NET 2005/2008/2010 (in particolare C# e VB.NET), anche nell'implementazione Mono per Linux. Conoscenze approfondite di Pascal, PHP, XML, HTML 4.01/5, CSS 2.1/3, Javascript (e jQuery). Conoscenze buone di C, LUA, GML, Ruby, XNA, AJAX e Assembly 68000. Competenze basilari di C++, SQL, Hlsl, Java.