最近RubyやJavaScriptばっかりいじってて、ちょっとご無沙汰でしたが、今日は久々にC#でプログラミング。
というのも、ちょっと急ぎめで作りたいソフトがあるのです。まだあんまり決まってないので詳しくは言いにくいですが、コンソールアプリを呼び出して内容をリダイレクトするものです。難易度としては大して高くないので、早めに仕上げてしまいたいですね。
さて、今回メインに使ったクラスは、System.Diagnostics.Processクラス。プロセスの起動のほか、終了待ちや出力のリダイレクトまでいろいろやれてしまう優れものクラスです。これをいろいろいじっていたので、ちょっとメモ程度に。
まず、初めて知ったのですが、このクラスは、出力された文字列をイベントハンドラに処理させることが出来ます。
Process proc = new Process(); ・ ・ ・ proc.StartInfo.RedirectStandardError = true; proc.StartInfo.RedirectStandardOutput = true; proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived); proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived); ・ ・ proc.BeginOutputReadLine(); proc.BeginErrorReadLine();
StartInfo.RedirectStandard[Output|Error]をtrueに設定したあと、[Output|Error]DataRecievedイベントにメソッドを格納しておくと、標準出力やエラーの発生時にこれらのメソッドが呼び出されて出力(行)を受け取ることが出来るという仕組みです(ただし、Process#Startしたあとに、Process#Begin[Output|Error]ReadLineメソッドを呼ぶ必要があります)。
このProcessオブジェクトでは、内部的に別スレッドが作られその中で動作しているらしく、イベントハンドラの中でコントロールをさわるときには、Invokeメソッドを呼び出さなければいけません。まあ、どちらにせよスレッドを作って監視するしかないかと思っていたので、これで手間が省けますね。
それともう一つ気づいたのは、このProcessオブジェクトの引数です。
プロセスを起動する前に、Process.StartInfoにプロセス起動に必要な情報を指定することができますが、ここではご丁寧にFileNameとArgumentsが分かれているので、コマンドプロンプトでコマンドを指定するときのように柔軟にはいきません(具体的には、パイプ処理などが行えません)
そこで、FileNameとArgumentsを以下のように指定します。
proc.StartInfo.FileName = "cmd"; proc.StartInfo.Arguments = "/C " + commandline;
すると、commandlineにパイプ記号などが入っていても、問題なく処理できます。
たとえばcommandlineに「ping 127.0.0.1 | grep 応答」などと書いておけば、pingの応答部分だけを取得することが出来るわけですね(grepはcygwinのコマンドなので、事前にcygwinをインストールし、そのbinフォルダにパスを通しておく必要があります)。
ぱっと思いついたことですが、これって結構便利かも。試してないですが、ほかの「外部アプリの実行にファイル名と引数の両方の入力を求められるもの(たとえばEclipseの外部ツール)」などでも使えるかもしれませんね。