从Winforms控制cmd.exe

时间:2010-11-05 16:01:47

标签: c# .net vb.net interop console

问题:我想从winforms控制cmd.exe。

我不是指使用startupinfo在单个进程中的每个命令,然后停止。

我的意思是例如启动(My)SQL或GDB命令提示符,发送命令,接收答案,发送下一个命令,接收下一个答案,停止SQL命令提示符
退出过程。

基本上我想在任何控制台应用程序之上编写GUI。

我希望将cmd.exe的输出重定向到文本字段,输入来自另一个文本字段(按下输入/确定按钮)。

我没有找到任何样品。 有办法吗?

4 个答案:

答案 0 :(得分:18)

CodeProject

上有一个很好的例子 祝你好运!

- 编辑: 我觉得这更像是它,我创建了一个简单的表单,2个文本框和3个按钮。 第一个文本框用于命令输入,第二个(多行)显示结果。

第一个按钮执行命令,第二个按钮更新结果(因为结果被读取为异步)

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        private static StringBuilder cmdOutput = null;
        Process cmdProcess;
        StreamWriter cmdStreamWriter;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            cmdOutput = new StringBuilder("");
            cmdProcess = new Process();

            cmdProcess.StartInfo.FileName = "cmd.exe";
            cmdProcess.StartInfo.UseShellExecute = false;
            cmdProcess.StartInfo.CreateNoWindow = true;
            cmdProcess.StartInfo.RedirectStandardOutput = true;

            cmdProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
            cmdProcess.StartInfo.RedirectStandardInput = true;
            cmdProcess.Start();

            cmdStreamWriter = cmdProcess.StandardInput;
            cmdProcess.BeginOutputReadLine();
        }

        private void btnExecute_Click(object sender, EventArgs e)
        {
            cmdStreamWriter.WriteLine(textBox2.Text);
        }

        private void btnQuit_Click(object sender, EventArgs e)
        {
            cmdStreamWriter.Close();
            cmdProcess.WaitForExit();
            cmdProcess.Close();
        }

        private void btnShowOutput_Click(object sender, EventArgs e)
        {
            textBox1.Text = cmdOutput.ToString();
        }

        private static void SortOutputHandler(object sendingProcess,
            DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                cmdOutput.Append(Environment.NewLine + outLine.Data);
            }
        }
    }
}

在屏幕截图中,您可以看到我输入了cd \命令来更改目录以及在此目录(dir)中执行的下一个命令。alt text

答案 1 :(得分:2)

你不需要互操作。 .NET Process类为您提供了所需的一切,只需重定向标准输入流和输出流即可完成。你可以在互联网上找到很多如何做到这一点的例子。

答案 2 :(得分:1)

您不需要使用cmd.exe,可以直接使用Process.Start()调用命令。如果重定向StandardInput和StandardOutput,则可以控制该过程。

我写了一个例子作为对another question的回复。

修改
我没有完整的示例,但如果您不想同步等待,则可以使用Process.OutputDataReceived事件收听StandardOutput。 MSDN页面上有一个示例。

答案 3 :(得分:0)

这是一个完美的答案:

using System;
using System.Windows.Forms;


namespace WindowsConsole
{


    public partial class Form1 : Form
    {
        System.Diagnostics.Process spdTerminal;
        System.IO.StreamWriter swInputStream;


        public delegate void fpTextBoxCallback_t(string strText);
        public fpTextBoxCallback_t fpTextBoxCallback;


        public Form1()
        {
            fpTextBoxCallback = new fpTextBoxCallback_t(AddTextToOutputTextBox);
            InitializeComponent();
        } // End Constructor


        public void AddTextToOutputTextBox(string strText)
        {
            this.txtOutput.AppendText(strText);
        } // End Sub AddTextToOutputTextBox


        private void btnQuit_Click(object sender, EventArgs e)
        {
            swInputStream.WriteLine("exit");
            swInputStream.Close();
            //spdTerminal.WaitForExit();
            spdTerminal.Close();
            spdTerminal.Dispose();
            Application.Exit();
        } // End Sub btnQuit_Click


        private void ConsoleOutputHandler(object sendingProcess, System.Diagnostics.DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                //this.Invoke(new fpTextBoxCallback_t(AddTextToOutputTextBox), Environment.NewLine + outLine.Data);
                if(this.InvokeRequired)
                    this.Invoke(fpTextBoxCallback, Environment.NewLine + outLine.Data);
                else
                    fpTextBoxCallback(Environment.NewLine + outLine.Data);
            } // End if (!String.IsNullOrEmpty(outLine.Data))

        } // End Sub ConsoleOutputHandler


        private void btnExecute_Click(object sender, EventArgs e)
        {
            if (this.spdTerminal.HasExited)
            {
                MessageBox.Show("You idiot, you have terminated the process", "Error");
                return;
            } // End if (this.spdTerminal.HasExited)

            swInputStream.WriteLine(txtInputCommand.Text);
        } // End Sub btnExecute_Click


        public void ProcessExited(object sender, EventArgs e)
        {
            MessageBox.Show("You idiot, you terminated the process.", "PBKAC");
        } // End Sub ProcessExited


        private void Form1_Load(object sender, EventArgs e)
        {
            spdTerminal = new System.Diagnostics.Process();

            if(Environment.OSVersion.Platform == PlatformID.Unix)
                //spdTerminal.StartInfo.FileName = "/usr/bin/gnome-terminal";
                spdTerminal.StartInfo.FileName = "/bin/bash";
            else
                spdTerminal.StartInfo.FileName = "cmd.exe";

            AddTextToOutputTextBox("Using this terminal: " + spdTerminal.StartInfo.FileName);

            spdTerminal.StartInfo.UseShellExecute = false;
            spdTerminal.StartInfo.CreateNoWindow = true;
            spdTerminal.StartInfo.RedirectStandardInput = true;
            spdTerminal.StartInfo.RedirectStandardOutput = true;
            spdTerminal.StartInfo.RedirectStandardError = true;

            spdTerminal.EnableRaisingEvents = true;
            spdTerminal.Exited += new EventHandler(ProcessExited);
            spdTerminal.ErrorDataReceived += new System.Diagnostics.DataReceivedEventHandler(ConsoleOutputHandler);
            spdTerminal.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(ConsoleOutputHandler);

            spdTerminal.Start();

            swInputStream = spdTerminal.StandardInput;
            spdTerminal.BeginOutputReadLine();
            spdTerminal.BeginErrorReadLine();
        } // End Sub Form1_Load


    } // End Class Form1


} // End Namespace WindowsConsole

早些时候,我尝试使用wile outputstream.Peek()!= -1 但是这会被.NET框架Peek函数中的一个错误所破坏,如果你读过流的末尾,它就不会超时或抛出错误......

它的效果更好,因为它真正捕获了所有输出,但它远非完美。

Public Class Form1


    ' That's our custom TextWriter class
    Private _writer As System.IO.TextWriter = Nothing

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        If p IsNot Nothing Then
            p.Close()
            p.Dispose()
            p = Nothing
        End If
    End Sub


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        InitProcess()
        '' Instantiate the writer
        '_writer = New ConsoleRedirection.TextBoxStreamWriter(Me.txtConsole)
        '' Redirect the out Console stream
        'Console.SetOut(_writer)
        'Console.WriteLine("Now redirecting output to the text box1")
        'Console.WriteLine("Now redirecting output to the text box2")
    End Sub

    Protected p As Process
    Protected sw As System.IO.StreamWriter
    Protected sr As System.IO.StreamReader
    Protected err As System.IO.StreamReader


    Protected objWriter As System.IO.StreamWriter
    Protected objWriteNumeric As System.IO.StreamWriter

    Private Sub InitProcess()
        p = New Process()

        Dim psI As New ProcessStartInfo("cmd")
        psI.UseShellExecute = False
        psI.RedirectStandardInput = True
        psI.RedirectStandardOutput = True
        psI.RedirectStandardError = True
        psI.CreateNoWindow = True
        p.StartInfo = psI
        p.Start()
        sw = p.StandardInput
        sr = p.StandardOutput
        err = p.StandardError
        sw.AutoFlush = True


        objWriter = New System.IO.StreamWriter("c:\temp\logmy.txt", True, System.Text.Encoding.ASCII)
        objWriteNumeric = New System.IO.StreamWriter("c:\temp\lognum.txt", True, System.Text.Encoding.ASCII)



        Timer1.Enabled = True
        Timer1.Start()

    End Sub

    Private Sub start()

        If Me.txtinput.Text <> "" Then
            sw.WriteLine(Me.txtinput.Text)
        Else
            'execute default command
            sw.WriteLine("dir c:\music")
        End If
        sw.Flush()

        Timer2.Enabled = True
    End Sub


    Private Sub start_original()
        p = New Process()
        Dim sw As System.IO.StreamWriter
        Dim sr As System.IO.StreamReader
        Dim err As System.IO.StreamReader
        Dim psI As New ProcessStartInfo("cmd")
        psI.UseShellExecute = False
        psI.RedirectStandardInput = True
        psI.RedirectStandardOutput = True
        psI.RedirectStandardError = True
        psI.CreateNoWindow = True
        p.StartInfo = psI
        p.Start()
        sw = p.StandardInput
        sr = p.StandardOutput
        err = p.StandardError
        sw.AutoFlush = True

        Me.txtinput.Text = "help"

        If Me.txtinput.Text <> "" Then
            sw.WriteLine(Me.txtinput.Text)
        Else
            'execute default command
            sw.WriteLine("dir \")
        End If
        sw.Close()



        'Me.txtConsole.Text = sr.ReadToEnd()

        'txtinput.Text = sr.ReadToEnd()
        'txtinput.Text += err.ReadToEnd()
    End Sub



    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        start()
    End Sub




    Protected sb As String = ""
    Sub ReadOutputStreamIfAvailable()
        'cbEndOfStream.Checked = sr.EndOfStream

        While True
            objWriteNumeric.WriteLine(sr.Peek().ToString())
            objWriteNumeric.Flush()



            If sr.Peek = -1 Then
                Exit While
            End If


            Dim iCharAsNumber As Integer = sr.Read()

            Dim cNumberAsChar As Char = Nothing
            If Not iCharAsNumber = Nothing Then
                Try
                    cNumberAsChar = Chr(iCharAsNumber)
                Catch
                    Continue While
                    'MsgBox(Prompt:=xx.ToString, Title:="Error")
                    'Exit While
                End Try

            End If

            Dim strCharAsString As String = ""
            If Not cNumberAsChar = Nothing Then
                strCharAsString = cNumberAsChar.ToString()
            End If

            sb += strCharAsString
        End While


        If Not String.IsNullOrEmpty(sb) Then
            'MsgBox(sb)
            MsgBox(sb)
            Me.txtConsole.Text += sb
            'MsgBox(sb)
            sb = ""
        End If
    End Sub






    Protected er As String = ""
    Sub ReadErrorStreamIfAvailable()
        'cbEndOfStream.Checked = sr.EndOfStream

        While True
            objWriteNumeric.WriteLine(sr.Peek().ToString())
            objWriteNumeric.Flush()


            If err.Peek = -1 Then
                Exit While
            End If


            Dim iCharAsNumber As Integer = err.Read()

            Dim cNumberAsChar As Char = Nothing
            If Not iCharAsNumber = Nothing Then
                Try
                    cNumberAsChar = Chr(iCharAsNumber)
                Catch
                    Continue While
                    'MsgBox(Prompt:=xx.ToString, Title:="Error")
                    'Exit While
                End Try

            End If

            Dim strCharAsString As String = ""
            If Not cNumberAsChar = Nothing Then
                strCharAsString = cNumberAsChar.ToString()
            End If

            er += strCharAsString
        End While


        If Not String.IsNullOrEmpty(er) Then
            'MsgBox(sb)
            'MsgBox(er)
            Me.txtConsole.Text += er
            'MsgBox(sb)
            er = ""
        End If
    End Sub



    Protected Shared objOutputStreamLocker As Object = New Object

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Timer1.Enabled = False

        SyncLock objOutputStreamLocker
            ReadOutputStreamIfAvailable()
            'ReadErrorStreamIfAvailable()
        End SyncLock

        Timer1.Enabled = True
    End Sub


    Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
        Try
            Timer2.Enabled = False
            sb = Chr(sr.Read()).ToString()
            ''
            'er = Chr(err.Read()).ToString()
            ''

            Timer1.Enabled = True
        Catch ex As Exception
            MsgBox("You have terminated the process", Title:="You idiot!")
        End Try
    End Sub


    ' http://www.c-sharpcorner.com/UploadFile/edwinlima/SystemDiagnosticProcess12052005035444AM/SystemDiagnosticProcess.aspx
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

    End Sub


End Class