2012-08-01 12 views
6

Per una delle mie implementazioni sto lavorando a uno strumento che deve inviare/recuperare comandi/risultati alla/dalla finestra di cmd. Tutto funziona bene ma il caso Usa non riesce a fare nulla. Sembra che la mia applicazione sia in attesa di qualcosa (invece di visualizzare il risultato)Invio di comandi al prompt di cmd in C#

Dal mio strumento si accede alla cartella python. Dalla cartella python provo a lanciare python.exe ma a questo punto, il mio editor non fa nulla. semplicemente continua ad aspettare.

Per la vostra gentile considerazione, sto anche collegando il video qui. Per voi ragazzi sarebbe più facile capire cosa sto cercando di dire.

View the Video here (on youtube)

Sono anche allegando il codice che ho attualmente.

  ProcessStartInfo info = new ProcessStartInfo("cmd.exe"); 

      string argument = null; 
      if (!string.IsNullOrEmpty(startingDirectory) && System.IO.Directory.Exists(startingDirectory)) 
      { 
       argument += @"cd\"; 
      } 
      else 
      { 
       argument += "\""; 
      } 
      info.Arguments = argument; 
      info.CreateNoWindow = true; 
      info.RedirectStandardError = true; 
      info.RedirectStandardInput = true; 
      info.RedirectStandardOutput = true; 
      info.UseShellExecute = false; 
      this.shellProcess = System.Diagnostics.Process.Start(info); 
      this.shellProcess.EnableRaisingEvents = true; 
      //this.InputStream.AutoFlush = true; 
      this.shellProcess.Exited += new EventHandler(ProcessExited); 
      this.ErrorBeginRead(); 
      this.OutputBeginRead(); 

private void OutputBeginRead() 
    { 
     this.shellProcess.StandardOutput.BaseStream.BeginRead(outputBuffer, 0, outputBuffer.Length, new AsyncCallback(this.OnOutputInput), this.shellProcess); 
    } 

     private void ErrorBeginRead() 
    { 
     this.shellProcess.StandardError.BaseStream.BeginRead(errorBuffer, 0, errorBuffer.Length, new AsyncCallback(this.OnErrorInput), this.shellProcess); 
    } 

Grazie!

MODIFICA: L'avvio di python è solo un esempio. Ho bisogno di usare lo stesso metodo anche per altri normali comandi della linea cmd. Sarebbe bello, se qualcuno può indicare cosa sto facendo male con il codice che ho o cosa devo fare, al fine di ottenere la funzionalità desiderata.

MODIFICA 2: i normali comandi cmd funzionano perfettamente. Gli strumenti da riga di comando come python, perl non funzionano.

Modifica 3: Così sono riuscito a muovermi un po 'in avanti seguendo i suggerimenti di Jamie. L'ui non è più "sospeso". ma quando accedo all'interprete python, l'output dell'interprete non è ancora visibile nel mio strumento. Qualche suggerimento sul perché ciò potrebbe accadere?

risposta

15

Non è possibile inviare comandi a una shell in questo modo. La stringa in info.Arguments è gli argomenti forniti al programma sulla riga di comando. Se si desidera che la shell di cmd.exe esegua una serie di comandi e quindi si esce, sarà necessario fornire l'argomento/c. Se hai più comandi da eseguire, dovrai inserire i comandi in un file batch ed eseguirli o racchiuderli tra virgolette e separarli con & &, ad esempio info.Arguments = @"/c ""cd \ && dir""";. L'altro problema con il non ritorno è che cmd.exe si apre in modalità interattiva per impostazione predefinita quando viene eseguito senza argomenti, o appropriati. L'opzione/c indica a cmd.exe di eseguire i comandi rilevanti e quindi di uscire.

Inoltre, interpreti come python e perl a volte hanno comportamenti strani quando vengono lanciati direttamente da ProcessStartInfo. Se info.Arguments = @"""MyPerlProgram.pl"""; con perl.exe non funziona, potrebbe essere necessario avviarli all'interno di cmd.exe per ottenere un comportamento normale, ad es. info.Arguments = @"/c ""perl.exe ""MyPerlProgram.pl""""";.

Vedere Cmd e ProcessStartInfo.Arguments Property.

Per rispondere al problema Modifica 3, probabilmente non si aggancia correttamente alle uscite. Invece di provare ad agganciare il BaseStream di StreamReader, collegare l'evento OutputDataReceived con this.shellProcess.OutputDataReceived += ProcessOutputHandler; prima di chiamare Start dove ProcessOutputHandler ha una firma come public static void ProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine). Subito dopo aver chiamato Start, chiama this.shellProcess.BeginOutputReadLine();. Il processo è simile anche all'uscita errore. Vedere Process.BeginOutputReadLine Method e Process.BeginErrorReadLine Method per ulteriori dettagli.

Se hai ancora un problema, cosa ottieni se ti limiti a provare process.StartInfo.Arguments = @"/c ""python.exe -c ""import sys; print 'Test.';""""";?

Inoltre, il codice qui sotto dimostra la maggior parte dei concetti necessari per la comunicazione shell:

public static void Main() 
{ 
    using (Process process = new Process()) 
    { 
     process.StartInfo.UseShellExecute = false; 
     process.StartInfo.RedirectStandardOutput = true; 
     process.StartInfo.RedirectStandardError = true; 
     process.StartInfo.WorkingDirectory = @"C:\"; 
     process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe"); 

     // Redirects the standard input so that commands can be sent to the shell. 
     process.StartInfo.RedirectStandardInput = true; 
     // Runs the specified command and exits the shell immediately. 
     //process.StartInfo.Arguments = @"/c ""dir"""; 

     process.OutputDataReceived += ProcessOutputDataHandler; 
     process.ErrorDataReceived += ProcessErrorDataHandler; 

     process.Start(); 
     process.BeginOutputReadLine(); 
     process.BeginErrorReadLine(); 

     // Send a directory command and an exit command to the shell 
     process.StandardInput.WriteLine("dir"); 
     process.StandardInput.WriteLine("exit"); 

     process.WaitForExit(); 
    } 
} 

public static void ProcessOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{ 
    Console.WriteLine(outLine.Data); 
} 

public static void ProcessErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine) 
{ 
    Console.WriteLine(outLine.Data); 
} 

Si può avere problemi di threading che causano i vostri problemi. Ho fatto qualche ulteriore lavoro con questa ed è stato in grado di ottenere una casella di testo in un modulo per aggiornare con il seguente codice:

using System; 
using System.Diagnostics; 
using System.IO; 
using System.Timers; 

namespace DummyFormsApplication 
{ 
    class ProcessLauncher : IDisposable 
    { 
     private Form1 form; 
     private Process process; 
     private bool running; 

     public bool InteractiveMode 
     { 
      get; 
      private set; 
     } 

     public ProcessLauncher(Form1 form) 
     { 
      this.form = form; 

      process = new Process(); 
      process.StartInfo.UseShellExecute = false; 
      process.StartInfo.RedirectStandardOutput = true; 
      process.StartInfo.RedirectStandardError = true; 
      process.StartInfo.WorkingDirectory = @"C:\"; 
      process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe"); 

      // Redirects the standard input so that commands can be sent to the shell. 
      process.StartInfo.RedirectStandardInput = true; 

      process.OutputDataReceived +=new DataReceivedEventHandler(process_OutputDataReceived); 
      process.ErrorDataReceived += new DataReceivedEventHandler(process_ErrorDataReceived); 
      process.Exited += new EventHandler(process_Exited); 
     } 

     public void Start() 
     { 
      if (running == false) 
      { 
       running = true; 
       InteractiveMode = true; 

       // Runs the specified command and exits the shell immediately upon completion. 
       process.StartInfo.Arguments = @"/c ""C:\python27\python.exe -i"""; 

       process.Start(); 

       process.BeginOutputReadLine(); 
       process.BeginErrorReadLine(); 
      } 
     } 

     public void Start(string scriptFileName) 
     { 
      if (running == false) 
      { 
       running = true; 
       InteractiveMode = false; 

       // Runs the specified command and exits the shell immediately upon completion. 
       process.StartInfo.Arguments = string.Format(@"/c ""C:\python27\python.exe ""{0}""""", scriptFileName); 
      } 
     } 

     public void Abort() 
     { 
      process.Kill(); 
     } 

     public void SendInput(string input) 
     { 
      process.StandardInput.Write(input); 
      process.StandardInput.Flush(); 
     } 

     private void process_OutputDataReceived(object sendingProcess, DataReceivedEventArgs outLine) 
     { 
      if (outLine.Data != null) 
      { 
       form.Invoke(form.appendConsoleTextDelegate, new object[] { outLine.Data }); 
      } 
     } 

     private void process_ErrorDataReceived(object sendingProcess, DataReceivedEventArgs outLine) 
     { 
      if (outLine.Data != null) 
      { 
       form.Invoke(form.appendConsoleTextDelegate, new object[] { outLine.Data }); 
      } 
     } 

     private void process_Exited(object sender, EventArgs e) 
     { 
      running = false; 
     } 

     public void Dispose() 
     { 
      if (process != null) 
      { 
       process.Dispose(); 
      } 
     } 
    } 
} 

ho creato un modulo e aggiunto una casella di testo e il seguente codice nella forma:

public delegate void AppendConsoleText(string text); 
    public AppendConsoleText appendConsoleTextDelegate; 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     appendConsoleTextDelegate = new AppendConsoleText(textBox1_AppendConsoleText); 
     using (ProcessLauncher launcher = new ProcessLauncher(this)) 
     { 
      launcher.Start(); 

      launcher.SendInput("import sys;\n"); 
      launcher.SendInput("print \"Test.\";\n"); 
      launcher.SendInput("exit()\n"); 
     } 
    } 

    private void textBox1_AppendConsoleText(string text) 
    { 
     textBox1.AppendText(string.Format("{0}\r\n", text)); 
    } 

Una cosa da notare è che se l'evento Form1_Load non viene completato, Invoke si bloccherà fino a quando non lo farà. Se in un evento è presente codice con esecuzione prolungata, è necessario richiamare in modo asincrono utilizzando BeginInvoke o chiamare periodicamente DoEvents nel codice di vecchia esecuzione.

EDIT

Per il tuo commento, ho modificato il codice per lavorare con mezzi interattivi. C'è, tuttavia, un problema. Il prompt python (>>>) viene fornito sull'output StandardError e non fa eco a StandardInput. Inoltre non termina la linea. Ciò rende difficile il rilevamento di un prompt e causa la fuoriuscita dell'ordine dei caratteri di prompt a causa del fatto che process_ErrorDataReceived non viene attivato fino a quando il processo non termina o viene visualizzata una fine riga.

+0

Ciao Jamie, può fornire un esempio o un collegamento che potrebbe aiutare, per favore? – Gagan

+0

Risposta aggiornata con un esempio di informazioni.Argomandi per cmd.exe e aggiunta spiegazione del motivo per cui il processo non termina mai da solo. – JamieSee

+0

Risposta aggiornata con soluzione per Modifica 3. – JamieSee

2

Nella tua domanda non c'è abbastanza codice per capire esattamente a cosa è appesa la tua applicazione. Ci sono alcune cose nel tuo codice che sembrano strane. Ad esempio, perché stai iniziando il tuo errore e output loop di lettura invece di usare quelli incorporati nella classe Process? Come questo:

var shellProcess = System.Diagnostics.Process.Start(info); 
shellProcess.EnableRaisingEvents = true; 
shellProcess.Exited += ProcessExited; 

shellProcess.OutputDataReceived += ShellProcess_OutputDataReceived; 
shellProcess.ErrorDataReceived += ShellProcess_ErrorDataReceived; 
shellProcess.BeginOutputReadLine(); 
shellProcess.BeginErrorReadLine(); 

void ShellProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e) 
{ 
    // Do Something 
} 

void ShellProcess_OutputDataReceived(object sender, DataReceivedEventArgs e) 
{ 
    // Do Something 
} 

Dato che il vostro errore e asincrone uscita eventi non sono sparare, mi porta a credere che ci potrebbe essere un problema di tutta la vita con la shellProcess. Se pubblichi più del tuo codice, possiamo fornire una guida migliore.

1

non riesco a vedere tutto il codice, ma si può facilmente oggetti utilizzare il vapore di scrivere/inviare comandi alla finestra CMD creato da te. es .:

StreamWriter inputStream = shellProcess.StandardInput; 
//send command to cmd prompt and wait for command to execute with thread sleep 
inputStream.WriteLine("echo "CMD just received input"); 
inputStream.Flush(); 

Nell'esempio sopra per esempio, riga di comando riceve il comando echo proprio come è stato inserito nella finestra. Per mostrare l'output, è necessario creare l'oggetto StreamReader e assegnarlo al processo StandardOutput.

+0

Non è questo bagaglio extra? – Gagan

Problemi correlati