Управление cmd.exe из Winforms - PullRequest
10 голосов
/ 05 ноября 2010

Вопрос: я хочу управлять cmd.exe из winforms.

Я НЕ имею в виду каждую команду в одном процессе, с startupinfo, а затем останавливаю.

Я имею в виду, например, запустить командную строку (My) SQL или GDB, отправить команду, получить ответ, отправить следующую команду, получить следующий ответ, остановить командную строку SQL
процесс выхода.

По сути, я хочу написать GUI поверх любого консольного приложения.

Я хочу, чтобы вывод cmd.exe перенаправлялся в текстовое поле, а ввод - из другого текстового поля (при нажатии кнопки ввода / ОК).

Я не нахожу образцов для этого. Есть ли способ?

Ответы [ 4 ]

18 голосов
/ 05 ноября 2010

Вот хороший пример CodeProject

Удачи!

-Edit: Я думаю, что это больше похоже на это, я создал простую форму, 2 текстовых поля и три кнопки. Первое текстовое поле предназначено для ввода команды, второе (многострочное) отображает результат.

Первая кнопка выполняет команду, вторая кнопка обновляет результат (поскольку результаты считываются асинхронно)

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 \ для изменения каталога и следующую команду, выполненную в этом каталоге (каталог). alt text

2 голосов
/ 05 ноября 2010

Вам не нужно взаимодействие для этого. Класс .NET Process дает вам все, что вам нужно, просто перенаправьте стандартный поток ввода и вывода, и все готово. Вы можете найти множество примеров, как это сделать в Интернете.

1 голос
/ 05 ноября 2010

Для этого вам не нужно использовать cmd.exe, вы можете вызывать команды напрямую с помощью Process.Start().Если вы перенаправите StandardInput и StandardOutput, вы сможете контролировать процесс.

Я написал пример в ответ на другой вопрос .

Редактировать
У меня нет полного примера для этого, но вы можете прослушать StandardOutput с событием Process.OutputDataReceived, если вы не хотите ждать синхронно.На странице MSDN есть пример.

0 голосов
/ 07 ноября 2010

Это идеальный ответ:

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 outputtream.Peek ()! = -1, но это вызывало ошибку в функции Peek .NET Framework, которая нене превышайте тайм-аут или не выдавайте ошибку, если вы читаете через конец потока ...

Он работает лучше, поскольку он действительно перехватывает весь вывод, но он далек от совершенства.

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