【问题标题】:Visual Basic Capture output of cmdVisual Basic 捕获 cmd 的输出
【发布时间】:2013-12-20 16:08:51
【问题描述】:

我希望 Visual Basic 能够在目录“C:\projectTest\”上运行“make”命令。

我试过用这个:

    Dim output As String = String.Empty

    Using Process As New Process
        Process.StartInfo = New ProcessStartInfo("cmd")
        Process.StartInfo.WorkingDirectory = "C:\projectTest\"
        Process.StartInfo.UseShellExecute = False
        Process.StartInfo.CreateNoWindow = True
        Process.StartInfo.RedirectStandardInput = True
        Process.StartInfo.RedirectStandardOutput = True
        Process.StartInfo.RedirectStandardError = True
        Process.Start()
        Process.BeginOutputReadLine()
        AddHandler Process.OutputDataReceived,
 _
           Sub(processSender As Object, lineOut As DataReceivedEventArgs)
               output += lineOut.Data + vbCrLf
           End Sub

        Using InputStream As System.IO.StreamWriter = Process.StandardInput
            InputStream.AutoFlush = False
            InputStream.WriteLine("make")
        End Using
        Do
            Application.DoEvents()
        Loop Until Process.HasExited
    End Using

此代码能够捕获控制台的“gcc ...”部分(来自 Makefile),但不会捕获错误(如果我手动打开 cmd 并在该目录上运行 make 会弹出) .

如何捕获出现的所有内容,包括错误?

【问题讨论】:

    标签: .net windows visual-studio vba process


    【解决方案1】:

    不止一个问题。首先,正如@shf301 已经告诉你的那样,你忘了阅读标准错误。他又忘记添加额外的一行:

        Process.Start()
        AddHandler Process.OutputDataReceived, _
           Sub(processSender As Object, lineOut As DataReceivedEventArgs)
               output += lineOut.Data + vbCrLf
           End Sub
        Process.BeginOutputReadLine()
        AddHandler Process.ErrorDataReceived, _
           Sub(processSender As Object, lineOut As DataReceivedEventArgs)
               output += lineOut.Data + vbCrLf
           End Sub
        Process.BeginErrorReadLine()
    

    还有一个非常麻烦的问题,你的事件处理程序运行迟了。他们在进程退出后触发。这些处理程序在线程池线程上运行的副作用。在使用 output 变量之前,您需要等待任意(且无法猜测)的时间:

        Do
            Application.DoEvents()
        Loop Until Process.HasExited
        System.Threading.Thread.Sleep(1000)
    

    这太丑了。以 任何 IDE 或编辑器的方式执行此操作。将输出重定向到一个临时文件,然后读取该文件:

        Dim tempfile As String = System.IO.Path.GetTempFileName
        Using Process As New Process
            Process.StartInfo = New ProcessStartInfo("cmd.exe")
            Process.StartInfo.Arguments = "/c make 1> """ + tempfile + """ 2>&1"
            Process.StartInfo.WorkingDirectory = "C:\projectTest"
            Process.StartInfo.UseShellExecute = False
            Process.StartInfo.CreateNoWindow = True
            Process.Start()
            Process.WaitForExit()
            output = System.IO.File.ReadAllText(tempfile)
            System.IO.File.Delete(tempfile)
        End Using
    

    一些带有神秘命令行的注释:

    • /c 告诉 cmd.exe 只执行单个命令然后退出
    • 1> 将输出重定向到临时文件
    • 2>&1 告诉 cmd.exe 将 stderr 重定向到 stdout
    • 三重双引号确保临时文件名中的空格不会造成问题。

    同样的2>&1 也可以解决你原来的问题;)

    【讨论】:

    • 你是高手!这段代码似乎也比以前更快地编译了项目。 21 小时后我会奖励你的赏金:)
    【解决方案2】:

    错误通常写入StandardError 流,而您只读取StandardOutput 流。为ErrorDataReceived 事件添加一个事件处理程序,您应该会看到错误。

    AddHandler Process.ErrorDataReceived, _
           Sub(processSender As Object, lineOut As DataReceivedEventArgs)
               output += lineOut.Data + vbCrLf
           End Sub
    

    【讨论】:

    • 谢谢,我补充了,但它仍然具有完全相同的输出(缺少错误)。
    【解决方案3】:

    对于像我这样想知道为什么我们无法在异步模式下获取错误流的人。 添加行

    process.BeginErrorReadLine()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多