#はじめに
これまでC#とPythonの連携について似たようなネタをいくつか書いてきた。
「C# GUIアプリケーションからPythonスクリプトを実行する」では、Pythonスクリプトを実行し、処理が終了するまで進捗状況を確認できるようにした。
「C# GUIからPythonのコードを実行する」では、Pythonスクリプトではなく、画面上からPythonコードを入力できるようにしてみた。
そして「C# GUIからバックグラウンドでプロセスを実行し管理する」では、Pythonを実行した後、終了まで待たずに別の作業をしつつ、気になったときに実行状況を確認できる方法について調べた。
そこで、今回がこのシリーズ最後のネタになる。
そもそもGUIなんかいらんから、Pythonのスクリプトを実行して、その処理結果を受け取るようなメソッドとか作りたいんだけど、どうすんの?
要するにGUIを介さず、Pythonスクリプトを開始し、実行が終わるまで次の処理に行かずにそこで待ち(同期をとる)、その後結果を取得するという形の呼び出しだ。
#ソース
はい、どん。
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace ChemShow
{
public class PythonExecutor
{
public String FileName { get; set; }
public String WorkingDirectory { get; set; }
public String Arguments { get; set; }
public String InputString { get; set; }
public String StandardOutput { get; set; }
public int ExitCode { get; set; }
private StringBuilder standardOutputStringBuilder = new StringBuilder();
public PythonExecutor()
{
}
/// 実行ボタンクリック時の動作
public void Execute()
{
ProcessStartInfo psInfo = new ProcessStartInfo();
psInfo.FileName = this.FileName;
psInfo.WorkingDirectory = this.WorkingDirectory;
psInfo.Arguments = this.Arguments;
psInfo.CreateNoWindow = true;
psInfo.UseShellExecute = false;
psInfo.RedirectStandardInput = true;
psInfo.RedirectStandardOutput = true;
psInfo.RedirectStandardError = true;
// Process p = Process.Start(psInfo);
Process p = new System.Diagnostics.Process();
p.StartInfo = psInfo;
p.OutputDataReceived += p_OutputDataReceived;
p.ErrorDataReceived += p_ErrorDataReceived;
// プロセスの実行
p.Start();
// 標準入力への書き込み
using (StreamWriter sw = p.StandardInput)
{
sw.Write(InputString);
}
//非同期で出力とエラーの読み取りを開始
p.BeginOutputReadLine();
p.BeginErrorReadLine();
// 終わるまでまつ
p.WaitForExit();
this.ExitCode = p.ExitCode;
this.StandardOutput = standardOutputStringBuilder.ToString();
}
/// <summary>
/// 標準出力データを受け取った時の処理
/// </summary>
void p_OutputDataReceived(object sender,
System.Diagnostics.DataReceivedEventArgs e)
{
//processMessage(sender, e);
if (e != null && e.Data != null && e.Data.Length > 0)
{
standardOutputStringBuilder.Append(e.Data + "\n");
}
}
/// <summary>
/// 標準エラーを受け取った時の処理
/// </summary>
void p_ErrorDataReceived(object sender,
System.Diagnostics.DataReceivedEventArgs e)
{
//必要な処理を書く
}
}
}
#ソース解説
目新しいことはなく、プロセスを開始したらそのままWaitForExit()
を実行しているだけ。そうするとプロセスが終了するまで次にいかない。これにより同期的な呼び出しが実現できる。実行終了後、終了コードや標準出力のデータをプロパティから取得できるようにしている。
#使い方
以下はスクリプトの標準入力にデータを与え、標準出力に出力された結果を受け取る例である。
PythonExecutor pe = new PythonExecutor();
pe.FileName=@"c:\python3.6\bin\python.exe";
pe.WorkingDirectory = @"c:\tmp";
pe.Arguments =@"C:\ChemShow\python\hist.py";
pe.InputString = "1,2,3,4,5,6,7,8,9,10"; //標準入力に与えるデータ
pe.Execute(); //実行
Console.WriteLine(pe.StandardOutput); //標準出力の結果を出力
#2020/2/24 修正
プロセスを2回スタートさせるという致命的バグがあったため以下の通り修正。ご迷惑おかけしました。
// Process p = Process.Start(psInfo);
Process p = new System.Diagnostics.Process();
p.StartInfo = psInfo;
#おわりに
要するに、今までに出てきた全ての呼び出し方に対応したコンポーネントをいい加減作れよという話。
後、タイトルはPythonスクリプトにしているが、コマンドラインから実行できるものであれば同じですね。