在C#中向cmd提示符发送命令

对于我的一个实现,我正在研究一个应该向cmd窗口发送/检索命令/结果的工具。 一切正常,但下面的用例无法做任何事情。 似乎我的应用程序正在等待某事(而不是显示结果)

从我的工具我导航到python文件夹。 从python文件夹我尝试启动python.exe但此时,我的编辑器没有做任何事情。 它只是在等待。

为了您的善意考虑,我也在这里链接video。 你们会更容易理解我想说的话。

在此处观看video(在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行命令使用相同的方法。如果有人可以指出我所做的代码或我必须做的事情,我会很好,以实现预期的function。

编辑2:正常的cmd命令工作正常。 像python,perl这样的命令行工具不起作用。

编辑3:所以我设法在Jamie的建议之后向前移动了一点点。 ui不再“悬挂”了。 但是当我访问python解释器时,解释器的输出仍然在我的工具中不可见。 有什么建议可能会发生吗?

您不能以这种方式向shell发送命令。 info.Arguments中的字符串是在命令行上提供给程序的参数。 如果您希望cmd.exe shell执行一系列命令然后退出,则必须提供/ c参数。 如果您希望它执行多个命令,则必须将命令放在批处理文件中并执行该命令或将它们用引号括起来并用&&分隔,即info.Arguments = @"/c ""cd \ && dir"""; 。 您永远不会返回的另一个问题是cmd.exe在没有任何或正确的参数的情况下执行时默认以交互模式打开。 / c选项告诉cmd.exe执行相关命令然后退出。

另外,当直接从ProcessStartInfo启动时,python和perl等解释器有时会产生奇怪的行为。 如果info.Arguments = @"""MyPerlProgram.pl"""; 如果perl.exe不起作用,您可能会发现有必要在cmd.exe中启动它们以获得正常行为,即info.Arguments = @"/c ""perl.exe ""MyPerlProgram.pl""""";

请参阅Cmd和ProcessStartInfo.Arguments属性 。

要回答编辑3问题,您可能没有正确地连接到输出。 而不是尝试挂钩StreamReader的BaseStream,而是使用this.shellProcess.OutputDataReceived += ProcessOutputHandler;挂钩OutputDataReceived事件this.shellProcess.OutputDataReceived += ProcessOutputHandler; 在调用Start之前,ProcessOutputHandler的签名类似于public static void ProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) 。 调用Start后立即调用this.shellProcess.BeginOutputReadLine(); 。 该过程也类似于错误输出。 有关更多详细信息,请参阅Process.BeginOutputReadLine方法和Process.BeginErrorReadLine方法 。

如果你还有问题,如果你只是尝试process.StartInfo.Arguments = @"/c ""python.exe -c ""import sys; print 'Test.';""""";会得到什么process.StartInfo.Arguments = @"/c ""python.exe -c ""import sys; print 'Test.';""""";

此外,下面的代码演示了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); } 

您可能遇到导致问题的线程问题。 我已经做了一些进一步的工作,并能够使用以下代码获取表单上的文本框:

 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没有触发而导致一些无序输出的提示字符,直到进程结束或看到行结束。

您的问题中没有足够的代码来确定您的应用程序究竟是什么。 你的代码中有些东西看起来很奇怪。 例如,为什么要启动自己的错误并输出读取循环而不是使用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可能存在生命周期问题。 如果您发布更多代码,我们可以提供更好的指导。

我看不到您的所有代码,但您可以轻松地使用Steam对象将命令写入/发送到您创建的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