narmi2
asked on
Capture commandline output
Hi
I am creating a program which runs a commandline in cmd. When the command is running, it displays progress information in the cmd.exe window. This progress is displayed as a %. How do I get that percentage into my program in a label?
Please have a look at the image below:
http://img249.imageshack.us/img249/1656/imageak2.png
As you can see, when i click the button, a cmd window opens and the processing starts, the red circle shows the percentage the processing is complete. I would like to hide the cmd window and display that percentage in my windows application instead.
How can I do this?
I am creating a program which runs a commandline in cmd. When the command is running, it displays progress information in the cmd.exe window. This progress is displayed as a %. How do I get that percentage into my program in a label?
Please have a look at the image below:
http://img249.imageshack.us/img249/1656/imageak2.png
As you can see, when i click the button, a cmd window opens and the processing starts, the red circle shows the percentage the processing is complete. I would like to hide the cmd window and display that percentage in my windows application instead.
How can I do this?
ASKER
I have tried implimenting that but i an not sure what I am doing... help
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEncode.Click
Dim myfile As New StreamWriter(Application.StartupPath & "\encode.bat")
myfile.WriteLine("flac -d --stdout """ & txtInput.Text & """ | lame " & txtSettings.Text & " - """ & txtOutput.Text & """")
myfile.Close()
myfile.Dispose()
' Set start information.
Dim start_info As New ProcessStartInfo
start_info.WorkingDirectory = Application.StartupPath
start_info.FileName = "encode.bat"
start_info.UseShellExecute = False
start_info.CreateNoWindow = False
start_info.RedirectStandardOutput = True
start_info.RedirectStandardError = False
' Make the process and set its start information.
Dim proc As New Process()
proc.StartInfo = start_info
' Start the process.
AddHandler proc.OutputDataReceived, AddressOf OutputHandler
proc.Start()
proc.BeginOutputReadLine()
proc.WaitForExit()
proc.Close()
File.Delete(Application.StartupPath & "\encode.bat")
End Sub
Private Sub OutputHandler(ByVal sendingProcess As Object, ByVal outLine As DataReceivedEventArgs)
'-- Here, you can analyse the output using outLine.Data
Control.CheckForIllegalCrossThreadCalls = False
TextBox1.Text = outLine.Data.ToString
End Sub
After run, any output at TextBox1?
I've modified the code, is it work for you?
PS: I dont have VS2005 for run test ;-)
I've modified the code, is it work for you?
PS: I dont have VS2005 for run test ;-)
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEncode.Click
Dim myfile As New StreamWriter(Application.StartupPath & "\encode.bat")
myfile.WriteLine("flac -d --stdout """ & txtInput.Text & """ | lame " & txtSettings.Text & " - """ & txtOutput.Text & """")
myfile.Close()
myfile.Dispose()
' Set start information.
Dim start_info As New ProcessStartInfo
start_info.WorkingDirectory = Application.StartupPath
start_info.FileName = "encode.bat"
start_info.UseShellExecute = False
start_info.CreateNoWindow = True '<------------- (1) Hide the output
start_info.RedirectStandardOutput = True
start_info.RedirectStandardError = False
' Make the process and set its start information.
Dim proc As New Process()
proc.StartInfo = start_info
' Start the process.
AddHandler proc.OutputDataReceived, AddressOf OutputHandler
AddHandler proc.Exited, AddressOf Batch_Exited '<------ (2) Delete file on batch finished
proc.Start()
proc.BeginOutputReadLine() '<----(3) Begin asynchronous read the output
'proc.WaitForExit() '<----------(4) Don't need to wait
'proc.Close()
End Sub
Private Sub OutputHandler(ByVal sendingProcess As Object, ByVal outLine As DataReceivedEventArgs)
'-- Here, you can analyse the output using outLine.Data
Control.CheckForIllegalCrossThreadCalls = False
TextBox1.Text = GetPercent( outLine.Data.ToString ) '<---------(5) Send the output to get only percent number
End Sub
Private Sub Batch_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
File.Delete(Application.StartupPath & "\encode.bat") '<-------(6) Perform delete file on process finished
End Sub
Function GetPercent(ByVal output As String) As String '<----(7) Function to parse for percent number using regular expression
Dim rx As System.Text.RegularExpressions.Regex = _
New System.Text.RegularExpressions.Regex("([0-9]{1,3})\s*%", _
RegexOptions.IgnoreCase Or RegexOptions.Multiline)
Dim founds As MatchCollection = rx.Matches(output)
If founds.Count > 0 Then Return founds(0).Groups(1).Value
Return Nothing
End Function
ASKER
Thank you gnoon. I will try this when I get home. Thanks again.
ASKER
I am getting an error on the following line
TextBox1.Text = GetPercent(outLine.Data.To String)
The error message is
Object reference not set to an instance of an object
Any ideas?
TextBox1.Text = GetPercent(outLine.Data.To
The error message is
Object reference not set to an instance of an object
Any ideas?
The error will be happen if an variable has not been initialized, but its method/property is being access.
I think, two possible cases
- outLine.Data is Nothing, then we're trying to call outLine.Data.ToString
- GetPercent is not visible as a function in your class, but it's looked like an array
Resolution
- check outLine.Data before access its methods
- define the function in class, copy/paste from my code above
Also, try this
I think, two possible cases
- outLine.Data is Nothing, then we're trying to call outLine.Data.ToString
- GetPercent is not visible as a function in your class, but it's looked like an array
Resolution
- check outLine.Data before access its methods
- define the function in class, copy/paste from my code above
Also, try this
Private Sub OutputHandler(ByVal sendingProcess As Object, ByVal outLine As DataReceivedEventArgs)
'-- Here, you can analyse the output using outLine.Data
If Not String.IsNullOrEmpty(outLine.Data) Then
Control.CheckForIllegalCrossThreadCalls = False
TextBox1.Text = GetPercent( outLine.Data.ToString ) '<---------(5) Send the output to get only percent number
End If
End Sub
ASKER
Ok i tried that but it shows nothing in the textbox1? the processing part of it works fine, it is just that, it gives me no feedback as to how much is done
Sorry narmi2, I just installed VS2005 on my comp and did test the code above.
I see that it need a line of code to achieve asynchronous read
proc.WaitForExit()
It works if this code exists, otherwise it ends the program before the next read is reached.
Sorry again that I made you confused.
I see that it need a line of code to achieve asynchronous read
proc.WaitForExit()
It works if this code exists, otherwise it ends the program before the next read is reached.
Sorry again that I made you confused.
ASKER
No problem at all. I will try that out as soon as I can, which will probably be at the end of the day. Thanks again for all the help. :)
ASKER
gnoon: Where do I add that line of code?
After the line of proc.BeginOutputReadLine() .
ASKER
i did that and it still did not work. So I did a little testing by changing the following line:
TextBox1.Text = GetPercent( outLine.Data.ToString ) '<---------(5) Send the output to get only percent number
to this:
TextBox1.Text = outLine.Data.ToString
And it only returned the following text:
C:\Document and Settings\narmi2\My Documents\Visual Studio 2000\Projects\Windows\Appl ication1\W indowsAppl ication1\b in\Debug\f lac -d --stdout "c:\audio.flac" |lame -V9 - "c:\audio.mp3"
That is it!
it did not return the rest of the output for some reason, which explains why it did not display the percentage complete in the textbox.
do you know why it is not returning the full text? You can see the full text in the image in the first post i made.
TextBox1.Text = GetPercent( outLine.Data.ToString ) '<---------(5) Send the output to get only percent number
to this:
TextBox1.Text = outLine.Data.ToString
And it only returned the following text:
C:\Document and Settings\narmi2\My Documents\Visual Studio 2000\Projects\Windows\Appl
That is it!
it did not return the rest of the output for some reason, which explains why it did not display the percentage complete in the textbox.
do you know why it is not returning the full text? You can see the full text in the image in the first post i made.
It prints the full text because the @echo command is turned on. If you turn it off, the full text should not printed.
myfile.WriteLine("@echo off") '<------------ turn off echo command itself
myfile.WriteLine("flac -d --stdout """ & txtInput.Text & """ | lame " & txtSettings.Text & " - """ & txtOutput.Text & """")
This is code I used to test capture. It's Console Application. Also, you can test it :-)
myfile.WriteLine("@echo off") '<------------ turn off echo command itself
myfile.WriteLine("flac -d --stdout """ & txtInput.Text & """ | lame " & txtSettings.Text & " - """ & txtOutput.Text & """")
This is code I used to test capture. It's Console Application. Also, you can test it :-)
Imports System.IO
Imports System.Text.RegularExpressions
Module Module1
Dim BaseDir As String = "c:"
Sub Main()
RunBatch()
End Sub
Sub RunBatch()
Dim myfile As New StreamWriter(BaseDir & "\encode.bat")
myfile.WriteLine("@ECHO OFF")
myfile.WriteLine("SET COUNT=0")
myfile.WriteLine(":Loop")
myfile.WriteLine("cls")
myfile.WriteLine("echo Encoding as 24 KHz VBR(q=9) j-stereo MPEG-2 Layer III (ca. 16.5x) qval=3")
myfile.WriteLine("echo audio.flac: %COUNT%%% complete")
myfile.WriteLine("SET /A COUNT=COUNT+1")
myfile.WriteLine("@ping 127.0.0.1 -n 2 -w 1000 > nul")
myfile.WriteLine("@ping 127.0.0.1 -n %1% -w 1000> nul")
myfile.WriteLine("goto Loop")
myfile.Close()
myfile.Dispose()
' Set start information.
Dim start_info As New ProcessStartInfo
start_info.WorkingDirectory = BaseDir
start_info.FileName = BaseDir & "\encode.bat"
start_info.UseShellExecute = False
start_info.CreateNoWindow = True '<------------- (1) Hide the output
start_info.RedirectStandardOutput = True
start_info.RedirectStandardError = False
' Make the process and set its start information.
Dim proc As New Process()
proc.StartInfo = start_info
' Start the process.
AddHandler proc.OutputDataReceived, AddressOf OutputHandler
AddHandler proc.Exited, AddressOf Batch_Exited '<------ (2) Delete file on batch finished
proc.Start()
proc.BeginOutputReadLine() '<----(3) Begin asynchronous read the output
proc.WaitForExit() '<----------(4) Don't need to wait
'proc.Close()
End Sub
Private Sub OutputHandler(ByVal sendingProcess As Object, ByVal outLine As DataReceivedEventArgs)
'-- Here, you can analyse the output using outLine.Data
If Not String.IsNullOrEmpty(outLine.Data) Then
Console.WriteLine(GetPercent(outLine.Data.ToString)) '<---------(5) Send the output to get only percent number
Else
Console.WriteLine("NULL!!!")
End If
End Sub
Private Sub Batch_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
File.Delete(BaseDir & "\encode.bat") '<-------(6) Perform delete file on process finished
End Sub
Function GetPercent(ByVal output As String) As String '<----(7) Function to parse for percent number using regular expression
Dim rx As System.Text.RegularExpressions.Regex = _
New System.Text.RegularExpressions.Regex("([0-9]{1,3})\s*%", _
RegexOptions.IgnoreCase Or RegexOptions.Multiline)
Dim founds As MatchCollection = rx.Matches(output)
If founds.Count > 0 Then Return founds(0).Groups(1).Value
Return Nothing
End Function
End Module
ASKER
Thank you. I will try this tonight.
ASKER
I tried that and it is not working, it produces no output at all in the text box. Can you please have a look at the project, you might see something which I have not seen. I have zipped the whole project: https://filedb.experts-exchange.com/incoming/ee-stuff/6028-WindowsApplication1.zip
OK, I will check it out this tonight when I got home.
ASKER
To get it to run, you will need lame.exe from this download: http://audio.ciara.us/rarewares/lame3.97.zip
and flac.exe from this download: http://switch.dl.sourceforge.net/sourceforge/flac/flac-1.2.1-win.zip
also, you will need a .flac audio file to convert. you can easily make on of those with the flac encoder above if you have a wav file already. if you need help with any of the above, please let me know.
and flac.exe from this download: http://switch.dl.sourceforge.net/sourceforge/flac/flac-1.2.1-win.zip
also, you will need a .flac audio file to convert. you can easily make on of those with the flac encoder above if you have a wav file already. if you need help with any of the above, please let me know.
ASKER
Hi
Looks like I need to capture the StandardError instead of the StandardOutput as the bit that I am interested in, the percentage complete, comes in the StandardError and not in the StandardOutput. I will try this when I get home in the evening...
Please let me know if you have any luck with this.
Looks like I need to capture the StandardError instead of the StandardOutput as the bit that I am interested in, the percentage complete, comes in the StandardError and not in the StandardOutput. I will try this when I get home in the evening...
Please let me know if you have any luck with this.
I tested the code. Yes, as you said. Lame prints out the percent message on error stream.
There are issues here about the code
- better start the process in another thread than UI-thread to prevent screen frozen.
- you need to check for not empty output before set to textbox.
- proc.Exited will be invoked only if proc.EnableRaisingEvents = True
- since you use AddHandler (asynchronous reading) and it's Win Form App (event driven programming), proc.WaitForExit() is not needed ;-)
Here the code I modified
There are issues here about the code
- better start the process in another thread than UI-thread to prevent screen frozen.
- you need to check for not empty output before set to textbox.
- proc.Exited will be invoked only if proc.EnableRaisingEvents = True
- since you use AddHandler (asynchronous reading) and it's Win Form App (event driven programming), proc.WaitForExit() is not needed ;-)
Here the code I modified
Imports System.IO
Imports System.Threading
Imports System.Text.RegularExpressions
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEncode.Click
'move code here into a thread to prevent screen frozen
Dim t As Thread = New Thread(AddressOf ProcessInThread)
t.Name = "ConvertThread"
t.IsBackground = True
t.Start()
'ProcessInThread()
End Sub
Private Sub ProcessInThread()
Dim myfile As New StreamWriter(Application.StartupPath & "\encode.bat")
myfile.WriteLine("@echo off")
myfile.WriteLine("flac -d --stdout """ & txtInput.Text & """ | lame " & txtSettings.Text & " - """ & txtOutput.Text & """")
myfile.Close()
myfile.Dispose()
' Set start information.
Dim start_info As New ProcessStartInfo
start_info.WorkingDirectory = Application.StartupPath
start_info.FileName = "encode.bat"
start_info.UseShellExecute = False
start_info.CreateNoWindow = True
start_info.RedirectStandardOutput = True
start_info.RedirectStandardError = True
' Make the process and set its start information.
Dim proc As New Process()
proc.StartInfo = start_info
proc.EnableRaisingEvents = True
' Start the process.
AddHandler proc.OutputDataReceived, AddressOf OutputHandler '<----- isn't need
AddHandler proc.ErrorDataReceived, AddressOf ErrorHandler
AddHandler proc.Exited, AddressOf Batch_Exited
proc.Start()
proc.BeginOutputReadLine()
proc.BeginErrorReadLine()
'proc.WaitForExit()
'proc.Close()
End Sub
Private Sub OutputHandler(ByVal sendingProcess As Object, ByVal outLine As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(outLine.Data) Then
Dim percent As String = GetPercent(outLine.Data.ToString)
Console.WriteLine("output: " & outLine.Data & " [" & percent & "]")
If Not String.IsNullOrEmpty(percent) Then
Control.CheckForIllegalCrossThreadCalls = False
TextBox1.Text = percent
'Console.WriteLine(percent)
End If
End If
End Sub
Private Sub ErrorHandler(ByVal sendingProcess As Object, ByVal outLine As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(outLine.Data) Then
Dim percent As String = GetPercent(outLine.Data.ToString)
Console.WriteLine("error: " & outLine.Data & " [" & percent & "]")
If Not String.IsNullOrEmpty(percent) Then
Control.CheckForIllegalCrossThreadCalls = False
TextBox1.Text = percent
'Console.WriteLine(percent)
End If
End If
End Sub
Private Sub Batch_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
Console.WriteLine("exit")
File.Delete(Application.StartupPath & "\encode.bat")
End Sub
Function GetPercent(ByVal output As String) As String
Dim rx As System.Text.RegularExpressions.Regex = _
New System.Text.RegularExpressions.Regex("([0-9]{1,3})\s*%", _
RegexOptions.IgnoreCase Or RegexOptions.Multiline)
Dim founds As MatchCollection = rx.Matches(output)
If founds.Count > 0 Then Return founds(0).Groups(1).Value
Return Nothing
End Function
Private Sub btnChoose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChoose.Click
OpenFileDialog1.Filter = "Flac files (*.flac)|*.flac"
If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
txtInput.Text = OpenFileDialog1.FileName
txtOutput.Text = Replace(OpenFileDialog1.FileName, ".flac", ".mp3")
End If
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
SaveFileDialog1.Filter = "mp3 files (*.mp3)|*.mp3"
If SaveFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
txtOutput.Text = SaveFileDialog1.FileName
End If
End Sub
End Class
ASKER
Hi, thanks for that. It is not doing what I was hoping for. This code displays the percentage when the process is complete, I was hoping to display the percentage as the process was processing, so you would see the percentage incrementing in the textbox.
>I was hoping to display the percentage as the process was processing
Actually, it did that. I think, the process is too fast, so you can see only 100% progress. You may try with a larger file. It's also happen on my comp.
However, I will help to check it again.
Actually, it did that. I think, the process is too fast, so you can see only 100% progress. You may try with a larger file. It's also happen on my comp.
However, I will help to check it again.
ASKER
I tried a large 80Mb flac file and nothing came in the textbox at all. It just displayed 100 when the processing finished. The time it took to process that 80Mb was 1 or 2 minutes.
ASKER
Please click on the link below to see an swf file of what happens.
http://www40.brinkster.com/namiiii/encoding.swf.html
you might have to use internet explorer 7 to open that link.
anyway, as you can see, when it is processing, nothing happens in the textbox. only after the processing is finished, does the percentage go into the textbox.
how do i make it more real time?
http://www40.brinkster.com/namiiii/encoding.swf.html
you might have to use internet explorer 7 to open that link.
anyway, as you can see, when it is processing, nothing happens in the textbox. only after the processing is finished, does the percentage go into the textbox.
how do i make it more real time?
Hi, sorry for late response.
I tried to run only flac in the Process
flac -d -f "c:\audio.flac"
, get the same result. It's lazy of print out.
Also, I searched over the net, and get nothing helpful.
I think it's something about cache of flac.exe or the difference between 32-bit GUI and 16-bit Console.
(This article is useful http://www.codeproject.com/KB/threads/redir.aspx)
However, I have an idea of solution. Lets called "C# pipeline".
Instead of redirecting flac's output to lamp directly, as input, just redirect it to C# then forward to lamp afterward.
This can be done by define two Process in code. One handles flac.exe and get its output at the ErrorHandler, then send the output (array of byte) to the second process of lame.exe. The second process produces MP3 file using the array of byte received. Count the bytes received as progress.
Just an idea. I will look for a free time to test it.
I tried to run only flac in the Process
flac -d -f "c:\audio.flac"
, get the same result. It's lazy of print out.
Also, I searched over the net, and get nothing helpful.
I think it's something about cache of flac.exe or the difference between 32-bit GUI and 16-bit Console.
(This article is useful http://www.codeproject.com/KB/threads/redir.aspx)
However, I have an idea of solution. Lets called "C# pipeline".
Instead of redirecting flac's output to lamp directly, as input, just redirect it to C# then forward to lamp afterward.
This can be done by define two Process in code. One handles flac.exe and get its output at the ErrorHandler, then send the output (array of byte) to the second process of lame.exe. The second process produces MP3 file using the array of byte received. Count the bytes received as progress.
Just an idea. I will look for a free time to test it.
ASKER
That sounds interesting!
Do you have any links about how to get flac to decode directly into .net byte?
Do you have any links about how to get flac to decode directly into .net byte?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Line 58 should be this
lame_proc.StandardInput.Ba seStream.W rite(buffD ata, 0, bytesRead)
not this
lame_proc.StandardInput.Ba seStream.W rite(buffD ata, 0, buffSize)
Sorry.
lame_proc.StandardInput.Ba
not this
lame_proc.StandardInput.Ba
Sorry.
ASKER
Hi
Thanks
That works a lot better than before. But just 1 question, why is there a delay between 99 and 100?
Thanks
That works a lot better than before. But just 1 question, why is there a delay between 99 and 100?
>why is there a delay between 99 and 100?
Just to make sure that user will see 100% only when the convert was already done.
We use the fixed-ratio = 0.72, but in real this number can be vary depend on option used by flac.
So, it's possible to get 100% (or more than 100) inside the while loop, but not long time it will break out.
Just to make sure that user will see 100% only when the convert was already done.
We use the fixed-ratio = 0.72, but in real this number can be vary depend on option used by flac.
So, it's possible to get 100% (or more than 100) inside the while loop, but not long time it will break out.
start_info.RedirectStandar
then just before proc.Start(), add an event handler
AddHandler proc.OutputDataReceived, AddressOf OutputHandler
Next, define a sub to handle the event
Private Sub OutputHandler(sendingProce
'-- Here, you can analyse the output using outLine.Data
End Sub
Finally, after you call proc.Start(), just call
proc.BeginOutputReadLine()