Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visual Basic Capture output of cmd

I want Visual Basic to be able to run the "make" command on the directory "C:\projectTest\".

I tried to use this:

    Dim output As String = String.Empty

    Using Process As New Process
        Process.StartInfo = New ProcessStartInfo("cmd")
        Process.StartInfo.WorkingDirectory = "C:\projectTest\"
        Process.StartInfo.UseShellExecute = False
        Process.StartInfo.CreateNoWindow = True
        Process.StartInfo.RedirectStandardInput = True
        Process.StartInfo.RedirectStandardOutput = True
        Process.StartInfo.RedirectStandardError = True
        Process.Start()
        Process.BeginOutputReadLine()
        AddHandler Process.OutputDataReceived,
 _
           Sub(processSender As Object, lineOut As DataReceivedEventArgs)
               output += lineOut.Data + vbCrLf
           End Sub

        Using InputStream As System.IO.StreamWriter = Process.StandardInput
            InputStream.AutoFlush = False
            InputStream.WriteLine("make")
        End Using
        Do
            Application.DoEvents()
        Loop Until Process.HasExited
    End Using

This code is able to capture the "gcc ..." part of the console (comes from the Makefile), but will not capture the error (which does pop up if I manually open cmd and run make on that directory).

How can I capture everything that appears including the error?

like image 442
Jimmay Avatar asked Jan 12 '23 15:01

Jimmay


1 Answers

More than one problem. First off, as @shf301 already told you, you forgot to read stderr. He in turn forgot to add an extra line:

    Process.Start()
    AddHandler Process.OutputDataReceived, _
       Sub(processSender As Object, lineOut As DataReceivedEventArgs)
           output += lineOut.Data + vbCrLf
       End Sub
    Process.BeginOutputReadLine()
    AddHandler Process.ErrorDataReceived, _
       Sub(processSender As Object, lineOut As DataReceivedEventArgs)
           output += lineOut.Data + vbCrLf
       End Sub
    Process.BeginErrorReadLine()

There's another very cumbersome problem, your event handlers run late. They fire after the process has already exited. A side effect of these handlers running on a thread-pool thread. You'll need to wait for an arbitrary (and unguessable) amount of time before you use the output variable:

    Do
        Application.DoEvents()
    Loop Until Process.HasExited
    System.Threading.Thread.Sleep(1000)

This is too ugly. Do this the way that any IDE or editor does it. Redirect the output to a temporary file and read the file afterwards:

    Dim tempfile As String = System.IO.Path.GetTempFileName
    Using Process As New Process
        Process.StartInfo = New ProcessStartInfo("cmd.exe")
        Process.StartInfo.Arguments = "/c make 1> """ + tempfile + """ 2>&1"
        Process.StartInfo.WorkingDirectory = "C:\projectTest"
        Process.StartInfo.UseShellExecute = False
        Process.StartInfo.CreateNoWindow = True
        Process.Start()
        Process.WaitForExit()
        output = System.IO.File.ReadAllText(tempfile)
        System.IO.File.Delete(tempfile)
    End Using

Some annotation with the mystic command line:

  • /c tells cmd.exe to execute just the single command and then exit
  • 1> redirects the output to the temporary file
  • 2>&1 tells cmd.exe to redirect stderr to stdout
  • the triple double quotes ensures that spaces in the tempfile name don't cause trouble.

That same 2>&1 would also have fixed your original problem ;)

like image 60
Hans Passant Avatar answered Jan 18 '23 04:01

Hans Passant