Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
911 views
in Technique[技术] by (71.8m points)

vb.net - How To Output Shell Command To a RichTextBox In Visual Basic

I've been struggling with this all week so I'm hoping the experts here can help me out. I have an executable that absolutely must be run from the command line with arguments. What I'm trying to do is instead of launching the command prompt window, I'd like to send the data to the rich text box on my form.

If I setup a batch file and run the batch file with the correct code (running it as a Process), this works no problem. However, I'd like for the user to be able to enter their own arguments into a TextBox instead of creating a batch file and referencing it.

I can only get this application to run correctly by using Call Shell. However, I read that you can't output the data to a RichTextBox if you're using Call Shell and that it needs to be setup as a new Process. I just can't seem to get this running as a Process.

So the question is, is it possible to somehow output the Call Shell data to a RichTextBox control, or is there a way to get this thing to run as a process? The Visual Basic code below will get it to run, but won't output to the RichTextBox. I removed any code that I tried because every try was a failure.

This button will start the Process, or if the Process is running, it will kill it.

    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
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It's quite possible using System.Diagnostics.Process.
The actual result depends on how that program actually works.
It might require some tests to get its output right.

This is a generic procedure you can use to test if your executable behaves in a standard way.
It's using tracert.exe and ouputs its results in a RichTextBox control.

Note that the Process.Start() initialization uses the Process.SynchronizingObject() set to the RichTextBox control Parent Form to avoid InvokeRequired. But if you don't want to use a Synch object, Control.Invoke is handled anyway, using MethodInvoker delegate.

To switch to your executable, subsitute the StartProcess() method parameters as required.

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:WindowsSystem32racert.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


Note:
If you need to terminate more that one running processes, you'll have to modify the code a bit more.
You could use a Class object to contain the Process Id, the Process Name (eventually) and a sequential value to maintain a reference to each process run.
Use a List(Of [Class]) for this.
You might also need to modify the StartProcess() method to pass a Control reference (the Control where the different processes output their results).
The code, as it is, needs very few modifications to achieve this.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...