How to get Output of a Command Prompt Window line by line in Visual Basic?

10,501

Solution 1

I've done some research. adb help writes output into STDERR. So you need something like:

    Dim proc As ProcessStartInfo = New ProcessStartInfo("cmd.exe")
    Dim pr As Process
    proc.CreateNoWindow = True
    proc.UseShellExecute = False
    proc.RedirectStandardInput = True
    proc.RedirectStandardOutput = True
    pr = Process.Start(proc)
    pr.StandardInput.WriteLine("C:\sdk\platform-tools")
    pr.StandardInput.WriteLine("adb help 2>&1")
    pr.StandardInput.Close()
    Console.WriteLine(pr.StandardOutput.ReadToEnd())
    pr.StandardOutput.Close()

to catch it.
You need no 2>&1 if you call ipconfig, for example.

Solution 2

Do not interate over the output and do not read it! Normally you don't know how long the output (same goes for error output too) would be, so you need to prepare for an unknown length. Since you are telling the Process class, that you want to handle the standard output and the standard error by yourself, you also need to bind to the events, in this case:

  • OutputDataReceived
  • ErrorDataReceived

or to block the current process and read the complete output at once like @Dmitry Kurilo does in his answer. I find the first approach better because I do not need to wait for the process to end to see it's output. The MSDN documentation of the ProcessStartInfo.RedirectstandardError property gives a good explanation of the different possibilities with a lot of examples. If you want to take a specific line, there are a lot of possibilities. One would be to store each output (line) in the delegate and use it later, using a List(Of String) and output the specific line when the process is done (= all output lines are present).

A possible solution could look like this:

' store error output lines
dim lines = new List(of String)

dim executable = "c:\temp\android\sdk\platform-tools\adb.exe"
dim arguments = " help"
dim process = new Process()
process.StartInfo = createStartInfo(executable, arguments)
process.EnableRaisingEvents = true
addhandler process.Exited, Sub (ByVal sender As Object, ByVal e As System.EventArgs) 
    Console.WriteLine(process.ExitTime)
    Console.WriteLine(". Processing done.")
    ' output line n when output is ready (= all lines are present)
    Console.WriteLine(lines(4))
end sub
' catch standard output
addhandler process.OutputDataReceived, Sub (ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs) 
        if (not String.IsNullOrEmpty(e.Data))
            Console.WriteLine(String.Format("{0}> {1}", DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") ,e.Data))
        end if
end sub
' catch errors
addhandler process.ErrorDataReceived, Sub (ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs) 
    'Console.WriteLine(String.Format("! {0}", e.Data))
    ' add every output line to the list of strings
    lines.Add(e.Data)
end sub
' start process
dim result = process.Start()
' and wait for output
process.BeginOutputReadLine()
' and wait for errors :-)
process.BeginErrorReadLine()

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

Now even if the adb writes to the error output, you will be able to see it. It will also be complete.

The output in this case looks like this:

14.10.2014 12:49:10
. Processing done.
 -e                            - directs command to the only running emulator.

Another possibility would be to put everything into one string and after the process has finished split the single string on line endings (CRLF \r\n) and you will gain the lines you want to filter.

Share:
10,501
Devenom
Author by

Devenom

A newbie Developer in Android, VB .NET, C, C++ and Java

Updated on June 14, 2022

Comments

  • Devenom
    Devenom almost 2 years

    I am trying to get a command line output line by line till the end of the output but I am not able to do so. I am using it in my Form and this code executes on click of a button.
    Can you tell me whats wrong with my code?

    Dim proc As ProcessStartInfo = New ProcessStartInfo("cmd.exe")
        Dim pr As Process
        proc.CreateNoWindow = True
        proc.UseShellExecute = False
        proc.RedirectStandardInput = True
        proc.RedirectStandardOutput = True
        pr = Process.Start(proc)
        pr.StandardInput.WriteLine("cd C:\sdk\platform-tools\")
        pr.StandardInput.WriteLine("adb help")
        Dim helpArray(20) as String
        For i as Integer 1 To 7
        helpArray(i) = pr.StandardOutput.ReadLine()
        Next
        pr.StandardOutput.Close()
    

    The program stops responding when this code is executed.

  • Mark Hurd
    Mark Hurd over 9 years
    There is also RedirectStandardError.
  • Mark Hurd
    Mark Hurd over 9 years
    I'd drop the if (not process.HasExited) check as you could otherwise lose the last line(s) of output from the program.
  • keenthinker
    keenthinker over 9 years
    @MarkHurd Thanks for the hint. I have removed the line.
  • Devenom
    Devenom over 9 years
    Hi @Dmitry Kurilo I have tried this method you have suggested. Thanks bro. But the thing is I want to put each line of output to a separate string. Later I'm gonna combine wanted lines and leave out the unwanted lines. and its not just for adb help. There are some other adb commands too that I wanna use. U get what I'm trying to do? Thanks Though.
  • Devenom
    Devenom over 9 years
    @pasty I am not working only with the adb help command. I am also using some other commands of adb. I know the output can vary in length. I simply gave an array string of 20 assuming that the number of output lines is 20. But the thing is I want to get each output line and save it in a separate string. Then Later I want to utilize only the required outlines and not the unwanted ones by making use of only the required strings. How do I save each line of out put to a separate string?
  • keenthinker
    keenthinker over 9 years
    I have updated my answer - now when the process is finished, only line 4 is outputed. You can change the code accordingly to your needs.
  • Devenom
    Devenom over 9 years
    Thanks a lot @Pasty. I'm kinda a newbie so I just need a little more help now. How do I save say Line 4 of the output to a String? and What if Line 4 is Empty or the end of the file? Thanks
  • keenthinker
    keenthinker over 9 years
    Since the list contains string, in order to save the line to another string variable, you just need to declare the variable and assign the line, for example: dim line as string = lines(4). You can always check if the string (in this case the line) is empty using the String.IsNullOrEmpty method. What do you mean by file? You do not have a file in this case but input (that is the output of another program in this particular case).
  • Devenom
    Devenom over 9 years
    I'm finding it a little difficult to understand. I got a perfect example. Say I'm using the command "adb devices -l" which lists all the running emulators. Now when I try pr.StantardOutPut.ReadLine() I get the output line by line. But the first seven lines are not needed. I skip them using a For loop. Now I want to store lines from the 8th line onwards into Strings. Say if there is only one emulator there will be 8 lines. If there is a 2nd emulator there will be 9 lines. But if I try to read more lines than there are it doesn't give any output. Can I email you my code for that part?
  • keenthinker
    keenthinker over 9 years
    You should not use for loops with hard coded indexes to skip lines. Reverse the logic - if you know, that you need everything from line 8 onwords start from line 8 and loop until the end. The code from my answer could be used to do that - for i as integer = 8 to list.count - 1 Console.WriteLine(list(i)) next i. Another possibility is to loop trough every line (not using hard coded index values) and search in every line for specific word - if the word occures, then show the line, else not.
  • Devenom
    Devenom over 9 years
    @Pasty I'm gonna confirm your answer(put the green tick) for sure but can you help me out. I find some things difficult to understand 'cause I'm a newbie. If i email you that part of the code can you fix it for me?
  • keenthinker
    keenthinker over 9 years
    I would prefer if you extend your question with the exact problem you are facing (what do you want to do exactly, what is the problem, what is the expected result, what is the result instead). Or we can have a chat later here on SO.
  • Devenom
    Devenom over 9 years
    @Pasty I need more points or something to chat. How about i put the code on paste bin? Just the part that I need to fix.
  • keenthinker
    keenthinker over 9 years
    Yup, I just saw that. If it is ok with you, paste the code in paste bin and I will have a look.
  • Devenom
    Devenom over 9 years
    Hi @Dmitry Kurilo. Thanks a lot bro. I fixed my problem. After taking a second look at your code I realized I had forgotten about this line. "pr.StandardInput.Close()" . I hadn't put it in my code. Works fine now. Thanks bro. God Bless You. Gonna give you the green tick after I apologize to Patsy for making him break his head on my code
  • Devenom
    Devenom over 9 years
    Hi @Pasty. I went through Dmitry Kurilo's answer again and I realised I hadn't put the line "pr.StandardInput.Close()". Now I've done that and its working fine. Sorry for making you go through all the trouble. I wanna give both of you the green tick mark but I don't know if that is possible. Thanks a lot man
  • keenthinker
    keenthinker over 9 years
    No problem. Glad that my answer helped you somehow to find the working solution for you! :)
  • Devenom
    Devenom over 9 years
    Thanks alot bro. I tried giving you the green tick but I can tick only one answer. Sorry about that. Btw do you if its possible to add files into your program itself. Like can I put the adb.exe file along with 2 dll files into my application itself?
  • keenthinker
    keenthinker over 9 years
    Yes, it is possible with ILMerge.
  • Devenom
    Devenom over 9 years
    So then can I link my program to execute commands from the files I put into my application?
  • keenthinker
    keenthinker over 9 years
    Yes. Have a look at the article and try it out.