none
Leggere finestra comandi ed aggiornare una form RRS feed

  • Domanda

  • Salve,

    ho fatto una ricerca ma, forse per colpa mia, non ho trovato quello che stavo cercando. Quindi vi espongo il mio problema:

    ho un'applicazione a linea di comando, nello specifico è un'applicazione che codifica files audio. A questa applicazione passo dei parametri e lei comunica con l'utente visualizzando nella finestra console di Windows, assieme ad altri dati, il progresso della codifica. Ho necessità di leggere questa finestra in modo da passare il valore del tempo di elaborazione istantaneo ad una ProgressBar (presente nella Main Form) che visualizzi il procedere della codifica.

    Quello che ho fatto fino adesso è lanciare l'applicazione con la dovuta serie di parametri, attendere la sua chiusura, caricare il risultato finale in una TextBox. A naso ho l'impressione che debba usare il multithreading ma è qui che mi areno.

    Potete darmi una mano?

    domenica 9 novembre 2014 16:55

Risposte

  • Nel nostro caso, il thread MyThread si chiude mediante il metodo Abort(). Quindi:

    MyThread.Abort()

    Tale metodo solleva sempre un'eccezione di tipo ThreadAbortException (che andrebbe intercettata/gestita tramite costrutto Try/Catch):

     Try
       MyThread.Abort()
     Catch a As Threading.ThreadAbortException
     End Try
    

    Il "problema" diventa quando utilizzare questo metodo, ovvero il momento in cui possiamo chiamarlo essendo certi che il programma che poggia sul thread abbia effettivamente concluso il suo compito. Se, per esempio, sei certo di utilizzare un solo processo, potresti rendere visibile all'intera classe la variabile p, controllando poi (su un determinato evento) il suo stato, e chiudendo il thread se essa ha valore Nothing.

    Nell'esempio qui a seguire, riporto il codice di cui sopra, modificato per rendere la variabile p visibile a tutta la classe, ed ipotizzando che il thread debba essere interrotto quando si preme un Button1, dedicato altresì alla chiusura dell'applicazione stessa (in grassetto, le modifiche più sostanziali)

    Public Class Form1
    
        Dim p As Process
        Dim MyThread As New System.Threading.Thread(AddressOf MThread)
        Private Delegate Sub AppendTextBoxDelegate(ByVal TB As TextBox, ByVal txt As String)
    
        Private Sub AppendTextBox(ByVal TB As TextBox, ByVal txt As String)
            If TB.InvokeRequired Then
                TB.Invoke(New AppendTextBoxDelegate(AddressOf AppendTextBox), New Object() {TB, txt})
            Else
                TB.AppendText(txt)
            End If
        End Sub
    
        Private Sub MThread()
            p = New Process()
            Dim ps As New ProcessStartInfo("ping.exe", "www.google.it -n 4")
            ps.RedirectStandardOutput = True
            ps.UseShellExecute = False
            p.StartInfo = ps
            p.Start()
    
            Dim sOut As IO.StreamReader = p.StandardOutput
            While Not (sOut.EndOfStream)
                AppendTextBox(TextBox1, sOut.ReadLine)
            End While
    
            sOut.Close()
            p.Close()
            p = Nothing
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            MyThread.Start()
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            If IsNothing(p) Then
                Try
                    MyThread.Abort()
                Catch ex As Threading.ThreadAbortException
                End Try
            End If
            Application.Exit()
        End Sub
    End Class


    Emiliano Musso - MVP Visual Basic

    martedì 11 novembre 2014 07:46
    Moderatore

Tutte le risposte

  • Salve, 

    Ho provato a cercare alcuni esempi pratici sul Multithreading e penso che possono aiutare:

    C# Multithreading Example

    Threading Tutorial

    Saluti


    Microsoft offre questo servizio gratuitamente, per aiutare gli utenti e aumentare il database dei prodotti e delle tecnologie. Il contenuto viene fornito “così come è” e non comporta alcuna responsabilità da parte dell'azienda.

    lunedì 10 novembre 2014 10:30
  • Buongiorno Max Go,

    ti propongo un esempio di threading con cattura dell'output, che dovrebbe fare al caso tuo.

    In primis, la definizione della Sub che lancerà il tuo processo

        Private Sub LanciaThreadProcesso()
            Dim p As New Process()
            Dim ps As New ProcessStartInfo("ping.exe", "www.google.it -n 120")
            ps.RedirectStandardOutput = True
            ps.UseShellExecute = False
            p.StartInfo = ps
            p.Start()
    
            Dim sOut As IO.StreamReader = p.StandardOutput
            While Not (sOut.EndOfStream)
                AppendTextBox(TextBox1, sOut.ReadLine)
            End While
    
            sOut.Close()
            p.Close()
        End Sub

    In essa definisco un processo (nel mio caso, lancerà un ping), chiedendo al ProcessStartInfo del processo di redirigere l'output, in modo che esso possa essere intercettato. Allo StandardOutput del processo connetto quindi uno StreamReader, che servirà per la lettura, e una volta lanciato il processo stesso, eseguiremo un ciclo in cui viene eseguita una sub (che vedremo ora) utile ad aggiungere l'output ad una ipotetica TextBox1, man mano che il processo produce dati.

    Non è possibile indicare direttamente la proprietà Text del TextBox, in quanto un thread non può accedere in maniera diretta agli oggetti presenti sul thread chiamante. Dovremo quindi predisporre un delegato che svolga questo compito. Lo dichiareremo così:

        Private Delegate Sub AppendTextBoxDelegate(ByVal TB As TextBox, ByVal txt As String)
    
        Private Sub AppendTextBox(ByVal TB As TextBox, ByVal txt As String)
            If TB.InvokeRequired Then
                TB.Invoke(New AppendTextBoxDelegate(AddressOf AppendTextBox), New Object() {TB, txt})
            Else
                TB.AppendText(txt)
            End If
        End Sub

    Infine, dovremo creare e lanciare un thread che prenda in carico la nostra sub LanciaThreadProcesso(). In un qualunque punto di programma, sarà sufficiente l'istruzione

            Dim MyThread As New System.Threading.Thread(AddressOf LanciaThreadProcesso)
            MyThread.Start()

    Naturalmente si dovrà poi gestire la chiusura del thread, quando esso non servirà più.

    Il codice qui riportato è funzionante, e credo sia una base di partenza valida. Chiedo scusa per la sbrigatività nel commentarlo, ma sono purtroppo un po' di corsa. In caso di necessità, non esitare a chiedere.

    Buona giornata


    Emiliano Musso - MVP Visual Basic


    lunedì 10 novembre 2014 10:59
    Moderatore
  • Ho provato il codice, funziona ma il problema è forse il tipo di output dell'applicazione lanciata, non riesco a vederla in real time. Nel frattempo mi sai anche dire come chiudo il thread correttamente?

    Grazie per la risposta celere.

    lunedì 10 novembre 2014 20:09
  • Nel nostro caso, il thread MyThread si chiude mediante il metodo Abort(). Quindi:

    MyThread.Abort()

    Tale metodo solleva sempre un'eccezione di tipo ThreadAbortException (che andrebbe intercettata/gestita tramite costrutto Try/Catch):

     Try
       MyThread.Abort()
     Catch a As Threading.ThreadAbortException
     End Try
    

    Il "problema" diventa quando utilizzare questo metodo, ovvero il momento in cui possiamo chiamarlo essendo certi che il programma che poggia sul thread abbia effettivamente concluso il suo compito. Se, per esempio, sei certo di utilizzare un solo processo, potresti rendere visibile all'intera classe la variabile p, controllando poi (su un determinato evento) il suo stato, e chiudendo il thread se essa ha valore Nothing.

    Nell'esempio qui a seguire, riporto il codice di cui sopra, modificato per rendere la variabile p visibile a tutta la classe, ed ipotizzando che il thread debba essere interrotto quando si preme un Button1, dedicato altresì alla chiusura dell'applicazione stessa (in grassetto, le modifiche più sostanziali)

    Public Class Form1
    
        Dim p As Process
        Dim MyThread As New System.Threading.Thread(AddressOf MThread)
        Private Delegate Sub AppendTextBoxDelegate(ByVal TB As TextBox, ByVal txt As String)
    
        Private Sub AppendTextBox(ByVal TB As TextBox, ByVal txt As String)
            If TB.InvokeRequired Then
                TB.Invoke(New AppendTextBoxDelegate(AddressOf AppendTextBox), New Object() {TB, txt})
            Else
                TB.AppendText(txt)
            End If
        End Sub
    
        Private Sub MThread()
            p = New Process()
            Dim ps As New ProcessStartInfo("ping.exe", "www.google.it -n 4")
            ps.RedirectStandardOutput = True
            ps.UseShellExecute = False
            p.StartInfo = ps
            p.Start()
    
            Dim sOut As IO.StreamReader = p.StandardOutput
            While Not (sOut.EndOfStream)
                AppendTextBox(TextBox1, sOut.ReadLine)
            End While
    
            sOut.Close()
            p.Close()
            p = Nothing
        End Sub
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            MyThread.Start()
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            If IsNothing(p) Then
                Try
                    MyThread.Abort()
                Catch ex As Threading.ThreadAbortException
                End Try
            End If
            Application.Exit()
        End Sub
    End Class


    Emiliano Musso - MVP Visual Basic

    martedì 11 novembre 2014 07:46
    Moderatore