【问题标题】:How To Output Shell Command To a RichTextBox In Visual Basic如何在 Visual Basic 中将 Shell 命令输出到 RichTextBox
【发布时间】:2018-12-21 02:50:57
【问题描述】:

我整个星期都在为此苦苦挣扎,所以我希望这里的专家可以帮助我。我有一个绝对必须从带有参数的命令行运行的可执行文件。我想要做的不是启动命令提示符窗口,而是将数据发送到表单上的富文本框。

如果我设置了一个批处理文件并使用正确的代码运行该批处理文件(以Process 运行它),这没有问题。但是,我希望用户能够将自己的参数输入到TextBox,而不是创建批处理文件并引用它。

我只能通过使用 Call Shell 来让这个应用程序正确运行。但是,我读到如果您使用的是 Call Shell,则无法将数据输出到RichTextBox,并且需要将其设置为新的Process。我似乎无法以Process 的身份运行它。

所以问题是,是否有可能以某种方式将 Call Shell 数据输出到RichTextBox 控件,或者有没有办法让这个东西作为一个进程运行?下面的 Visual Basic 代码将使其运行,但不会输出到 RichTextBox。我删除了我尝试过的所有代码,因为每次尝试都失败了。

此按钮将启动Process,或者如果Process 正在运行,它将杀死它。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim exeargs As String = txtExeArgs.Text
    Dim p1() As Process
    Dim strCommand As String = "executable.exe " & exeargs & ""
    p1 = Process.GetProcessesByName("executable")
    Dim exepath As String = IO.Path.GetDirectoryName(Me.txtExeLocation.Text)

    If p1.Count <= 0 Then

        RichTextBox1.Clear()

        Call Shell("cmd.exe /c cd /d " & exepath & " & " & strCommand, 0)

    Else
        Dim killprocess = System.Diagnostics.Process.GetProcesses().Where((Function(p) p.ProcessName = "executable"))
        For Each p As Process In killprocess
            p.Kill()
        Next
        RichTextBox1.Clear()
    End If
End Sub

【问题讨论】:

    标签: vb.net shell command-line richtextbox


    【解决方案1】:

    很可能使用System.Diagnostics.Process
    实际结果取决于该程序的实际工作方式。
    它可能需要一些测试才能正确输出。

    这是一个通用过程,可用于测试您的可执行文件是否以标准方式运行。
    它使用tracert.exe 并将其结果输出到RichTextBox 控件中。

    注意Process.Start()初始化使用Process.SynchronizingObject()设置为RichTextBox控制Parent Form来避免InvokeRequired。但是,如果您不想使用同步对象,则无论如何都会使用 MethodInvoker 委托处理 Control.Invoke

    要切换到您的可执行文件,请根据需要替换 StartProcess() 方法参数。

    Imports System.Diagnostics
    Imports System.IO
    
    Private CurrentProcessID As Integer = -1
    
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        StartProcess("C:\Windows\System32\tracert.exe", "stackoverflow.com")
    End Sub
    
    Private Sub StartProcess(FileName As String, Arguments As String)
    
        Dim MyStartInfo As New ProcessStartInfo() With {
            .FileName = FileName,
            .Arguments = Arguments,
            .WorkingDirectory = Path.GetDirectoryName(FileName),
            .RedirectStandardError = True,
            .RedirectStandardOutput = True,
            .UseShellExecute = False,
            .CreateNoWindow = True
        }
    
        Dim MyProcess As Process = New Process() With {
            .StartInfo = MyStartInfo,
            .EnableRaisingEvents = True,
            ' Setting a SynchronizingObject, we don't need to BeginInvoke. 
            ' I leave it there anyway, in case there's no SynchronizingObject to set
            ' BeginInvoke can be used with or without a synchronization context.
            .SynchronizingObject = Me
        }
    
        MyProcess.Start()
        MyProcess.BeginErrorReadLine()
        MyProcess.BeginOutputReadLine()
    
        CurrentProcessID = MyProcess.Id
    
        AddHandler MyProcess.OutputDataReceived,
            Sub(sender As Object, e As DataReceivedEventArgs)
                If e.Data IsNot Nothing Then
                    BeginInvoke(New MethodInvoker(
                    Sub()
                        RichTextBox1.AppendText(e.Data + Environment.NewLine)
                        RichTextBox1.ScrollToCaret()
                    End Sub))
                End If
            End Sub
    
        AddHandler MyProcess.ErrorDataReceived,
            Sub(sender As Object, e As DataReceivedEventArgs)
                If e.Data IsNot Nothing Then
                    BeginInvoke(New MethodInvoker(
                    Sub()
                        RichTextBox1.AppendText(e.Data + Environment.NewLine)
                        RichTextBox1.ScrollToCaret()
                    End Sub))
                End If
            End Sub
    
        AddHandler MyProcess.Exited,
            Sub(source As Object, ev As EventArgs)
                MyProcess.Close()
                If MyProcess IsNot Nothing Then
                    MyProcess.Dispose()
                End If
            End Sub
    End Sub
    


    注意
    如果您需要终止多个正在运行的进程,则必须对代码进行更多修改。
    您可以使用 Class 对象来包含进程 ID、进程名称(最终)和一个顺序值,以维护对每个进程运行的引用。
    为此使用List(Of [Class])
    您可能还需要修改 StartProcess() 方法以传递 Control 引用(不同进程输出其结果的 Control)。
    代码实际上只需要很少的修改即可实现这一点。

    【讨论】:

    • 哇!这很顺利。绝对精彩的答案。非常感谢!
    • 您应该避免使用IsNothing() 函数,原因是提到了here,而是检查If MyProcess IsNot Nothing Then。除此之外,为什么投反对票的人认为这有资格投反对票?
    • 另外,您可以将Dim mi As MethodInvoker = ... 移到InvokeRequired 检查之外,然后在Else 语句中调用mi.Invoke()。这允许您在两种情况下都使用mi 委托,从而无需编写两次代码。 :)
    • @Visual Vincent 是的,正如我所写的,那是某种出局。目的是让 OP 选择一种方法或另一种方法,删除不必要的部分,这取决于他最喜欢什么(如果您使用同步对象,则不需要InvokeRequired 检查;如果您不需要) t,您不需要 Else 部分)。无论如何,我会给它一个调整。 (此外,这或多或少是您在 MSDN 示例中找到的内容。如果您不习惯,它会更容易理解。
    • @Visual Vincent 已编辑。两种方式都应该可以正常工作(您不需要关心是否使用同步对象)。欢迎您提出任何进一步的建议:)
    猜你喜欢
    • 2015-02-11
    • 2021-02-04
    • 1970-01-01
    • 2016-04-21
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 2013-05-30
    相关资源
    最近更新 更多