1

I am trying to make an application to run multiple (adb specially) commands and get the output and display in a label.

First of all, i need to start the process to execute the commands. Thanks to stackoverflow and @pasty I found this (second reply): How to get Output of a Command Prompt Window line by line in Visual Basic?

Well, i thought that because it outputted to the console, it would be simple to just write it to the label. BIG MISTAKE! It gives me a cross threading error! A little bit of googling and stack overflow I found this: vb.net accessed from a thread other than the thread it was created on

Well, i tried to implement that, but the program just crashes freezes.

Here is the code:

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ' store error output lines

    Dim executable As String() = {"adb", "adb"}
    Dim arguments As String() = {"help", "reboot"}
    For i As Integer = 0 To 0
        Dim process = New Process()
        process.StartInfo = createStartInfo(executable(i), arguments(i))
        process.EnableRaisingEvents = True
        AddHandler process.Exited, Sub(ByVal sendera As Object, ByVal ea As System.EventArgs)
                                       Console.WriteLine(process.ExitTime)
                                       Console.WriteLine(". Processing done.")
                                       'UpdateTextBox(ea)

                                   End Sub
        ' catch standard output
        AddHandler process.OutputDataReceived, Sub(ByVal senderb As Object, ByVal eb As DataReceivedEventArgs)
                                                   If (Not String.IsNullOrEmpty(eb.Data)) Then
                                                       Console.WriteLine(String.Format("{0}> {1}", DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss"), eb.Data))
                                                       'UpdateTextBox(eb.Data)

                                                   End If
                                               End Sub
        ' catch errors
        AddHandler process.ErrorDataReceived, Sub(ByVal senderc As Object, ByVal ec As DataReceivedEventArgs)
                                                  Console.WriteLine(String.Format("! {0}", ec.Data))
                                                  Dim a As String = String.Format("! {0}", ec.Data)
                                                  'UpdateTextBox(a)
                                              End Sub
        ' start process
        Dim result = process.Start()
        ' and wait for output
        process.BeginOutputReadLine()
        ' and wait for errors :-)
        process.BeginErrorReadLine()
        process.WaitForExit()

    Next

End Sub

Private Sub UpdateTextBox(ByVal a As String)
    If Me.InvokeRequired Then
        Dim args() As String = {a}
        Me.Invoke(New Action(Of String)(AddressOf UpdateTextBox), args)
        Return
    End If
    Label1.Text += "a"
End Sub

Private Function createStartInfo(ByVal executable As String, ByVal arguments As String) As ProcessStartInfo
    Dim processStartInfo = New ProcessStartInfo(executable, arguments)
    processStartInfo.WorkingDirectory = Path.GetDirectoryName(executable)
    ' we want to read standard output
    processStartInfo.RedirectStandardOutput = True
    ' we want to read the standard error
    processStartInfo.RedirectStandardError = True
    processStartInfo.UseShellExecute = False
    processStartInfo.ErrorDialog = False
    processStartInfo.CreateNoWindow = True
    Return processStartInfo

End Function

And the source code: https://github.com/iAmAOpenSource/SyncfusionWindowsFormsApplication3

  • `the program just crashes` Are you debugging, and you get an error message? Or what? – djv Jul 27 '17 at 14:11
  • @djv It just freezes. Should have reworded it better :). No output or anything. Need to learn debugging in visual studio. Still have a long way to go – Fabio Rameiro Jul 27 '17 at 14:16
  • My guess is that the call to `process.WaitForExit()` is blocking the UI thread until the process ends, and when you get a line of output in the `process.OutputDataReceived` you are calling `Me.Invoke`, which tries to run code on the UI thread, which is blocked, so... freeze. You probably need to move the logic that starts the processes onto a different thread - maybe just wrap the contents of `Button1_Click` in `Task.Run`. – Mark Jul 27 '17 at 17:45
  • You should start by debugging your program - step through it line by line. Put a breakpoint inside the `Button1_Click` handler, and "step over" each line. One of these lines most likely won't grant you control of the program anymore; this line would be the culprit. I suspect @Mark is right. Unfortunately we can't debug *for* you, so try it and we can help you make sense of things. – djv Jul 27 '17 at 18:06
  • @djv @Mark you lot were right! it was the `process.WaitForExit()` line that was blocking the thread. The `task.run(Sub() NameOfTheSub())` works perfectly. Thank you very much!!!! Do any of you want to post the answer so I can tick as correct? – Fabio Rameiro Jul 28 '17 at 13:08

1 Answers1

0

The call to process.WaitForExit() will block the UI thread until the spawned process exits, but while processing the output in the process.OutputDataReceived event you are calling Me.Invoke which tries to run the code on the UI thread, which is blocked, so the program freezes. You could move the logic in Button1_Click onto another thread, e.g.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Task.Run(
        Sub()
            ... current logic
        End Sub
    )
End Sub

That way the UI thread won't be blocked and the Me.Invoke call won't cause a deadlock.

Mark
  • 8,140
  • 1
  • 14
  • 29
  • Thank you very much. Still had a little problem because the `textbox` was not being accepted as a argument for the function `UpdateTextBox`, although i resolved it by using a global variable to store which textbox to output it to(i know i should not be doing this, but it works so :)). – Fabio Rameiro Jul 28 '17 at 21:23