一旦我尝试读取标准输出或错误,VB.Net进程就会退出

时间:2013-03-22 21:05:41

标签: vb.net process redirectstandardoutput

初始问题

如果此问题已在其他地方提出并解决,我道歉;我搜索了这个网站,谷歌一直没有运气。

我试图编写一个简单的VB.Net Windows窗体应用程序,允许用户使用非常简单的GUI运行Windows文件比较程序(fc.exe)("浏览"按钮选择文件,复选框选择修饰符,输出文本框。

问题在于,每当我尝试从进程读取标准输出或错误时,它会立即停止,并且不会输出任何内容。我通过设置" createnowindow"来验证过程参数是否正确。为假,不重定向输出或错误。

为了查看进程是否实际运行,我放了一个" while"在proc.start之后循环:

Do While proc.HasExited = False
textbox.AppendText(i & vbNewLine)
i += 1
Loop

如果进程正常运行,我的计数最多可达80或90.如果我对标准输出或标准错误做任何事情,则文本框仅显示" 0"的初始值。通过"任何事情",我的意思是将proc.StandardOutput.ReadToEnd分配给变量。如果我使用proc.StandardOutput.Peek,则返回-1并且循环保持为0。

我注意到如果我只重定向输出错误(但不是两者),并且我启用进程打开一个新窗口,则新窗口为空并立即退出(即使我没有尝试在我的代码中读取重定向的流),如果两者都没有被重定向,它会显示几页结果,然后退出。我不知道这是否正常,或者文件比较可执行文件是以某种方式混合输出和错误流来生成其输出,或者如果这样的话甚至可能。

我对编码非常陌生(我已经和VB.net一起工作了大约一个月,这是我编程经验的程度),所以我和我的#39 ;我知道我的故障排除和假设可能完全偏离基础,我感谢任何人都可以提供的帮助。事实上,我完全陷入困境,而我的缺乏经验使得难以寻找替代方案(例如,我无法弄清楚如何正确处理异步输出)。作为参考,这是我现在令人尴尬的笨拙代码:

Dim cmdinput As String = """" & file1path & """" & " " & """" & file2path & """"
    Dim cmdmods As String = " "
    Dim i As Integer = 0
    Dim proc As New Process
    proc.StartInfo.CreateNoWindow = True
    proc.StartInfo.UseShellExecute = False
    proc.StartInfo.FileName = "C:\Windows\System32\fc.exe"
    proc.StartInfo.Arguments = cmdinput & cmdmods
    proc.StartInfo.RedirectStandardOutput = True
    proc.StartInfo.RedirectStandardError = True
    proc.Start()
    proc.StandardOutput.ReadToEnd()

    Do While proc.HasExited = False
        scanbox.AppendText(i & vbNewLine)

        i += 1
    Loop

可能的解决方案

在Hans Passant指出我应该看到错误之后,如果没别的话,我弄乱了我的代码,并且能够得到一个结果,尽管不是最优的结果。我没有直接运行FC.exe,而是运行了CMD.exe。我之前没有运气就试过这个,但那是因为CMD.exe不接受" fc" as process.startinfo.arguments。

我通过了" fc"到proc.standardinput.writeline()的cmd.exe。此时我能够读取CMD.exe的重定向输出。我仍然不知道为什么我不能直接阅读FC.exe,但在此期间这是一个非常好的创可贴。如果其他人觉得有必要将GUI添加到完美的命令行可执行文件并遇到问题,那么这就是我的代码:

Public Sub compare()
    Dim cmdinput As String = "fc " & """" & file1path & """" & " " & """" & file2path & """"
    Dim cmdmods As String = " "
    Dim proc As New Process
    proc.StartInfo.CreateNoWindow = True
    proc.StartInfo.UseShellExecute = False
    proc.StartInfo.FileName = "C:\Windows\System32\cmd.exe"
    proc.StartInfo.Arguments = cmdinput & cmdmods
    proc.StartInfo.RedirectStandardOutput = True
    proc.StartInfo.RedirectStandardError = True
    proc.StartInfo.RedirectStandardInput = True
    proc.Start()
    proc.StandardInput.WriteLine(cmdinput)
    proc.StandardInput.Close()
    scanbox.AppendText(proc.StandardOutput.ReadToEnd)
    proc.WaitForExit()
    proc.Close()
    proc.Dispose()
End Sub

我非常感谢Hans Passant和Dan Verdolino对我的漫无边际问题提出建议的耐心。我已经把头撞到了墙上一个星期,试图以某种方式将这种方式融合在一起。

2 个答案:

答案 0 :(得分:1)

解决方案

我没有直接运行FC.exe,而是运行了CMD.exe。我之前尝试过这个没有运气,但那是因为CMD.exe不接受“fc(args)”作为process.startinfo.arguments。

我通过proc.standardinput.writeline()将“fc(args)”传递给cmd.exe。此时我能够读取CMD.exe的重定向输出。我仍然不知道为什么我不能直接读取FC.exe的输出或错误,但在此期间这是一个非常好的创可贴。如果有人认为需要将GUI添加到完美的命令行可执行文件并遇到问题,那么这就是我的代码:

Public Sub compare()
    Dim cmdinput As String = "fc " & """" & file1path & """" & " " & """" & file2path & """"
    Dim cmdmods As String = " "
    Dim proc As New Process
    proc.StartInfo.CreateNoWindow = True
    proc.StartInfo.UseShellExecute = False
    proc.StartInfo.FileName = "C:\Windows\System32\cmd.exe"
    proc.StartInfo.Arguments = cmdinput & cmdmods
    proc.StartInfo.RedirectStandardOutput = True
    proc.StartInfo.RedirectStandardError = True
    proc.StartInfo.RedirectStandardInput = True
    proc.Start()
    proc.StandardInput.WriteLine(cmdinput)
    proc.StandardInput.Close()
    scanbox.AppendText(proc.StandardOutput.ReadToEnd)
    proc.WaitForExit()
    proc.Close()
    proc.Dispose()
End Sub

答案 1 :(得分:0)

Private Delegate Sub InvokeWithString(ByVal text As String)
Public Sub StartFC()
    Private psi As ProcessStartInfo
    Private cmd As Process

Dim CMDINPUT As String = "fc " & """" & file1path & """" & " " & """" & file2path & """"
       Dim FileToHit As String = "c:\windows\system32\fc.exe "

 psi = New ProcessStartInfo(FileToHit)
        Dim systemencoding As System.Text.Encoding = _
            System.Text.Encoding.GetEncoding(Globalization.CultureInfo.CurrentUICulture.TextInfo.OEMCodePage)

        With psi
            .Arguments = CMDINPUT 
            .UseShellExecute = False   ' Required for redirection
            .RedirectStandardError = True
            .RedirectStandardOutput = True
            .RedirectStandardInput = True
            .CreateNoWindow = True
            .StandardOutputEncoding = systemencoding  
            .StandardErrorEncoding = systemencoding
        End With

        ' EnableraisingEvents is required for Exited event
        cmd = New Process With {.StartInfo = psi, .EnableRaisingEvents = True}
        AddHandler cmd.ErrorDataReceived, AddressOf Async_Data_Received
        AddHandler cmd.OutputDataReceived, AddressOf Async_Data_Received
        AddHandler cmd.Exited, AddressOf CMD_Exited
        cmd.Start()
        pID1 = cmd.Id
        cmd.BeginOutputReadLine()
        cmd.BeginErrorReadLine()
        Me.txtInputStringIn.Select() ' textbox where you can send more commands. 
End Sub
'This event fires when process exited
 Private Sub CMD_Exited(ByVal sender As Object, ByVal e As EventArgs)
        'Me.Close()
        MessageBox.Show("Process is exited.")
    End Sub
'This part calls when Output received 
    Private Sub Async_Data_Received(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
        Me.Invoke(New InvokeWithString(AddressOf Sync_Output), e.Data)
    End Sub

    Private Sub Sync_Output(ByVal text As String)
'an output textbox will show the output of the command prompt.
        txtOutPut.AppendText(text & Environment.NewLine)
        txtOutPut.ScrollToCaret()
    End Sub