Avatar of andieje
andieje
 asked on

help needed running a command line program from vb.net

Hi

I have some code for running a command line program from within vb.net. This is shown below

I have added an event handler to handle the exit event of the program. Once the process exits and the event handler is called, where will program execution continue? Will it return to the main method? I am running the program now but the program takes so long so run its not easy to test things with.

Also my vb.net program is still running while the process is running. What is keeping the vb.net program running? Is it because the call outputstream.readToEnd does not complete until the process has finished so my vb.net program is waiting at this line?

what will happen to these function calls
out = outputStream.ReadToEnd()
err = errorStream.ReadToEnd
if the process exits because of an error? Will they run ok and still get any available output and error data? If not, how do i make sure that they do? Should i move these function calls to the Exit event handler.

Basically the order of  handling the exit event and then reading the output streams seems wrong to me. How should i do it properly?

Also, how would i echo the output of the process to the console window so i can see the output as the program is running?

Thanks very much. Here is the code


==========================================



    Sub Main()

   

        Dim p As Process = New Process()
        Dim out As String
        Dim err As String

        Dim si As New ProcessStartInfo

        si.FileName = "C:\xmltv\xmltv-0.5.44-win32\xmltv-0.5.44-win32\xmltv.exe"
        si.Arguments = "tv_grab_uk_rt --output FILE"
        si.RedirectStandardError = True
        si.RedirectStandardOutput = True
        si.UseShellExecute = False
        si.CreateNoWindow = True
        p.StartInfo = si
        p.EnableRaisingEvents = True

        ' add an Exited event handler
        AddHandler p.Exited, AddressOf ProcessExited

        p.Start()
       

        Dim outputStream As System.IO.StreamReader = p.StandardOutput
        Dim errorStream As System.IO.StreamReader = p.StandardError


        out = outputStream.ReadToEnd()
        err = errorStream.ReadToEnd
       

        outputStream.Close()
        errorStream.Close()
        p.Close()


        Console.ReadLine()



    End Sub

    ' event handler
    Friend Sub ProcessExited(ByVal sender As Object, _
       ByVal e As System.EventArgs)
        Dim myProcess As Process = DirectCast( _
           sender, Process)
        Console.WriteLine("The process exited, raising " & _
           "the Exited event at: " & myProcess.ExitTime & _
           "." & System.Environment.NewLine & _
           "Exit Code: " & myProcess.ExitCode)
        myProcess.Close()

    End Sub

Visual Basic.NET

Avatar of undefined
Last Comment
andieje

8/22/2022 - Mon
jake072

Your exe is still running because you need to use UseShellExecute = True.  But your process traps the output, so I'm pretty sure that that wouldn't change it either.

Everything else seems ok to me.

Process Exited happens when your process is terminating (i.e., after the Main() has executed).

Jake
Fernando Soto

What version of VB .Net?
Fernando Soto

Hi andieje;

If this is VB .Net 2.0
These two line will cause problems, Dead Lock, you will wait for ever .

        out = outputStream.ReadToEnd()
        err = errorStream.ReadToEnd

One needs to be a async read and the other can be a sync read, ReadToEnd.

Fernando
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
andieje

ASKER
Hi Fernando, that's exactly what has happened :)

Please can you explain more about about what you mean by "One needs to be a async read and the other can be a sync read, ReadToEnd."

Also, would you be able to answer the other questions i asked in my post about where program execution resumes after process exit.

Many thanks
andrea

Jake, if i set the shell property to true i get an error. It has to be set to false to redirect the streams.
andieje

ASKER
Me again. I've been thinking some more and i don't understand why there should be a dead lock. If the process has exited, why can't ont output stream read to the end of its stream and then the other do the same. I must be missing an important key point.
ASKER CERTIFIED SOLUTION
Fernando Soto

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
Fernando Soto

From Microsoft  documentation

http://msdn2.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput.aspx

The redirected StandardOutput stream can be read synchronously or asynchronously. Methods such as Read, ReadLine, and ReadToEnd perform synchronous read operations on the output stream of the process. These synchronous read operations do not complete until the associated Process writes to its StandardOutput stream, or closes the stream.

In contrast, BeginOutputReadLine starts asynchronous read operations on the StandardOutput stream. This method enables a designated event handler for the stream output and immediately returns to the caller, which can perform other work while the stream output is directed to the event handler.

Synchronous read operations introduce a dependency between the caller reading from the StandardOutput stream and the child process writing to that stream. These dependencies can result in deadlock conditions. When the caller reads from the redirected stream of a child process, it is dependent on the child. The caller waits on the read operation until the child writes to the stream or closes the stream. When the child process writes enough data to fill its redirected stream, it is dependent on the parent. The child process waits on the next write operation until the parent reads from the full stream or closes the stream. The deadlock condition results when the caller and child process wait on each other to complete an operation, and neither can proceed. You can avoid deadlocks by evaluating dependencies between the caller and child process.

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
Fernando Soto

To your question:

Also my vb.net program is still running while the process is running. What is keeping the vb.net program running? Is it because the call outputstream.readToEnd does not complete until the process has finished so my vb.net program is waiting at this line?

Yes, the program does not contiue until the call to outputstream.readToEnd has returned.

what will happen to these function calls
out = outputStream.ReadToEnd()
err = errorStream.ReadToEnd
if the process exits because of an error? Will they run ok and still get any available output and error data? If not, how do i make sure that they do? Should i move these function calls to the Exit event handle

The information from ReadToEnd will most likely be lost or any part that has not yet been written to the string variable. You can minimize the lost of data by using asynchronously read functions as shown in my post on both out streams.

Also, how would i echo the output of the process to the console window so i can see the output as the program is running?

On the ReadToEnd function you can not because the function has not returned from the call. With the asynchronously  read in the event handler you can do this
    Console.WriteLine(outLine.Data)

Fernando
andieje

ASKER
That's excellent.

I've just added another question after i've seen your reply.

How would i modify the above so i can view the output of the program in a console window as it runs.

Please answer in the new question so i can award you the points there too.

andrea
andieje

ASKER
Hi Fernando

That's absolutely brilliant - thank you so much.

Just one more thing. I understand how I could write the data from the ERROR stream to the console window in the ProcessErrorHandler event handler. However, how would i echo the output from the normal OUTPUT stream as it happens because we are using output.readToEmd

I could add a similar event for process.OutputDataReceived?

How then could i keep my vb.net program going if i am not using readToEnd to hold it up

I could do this?

while true
 if p.hasexited then
   break while
end if
end while

Many thanks
andrea
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
andieje

ASKER
Me again

I tried my own suggestion and it didn't work. I don't get any output data written to screen

Sub Main()


        Dim p As Process = New Process()
        Dim out As String

        Dim si As New ProcessStartInfo
        si.FileName = "C:\xmltv\xmltv-0.5.44-win32\xmltv-0.5.44-win32\xmltv.exe"
        si.Arguments = "tv_grab_uk_rt --output FILE"


        ' The event handler that will be called when stderr has something to write.
        AddHandler p.ErrorDataReceived, AddressOf ProcessErrorHandler

        AddHandler p.OutputDataReceived, AddressOf ProcessOutputHandler <====================

        si.RedirectStandardOutput = True
        si.UseShellExecute = False
        si.CreateNoWindow = True
        p.StartInfo = si
        p.EnableRaisingEvents = True

        ' add an Exited event handler
        AddHandler p.Exited, AddressOf ProcessExited

        p.Start()




        While True
            If p.HasExited Then
                Exit While
            End If
        End While
        p.Close()

        Console.ReadLine()

    End Sub

    ' event handler
    Private Sub ProcessExited(ByVal sender As Object, ByVal e As System.EventArgs)

        Dim myProcess As Process = DirectCast(sender, Process)
        Console.WriteLine("The process exited, raising " & _
            "the Exited event at: " & myProcess.ExitTime & _
            "." & System.Environment.NewLine & _
            "Exit Code: " & myProcess.ExitCode)
        'myProcess.Close()

    End Sub

    ' Class level variable to hold the messages from the stderr output
    Private ErrorOutput As New StringBuilder

    ' Event handler that is called when stderr has something to write.
    Private Sub ProcessErrorHandler(ByVal sendingProcess As Object, _
        ByVal outLine As DataReceivedEventArgs)

        ErrorOutput.Append(outLine.Data)

    End Sub

    Private Sub ProcessOutputHandler(ByVal sendingProcess As Object, _
        ByVal outLine As DataReceivedEventArgs)

        Console.WriteLine(outLine.Data)

    End Sub