.net – 运行命令行进程并在该进程仍在运行时获取输出?

如何在该进程仍在运行时运行命令行进程并获取输出?

我的意思是使用自己的进度条运行CLI进程,可执行文件本身需要很长时间才能完成操作,因此我想从自己的进程中获取进度信息以显示应用程序的进度,其他方式我不知道t有任何信息显示进度,直到过程结束.

我在WindowsForm项目中工作,而不是控制台应用程序.

我尝试使用FFMPEG.exe(x64)做同样的事情,我可以在FFMPEG运行时读取“进度”,我可以从FFMPEG中选择进度并做我想要的,但是使用这个可执行文件我就是不能这样做,我不知道是否可能.

该程序是“dbPowerAmp CoreConverter”,它是一个音乐转换器,我认为该程序以Unicode编码发送所有输出,因为要读取我需要将输出编码设置为Unicode的输出.

…其他问题是我找不到一种方法来读取此过程的StandardError输出,即使使用Unicode,所以如果有人可以帮助我解决这两个问题.

这是应用程序:http://www.dbpoweramp.com/install/dMC-R14.4-Ref-Trial.exe

以下是直接从CMD启动的程序的示例输出:

(我需要的是在进程运行时选择进度条“*”星号字符数量来计算并在我的应用程序中显示该百分比)

这是我的代码:

Private Shared CoreConverter As New Process()

Private Shared CoreConverter_Info As New ProcessStartInfo() With { _
              .CreateNoWindow = True, _
              .UseShellExecute = False, _
              .RedirectStandardOutput = True, _
              .RedirectStandardError = True _
}

Private Shared Sub Run_CoreConverter()


    ' Just for testing CMD Unicode output:
    '
    ' CoreConverter_Info.FileName = "cmd"
    ' CoreConverter_Info.Arguments = "/U /C C:\CoreConverter.exe " & CoreConverter_Info.Arguments


    CoreConverter_Info.FileName = "C:\CoreConverter.exe"
    CoreConverter_Info.Arguments = String.Format("-infile=""{0}"" -outfile=""{1}"" -convert_to=""mp3 (Lame)""" ..., blah blah blah)
    CoreConverter_Info.StandardErrorEncoding = System.Text.Encoding.Unicode
    CoreConverter_Info.StandardOutputEncoding = System.Text.Encoding.Unicode
    CoreConverter.StartInfo = CoreConverter_Info
    CoreConverter.EnableRaisingEvents = True
    CoreConverter.Start()
    CoreConverter.WaitForExit()


    Dim readerStdOut As IO.StreamReader = CoreConverter.StandardOutput

    ' This part works with FFMPEG executable but not with Coreconverter.exe,
    ' What I mean is that the msgbox is displayed when CoreConverter.exe finishes :( 
    ' but with FFMPEG I can read the output while FFMPEG still running.
    While CoreConverter.StandardOutput.EndOfStream = True
        MsgBox(CoreConverter.StandardOutput.ReadLine)
    End While

    If CoreConverter.ExitCode <> 0 Then
        ' Throw New Exception(CoreConverter.StandardError.ReadToEnd)

        ' No way to read the ErrorOutput...
        MessageBox.Show(CoreConverter.StandardError.ReadToEnd, "CoreConverter", MessageBoxButtons.OK, MessageBoxIcon.Error)

        MessageBox.Show(CoreConverter.StandardOutput.ReadToEnd, "CoreConverter", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End If

End Sub

UPDATE:

真的我非常沮丧,我所有的意图都是史诗般的失败,我已经尝试了所有我的技能可以做的事情(但这并不多).

即使使用多线程在分离的线程中运行进程,而我尝试从主线程获取输出,我也无法尝试检索进程输出,直到进程退出,这太疯狂了!我不明白为什么会发生这种情况.

有些人告诉我,或许制作一个COM对象,我可以做到这一点,这是一个疯狂的事情,好吧,我不想花几个月学习如何制作一个COM对象从控制台中挑选几个角色,那几个月的学习对我生活中的任何事情都没有帮助,就像那样,我需要更基本的东西,也许读取缓冲控制台的想法可行,但我肯定不会能够读取缓冲区,直到进程退出,所以.

所以.我该怎么办这个该死的过程?

只是为了更新我的问题来说出我自己的desesperation字样,我将在这里粘贴一个我尝试过的多线程的东西.

…就像我说的那样,当进程仍在运行时我无法尝试读取输出,并且即使进程完成(错误的故障),错误输出也无法被重新编译(字符串始终为空) .

Public Shared error_output As String
Public Shared standard_output As String
Public Shared active As Boolean = False ' CoreConverter Thread is active?

Public Shared MyThread As Threading.Thread = New Threading.Thread(AddressOf Run_CoreConverter)

Public Shared Sub Convert_To_MP3(ByVal In_File As String, _

' blah blah blah
'Run_CoreConverter()

    MyThread = New Threading.Thread(AddressOf Run_CoreConverter)
    ' MyThread.IsBackground = True
    active = True
    MyThread.Start()
    Get_Output()

End Sub

Public Shared Sub Get_Output()

While active ' While Coreconverter.exe is runing...

    Try
        If Not CoreConverter.HasExited Then
            MsgBox(CoreConverter.StandardOutput.ReadToEnd) ' This will not be displayed until process has exited...
        End If
    Catch ex As Exception

    End Try

    If error_output IsNot Nothing Then
        MsgBox(error_output) ' This will not be displayed until process has exited...
    End If

    If standard_output IsNot Nothing Then
        MsgBox(standard_output) ' This will not be displayed until process has exited...
    End If

End While

MsgBox("end active")

End Sub

Public Shared Sub Run_CoreConverter()
    CoreConverter_Info.FileName = CoreConverter_Location
    CoreConverter_Info.StandardErrorEncoding = System.Text.Encoding.Unicode
    CoreConverter_Info.StandardOutputEncoding = System.Text.Encoding.Unicode
    CoreConverter.StartInfo = CoreConverter_Info
    CoreConverter.EnableRaisingEvents = False

    CoreConverter.Start()
    ' CoreConverter.WaitForExit()

    ' Threading.Thread.Sleep(2000)
    ' For x As Integer = 0 To 99999999
    error_output = CoreConverter.StandardError.ReadToEnd
    standard_output = CoreConverter.StandardOutput.ReadToEnd
    ' Next

    If CoreConverter.ExitCode <> 0 Then
        Throw New Exception(CoreConverter.StandardError.ReadToEnd)
         MessageBox.Show(CoreConverter.StandardError.ReadToEnd, "CoreConverter", MessageBoxButtons.OK, MessageBoxIcon.Error)
         MessageBox.Show(CoreConverter.StandardOutput.ReadToEnd, "CoreConverter", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End If

    CoreConverter.Close()
    active = False

End Sub
最佳答案
我认为dbPowerAmp的编码可能存在错误.在cmd.exe / u Unicode环境中输出看起来很棒,但是当你挂钩.Net Process对象时,它最终会在字符之间出现空字节.您可以通过在良好的UTF8字符之间丢弃空字节来解决此问题.

我在PowerShell中更流利,所以这就是我写的,以证明这将有效.

$progressCounter = 0.0
$progressScaleString = "0%-----------25%-----------50%-----------75%-----------100%"
$psi = new-object system.diagnostics.processstartinfo
$psi.FileName = "C:\Program Files (x86)\Illustrate\dBpoweramp\CoreConverter.exe"
# Moby Dick audiobook available at <http://ia600208.us.archive.org/0/items/moby_dick_librivox/mobydick_135_melville.mp3>
$psi.arguments =  '-infile="C:\AudioBooks\mobydick_135_melville.mp3" -outfile="c:\AudioBooks\mobydick_135_melville.flac" -convert_to="flac" -encoding="SLOW"'
$psi.StandardOutputEncoding = [System.Text.Encoding]::Unicode
$psi.RedirectStandardOutput = $true
$psi.UseShellExecute = $false
$proc = new-object System.Diagnostics.Process
$proc.StartInfo = $psi
$proc.Start();
#Look for the magic progress bar string
$outputBuf = ""
while($true){ $chr = [char]$proc.StandardOutput.BaseStream.ReadByte(); if($chr -ne [char]0){ $outputBuf += $chr; } if($outputBuf.Contains($progressScaleString)){ break; }else{ sleep .01; }}
#We've seen the progress ruler, now start counting the pips.
while($progressCounter -le 100.0){ $chr = $proc.StandardOutput.BaseStream.ReadByte(); if($chr -lt 0){break;} if([char]$chr -eq [char]"*"){ $progressCounter += 100.0/($progressScaleString.Length+1); write-host $progressCounter; }}

这是我将此移植到VB的难点;只需在工作线程上运行它并将PercentDone挂钩到您的UI:

Public Event PercentDone(ByVal Percent As Float)

Private Shared CoreConverter As New Process()

Private Shared CoreConverter_Info As New ProcessStartInfo() With { _
              .CreateNoWindow = True, _
              .UseShellExecute = False, _
              .RedirectStandardOutput = True, _
              .RedirectStandardError = True _
}

Private Shared Sub Run_CoreConverter()

    Dim ProgressScaleString As String = "0%-----------25%-----------50%-----------75%-----------100%"
    Dim ProgressCounter As Float = 0.0

    CoreConverter_Info.FileName = "C:\CoreConverter.exe"
    CoreConverter_Info.Arguments = String.Format("-infile=""{0}"" -outfile=""{1}"" -convert_to=""mp3 (Lame)""" ..., blah blah blah)
    CoreConverter_Info.StandardErrorEncoding = System.Text.Encoding.Unicode
    CoreConverter_Info.StandardOutputEncoding = System.Text.Encoding.Unicode
    CoreConverter.StartInfo = CoreConverter_Info
    CoreConverter.Start()

    Dim OutputBuf As String = ""
    Dim chr As Byte;
    While True
      chr = CoreConverter.StandardOutput.BaseStream.ReadByte();
      If chr < 0 Then
        Exit While
      ElseIf chr <> 0
        OutputBuf += CType(chr, Char)
      End If
      If OutputBuf.Contains(ProgressScaleString) Then
        Exit While
      Else
        System.Threading.Thread.Sleep(10)
      End If
    End While

    While ProgressCounter <= 100
      chr = CoreConverter.StandardOutput.BaseStream.ReadByte()
      If chr <= 0 Then
        Exit While
      End If
      If chr == CType("*"C, Char) Then
        ProgressCounter += 100.0/($progressScaleString.Length+1)
        RaiseEvent PercentDone(ProgressCounter)
      End If
    End While

End Sub

转载注明原文:.net – 运行命令行进程并在该进程仍在运行时获取输出? - 代码日志