2012-08-01 70 views
6

對於我的一個實現,我正在開發一個應該從/向cmd窗口發送/檢索命令/結果的工具。一切正常,但下面的用例無法做任何事情。看起來好像我的應用程序正在等待某些東西(而不是顯示結果)發送命令到C#中的cmd提示符

從我的工具導航到python文件夾。從python文件夾中,我嘗試啓動python.exe,但在這一點上,我的編輯器沒有做任何事情。它只是繼續等待。

爲了您的親切考慮,我也在此鏈接視頻。你們會更容易理解我想說的話。

View the Video here (on youtube)

我還附上我目前擁有的代碼。

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

謝謝!編輯: 啓動python只是一個例子。我需要爲其他普通cmd行命令使用相同的方法。如果有人能夠指出我所做的代碼有錯誤或必須執行的操作,以便實現預期的功能,那就太好了。

編輯2:正常的cmd命令工作正常。像python,perl這樣的命令行工具不起作用。

編輯3:所以我設法按照傑米的建議做了一點動作。用戶界面不再「掛」了。但是當我訪問python解釋器時,解釋器的輸出在我的工具中仍然不可見。任何建議,爲什麼可能會發生?

回答

15

您無法以這種方式將命令發送到shell。 info.Arguments中的字符串是在命令行中提供給程序的參數。如果你想讓cmd.exe shell執行一系列命令然後退出,你必須提供/ c參數。如果您有多個要執行的命令,則必須將這些命令放入批處理文件中並執行該命令,或將它們用引號引起來,並用& &即info.Arguments = @"/c ""cd \ && dir""";將它們分隔開。您從未返回的其他問題是,cmd.exe在缺少任何或適當參數的情況下執行時會默認以交互模式打開。/c選項告訴cmd.exe執行相關命令,然後退出。

此外,像Python和Perl這樣的解釋器有時會在從ProcessStartInfo直接啓動時出現奇怪的行爲。如果帶有perl.exe的info.Arguments = @"""MyPerlProgram.pl""";不起作用,您可能會發現需要在cmd.exe中啓動它們才能獲得正常行爲,即info.Arguments = @"/c ""perl.exe ""MyPerlProgram.pl""""";

請參閱CmdProcessStartInfo.Arguments Property

要回答你的編輯3問題,你可能沒有正確掛鉤輸出。在調用Start之前,在ProcessOutputHandler具有類似public static void ProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)的簽名之前,不要試圖掛鉤StreamReader的BaseStream,而要掛鉤OutputDataReceived事件this.shellProcess.OutputDataReceived += ProcessOutputHandler;。在撥打開始後立即致電this.shellProcess.BeginOutputReadLine();。該過程對於錯誤輸出也是類似的。有關更多詳細信息,請參見Process.BeginOutputReadLine MethodProcess.BeginErrorReadLine Method

如果您仍有問題,如果您只是嘗試process.StartInfo.Arguments = @"/c ""python.exe -c ""import sys; print 'Test.';""""";,您會得到什麼?

此外,下面的代碼演示了大多數必要的理念,爲殼連通:

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

您可能導致您的問題線程問題。我已經做了一些這方面的進一步工作,並能得到一個文本框在窗體上用下面的代碼更新:

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

我創建了一個表格,並添加一個文本框,並在表格下面的代碼:

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

需要注意的一件事是,如果Form1_Load事件沒有完成,Invoke會掛起直到它發生。如果在事件中有長時間運行的代碼,則需要使用BeginInvoke異步調用,或者在長時間運行的代碼中定期調用DoEvents。

編輯

根據您的意見,我已經修改了代碼互動提交工作。但是,有一個問題。在StandardError輸出中提供了python提示(>>>),它不會回顯StandardInput。它也不會終止線路。這使得檢測提示變得困難並且由於process_ErrorDataReceived不會觸發而導致提示字符的一些亂序輸出,直到過程結束或者看到行結束爲止。

+0

嗨傑米,請你提供一個可能有幫助的例子或鏈接嗎? – Gagan 2012-08-01 22:09:38

+0

用cmd.exe的info.Arguments示例取消了回答,並添加了解釋爲什麼該進程永遠不會自行終止的解釋。 – JamieSee 2012-08-01 22:45:55

+0

用編輯3的解決方案更新了答案。 – JamieSee 2012-08-14 17:14:12

2

在你的問題中沒有足夠的代碼來確切地確定你的應用程序掛在什麼。代碼中有些東西看起來很奇怪。例如,爲什麼你開始自己的錯誤並輸出讀取循環而不是使用Process類中內置的循環?像這樣:

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 
} 

由於你的錯誤和輸出異步事件沒有被觸發,所以我相信shellProcess可能存在一生問題。如果您發佈了更多的代碼,我們可以提供更好的指導。

1

我不能看到所有的代碼,但是你可以很容易地使用汽對象寫/發送命令到CMD窗口由你創建。例如: -

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

在比如上面的例子中,命令提示符將收到就像它在窗口中輸入的echo命令。要顯示輸出,您必須創建StreamReader對象並將其分配給進程的StandardOutput

+0

這不是額外的行李嗎? – Gagan 2012-08-01 23:43:58

相關問題