2010-11-05 8 views
9

Domanda: Voglio controllare cmd.exe da winforms.

NON intendo ogni comando in un singolo processo, con startupinfo, e quindi si interrompe.

mi riferisco ad esempio avviare il (mio) prompt dei comandi SQL o GDB, inviare il comando, ricevere risposta, inviare il comando successivo, ricevere risposta successiva, comando SQL fermare prompt dei
processo di uscita.

In pratica, voglio scrivere una GUI su qualsiasi applicazione per console.

Desidero avere l'output da cmd.exe reindirizzato a un campo di testo e l'input proveniente da un altro campo di testo (premere Invio/pulsante OK).Controllo cmd.exe da Winforms

Non trovo campioni per questo. C'è un modo?

risposta

16

C'è un bel esempio su CodeProject

Buona fortuna!

-Modulo: Penso che questo sia più simile, ho creato un semplice modulo, 2 caselle di testo e tre pulsanti. La prima casella di testo è per l'immissione del comando, la seconda (multilinea), visualizza il risultato.

Il primo pulsante esegue il comando, il secondo pulsante aggiorna il risultato (asincrona perché i risultati vengono letti)

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); 
      } 
     } 
    } 
} 

Nello screenshot si può vedere che ho inserito il comando cd \ cambiare directory e la prossima comando eseguito in questa directory (dir). alt text

+0

ciao, come posso scrivere Ctrl + C in comando? –

2

Non è necessario eseguire un'interop per questo. La classe .NET Process ti offre tutto ciò di cui hai bisogno, semplicemente reindirizzando il flusso di input standard e il flusso di output e il gioco è fatto. Puoi trovare molti esempi su come farlo su internet.

+1

Sì, ma non è così semplice se si desidera emettere più comandi uno dopo l'altro NELLO STESSO PROCESSO, dove il secondo comando non è noto al momento della compilazione, come detto. E per quello, non trovi nessuno che funzioni. –

1

Non è necessario utilizzare cmd.exe per questo, è possibile chiamare i comandi direttamente con Process.Start(). Se si reindirizza StandardInput e StandardOutput, è possibile controllare il processo.

Ho scritto un esempio come risposta a another question.

Modifica
Non ho un esempio completo per esso, ma si poteva ascoltare StandardOutput con l'evento Process.OutputDataReceived se non si vuole aspettare in modo sincrono. C'è un esempio sulla pagina MSDN.

+0

Questo in nessun modo risolve il problema. Chiude il flusso di input per leggere l'output, ma da quel momento in poi non è possibile inviare ulteriori comandi. –

+0

@Quandary, ho aggiornato la mia risposta con un collegamento all'evento OutputDataReceived che potrebbe essere più utile nel tuo caso. –

0

Questa è la risposta perfetta:

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 

In precedenza ho provato con outputstream.Peek Wile() = -1 ma questo viene arrestato da un bug nella funzione Peek framework .NET, che doesn t timeout o genera un errore se leggi oltre la fine del flusso ...

Funziona meglio in quanto cattura davvero tutto l'output, ma è tutt'altro che perfetto.

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