您不能以这种方式向 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 属性。
要回答您的Edit 3问题,您可能没有正确连接到输出。与其尝试挂钩 StreamReader 的 BaseStream,不如this.shellProcess.OutputDataReceived += ProcessOutputHandler;
在调用 Start 之前挂钩 OutputDataReceived 事件,其中 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.';""""";
?
此外,下面的代码演示了 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。
编辑
根据您的评论,我已修改代码以处理交互式提交。但是,有一个问题。Python 提示符 ( >>>
) 在 StandardError 输出中提供,它不回显 StandardInput。它也不会终止线路。这使得检测提示变得困难,并导致提示字符的一些乱序输出,因为 process_ErrorDataReceived 在进程结束或看到行结束之前不会触发。