この方法でシェルにコマンドを送信することはできません。info.Argumentsの文字列は、コマンドラインでプログラムに提供される引数です。cmd.exeシェルで一連のコマンドを実行してから終了する場合は、/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""""";
。
CmdおよびProcessStartInfo.Argumentsプロパティを参照してください。
Edit 3の問題に答えるには、おそらく出力に正しくフックしていません。StreamReaderのBaseStreamをフックしようとする代わりに、this.shellProcess.OutputDataReceived += ProcessOutputHandler;
ProcessOutputHandlerがのような署名を持つStartを呼び出す前に、OutputDataReceivedイベントをフックします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.';""""";
か?
また、以下のコードは、シェル通信に必要な概念のほとんどを示しています。
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));
}
注意すべき点の1つは、Form1_Loadイベントが完了しない場合、Invokeは完了するまでハングすることです。イベントに長時間実行されるコードがある場合は、BeginInvokeを使用して非同期的に呼び出すか、長時間実行されるコードで定期的にDoEventsを呼び出す必要があります。
編集
あなたのコメントによると、私はインタラクティブな提出で動作するようにコードを変更しました。ただし、問題があります。Pythonプロンプト(>>>
)はStandardError出力で提供され、StandardInputをエコーしません。また、回線を終了しません。これにより、プロンプトの検出が困難になり、プロセスが終了するか行の終わりが表示されるまでprocess_ErrorDataReceivedが起動しないため、プロンプト文字の出力が乱れます。