这种方式无法发送命令到shell。info.Arguments中的字符串是在命令行中提供给程序的参数。如果您想让cmd.exe shell执行一系列命令,然后退出,您将需要提供/c参数。如果您有多个要执行的命令,您将需要将它们放入批处理文件中并执行该文件,或者用引号括起来并用&&分隔它们,例如:info.Arguments = @"/c ""cd \ && dir""";
。您遇到的另一个问题是,当没有任何或正确的参数时,cmd.exe默认以交互模式打开。/c选项告诉cmd.exe执行相关命令,然后退出。
此外,像Python和Perl这样的解释器有时会在直接从ProcessStartInfo启动时出现奇怪的行为。如果使用info.Arguments = @"""MyPerlProgram.pl""";
和perl.exe不起作用,则可能需要在cmd.exe中启动它们,以便获得正常的行为,例如:info.Arguments = @"/c ""perl.exe ""MyPerlProgram.pl""""";
。
请参见Cmd和ProcessStartInfo.Arguments Property。
为了回答您的Edit 3问题,您可能没有正确地连接输出。而不是尝试钩住StreamReader的BaseStream,请使用this.shellProcess.OutputDataReceived += ProcessOutputHandler;
钩住OutputDataReceived事件,在调用Start之前,ProcessOutputHandler具有类似于public static void ProcessOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
的签名。在调用Start后立即调用this.shellProcess.BeginOutputReadLine();
。对于错误输出,处理过程也类似。请参见Process.BeginOutputReadLine Method和Process.BeginErrorReadLine Method了解更多详细信息。
如果仍然有问题,请尝试以下代码: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");
process.StartInfo.RedirectStandardInput = true;
process.OutputDataReceived += ProcessOutputDataHandler;
process.ErrorDataReceived += ProcessErrorDataHandler;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
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");
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;
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;
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直到进程结束或看到行末才触发,因此导致某些提示字符的输出顺序错误。