|
.NET TIPS
外部プログラム実行時に処理が固まる場合には?[2.0、C#、VB]
デジタルアドバンテージ 遠藤 孝信
2008/11/06 |
|
|
「TIPS:コンソール・アプリケーションの出力を取り込むには?」では、外部プログラムを実行し、その標準出力を読み込む方法について示しているが、これに若干手を加えれば、標準入力から文字列を読み取って、その処理結果を標準出力に出力するような外部プログラムにも対応できる。以下にそのサンプル・プログラムを示す。
// pipesync.cs
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
class PipeSync {
static void Main() {
// 子プロセスで処理したいテキスト
string text = File.ReadAllText("sample.txt",
Encoding.GetEncoding("Shift_JIS"));
Process p = new Process();
// 子プロセスの実行ファイル名
p.StartInfo.FileName = "cat.exe";
// 子プロセスのオプション(もしあれば)
p.StartInfo.Arguments = "-n";
// コンソール・ウィンドウを開かない
p.StartInfo.CreateNoWindow = true;
// シェル機能を使用しない
p.StartInfo.UseShellExecute = false;
// 標準出力をリダイレクト
p.StartInfo.RedirectStandardOutput = true;
// 標準入力をリダイレクト
p.StartInfo.RedirectStandardInput = true;
p.Start(); // 子プロセスの実行開始
using (StreamWriter sw = p.StandardInput) {
sw.Write(text); // 子プロセスへの書き込み
}
// 子プロセスの出力の読み込み
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit(); // 子プロセスが終了するのを待つ
p.Dispose(); // 子プロセスの破棄
Console.WriteLine(output); // 子プロセスの出力を表示
}
}
// コンパイル方法:csc pipeasync.cs
|
|
外部プログラムの標準入出力をリダイレクトするC#のプログラム(pipesync.cs) |
実行する場合には、子プロセスで処理したいテキスト、子プロセスの実行ファイル名、オプションの部分を修正する必要がある。
|
' pipesync.vb
Imports System
Imports System.Text
Imports System.IO
Imports System.Diagnostics
Class PipeSync
Shared Sub main()
' 子プロセスで処理したいテキスト
Dim text As String = File.ReadAllText("sample.txt", _
Encoding.GetEncoding("Shift_JIS"))
Dim p As New Process()
' 子プロセスの実行ファイル名
p.StartInfo.FileName = "cat.exe"
' 子プロセスのオプション(もしあれば)
p.StartInfo.Arguments = "-n"
' コンソール・ウィンドウを開かない
p.StartInfo.CreateNoWindow = True
' シェル機能を使用しない
p.StartInfo.UseShellExecute = False
' 標準出力をリダイレクト
p.StartInfo.RedirectStandardOutput = True
' 標準入力をリダイレクト
p.StartInfo.RedirectStandardInput = True
p.Start() ' 子プロセスの実行開始
Using sw As StreamWriter = p.StandardInput
sw.Write(text) ' 子プロセスへの書き込み
End Using
' 子プロセスの出力の読み取り
Dim output As String = p.StandardOutput.ReadToEnd()
p.WaitForExit() ' 子プロセスが終了するのを待つ
p.Dispose() ' 子プロセスの破棄
Console.WriteLine(output) ' 子プロセスの出力を表示
End Sub
End Class
' コンパイル方法:vbc pipesync.vb
|
|
外部プログラムの標準入出力をリダイレクトするVBのプログラム(pipesync.vb) |
実行する場合には、子プロセスで処理したいテキスト、子プロセスの実行ファイル名、オプションの部分を修正する必要がある。
|
子プロセスへの標準入力も標準出力と同様にストリームであるため、その扱いは基本的に同じで、特に難しいポイントはない。
しかしこのプログラムでは、子プロセスとして起動する外部プログラムや、子プロセスに処理させるデータの量によっては、(子プロセスの出力の読み取り部分の前で)プログラムが固まって(フリーズして)しまう場合がある。子プロセスとのデータのやり取りに使用される“パイプ”がいっぱいになり、詰まってしまうのである。
これは、子プロセスの出力によりパイプがいっぱいになっているにもかかわらず、子プロセスの出力を処理せずに、パイプへの書き込みを続けようとするために発生する。このような場合には、子プロセスの標準入力への書き込みと、子プロセスの標準出力からの読み込みを非同期に行えばよい。その方法について次に述べる。
非同期に標準出力を読み込む
Processクラスでは、子プロセスの標準出力からの読み込みを非同期に行う機能が用意されている。これを行うには、ProcessクラスのOutputDataReceivedイベントにイベント・ハンドラとなるメソッドを設定しておき、プロセスの開始後にBeginOutputReadLineメソッドを呼び出せばよい。
OutputDataReceivedイベント・ハンドラは、子プロセスが標準出力に書き込むたび、プログラム本体とは非同期に呼び出される。書き込まれた文字列は、イベント・ハンドラの第2パラメータであるDataReceivedEventArgsオブジェクト(System.Diagnostics名前空間)のDataプロパティにより取得できる。
以下に子プロセスの標準出力を非同期に読み込むサンプル・プログラムを示す。
// pipeasync.cs
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
class PipeAsync {
static StringBuilder output = new StringBuilder();
static void Main() {
// 子プロセスで処理したいテキスト
string text = File.ReadAllText("sample.txt",
Encoding.GetEncoding("Shift_JIS"));
Process p = new Process();
// 子プロセスの実行ファイル名
p.StartInfo.FileName = "cat.exe";
// 子プロセスのオプション(もしあれば)
p.StartInfo.Arguments = "-n";
// コンソール・ウィンドウを開かない
p.StartInfo.CreateNoWindow = true;
// シェル機能を使用しない
p.StartInfo.UseShellExecute = false;
// 標準出力をリダイレクト
p.StartInfo.RedirectStandardOutput = true;
// 標準入力をリダイレクト
p.StartInfo.RedirectStandardInput = true;
// イベント・ハンドラ設定
p.OutputDataReceived += OutputHandler;
p.Start(); // 子プロセスの実行開始
p.BeginOutputReadLine(); // 子プロセスの出力読み込み開始
using (StreamWriter sw = p.StandardInput) {
sw.Write(text); // 子プロセスへの書き込み
}
p.WaitForExit(); // 子プロセスが終了するのを待つ
p.Dispose(); // 子プロセスの破棄
Console.WriteLine(output.ToString()); // 子プロセスの出力を表示
}
// 子プロセスが標準出力に出力したときに呼び出されるメソッド
static void OutputHandler(object o, DataReceivedEventArgs args) {
output.AppendLine(args.Data); // 出力されたデータを保存
}
}
// コンパイル方法:csc pipeasync.cs
|
|
子プロセスの標準出力を非同期に読み込むC#のプログラム(pipeasync.cs) |
実行する場合には、子プロセスで処理したいテキスト、子プロセスの実行ファイル名、オプションの部分を修正する必要がある。
|
' pipeasync.vb
Imports System
Imports System.Text
Imports System.IO
Imports System.Diagnostics
Class PipeAsync
Shared output As New StringBuilder
Shared Sub main()
' 子プロセスで処理したいテキスト
Dim text As String = File.ReadAllText("sample.txt", _
Encoding.GetEncoding("Shift_JIS"))
Dim p As New Process()
' 子プロセスの実行ファイル名
p.StartInfo.FileName = "cat.exe"
' 子プロセスのオプション(もしあれば)
p.StartInfo.Arguments = "-n"
' コンソール・ウィンドウを開かない
p.StartInfo.CreateNoWindow = True
' シェル機能を使用しない
p.StartInfo.UseShellExecute = False
' 標準出力をリダイレクト
p.StartInfo.RedirectStandardOutput = True
' 標準入力をリダイレクト
p.StartInfo.RedirectStandardInput = True
' イベント・ハンドラ設定
AddHandler p.OutputDataReceived, AddressOf OutputHandler
p.Start() ' 子プロセスの実行開始
p.BeginOutputReadLine() ' 子プロセスの出力読み込み開始
Using sw As StreamWriter = p.StandardInput
sw.Write(text) ' 子プロセスへの書き込み
End Using
p.WaitForExit() ' 子プロセスが終了するのを待つ
p.Dispose() ' 子プロセスの破棄
Console.WriteLine(output.ToString()) ' 子プロセスの出力を表示
End Sub
' 子プロセスが標準出力に出力したときに呼び出されるメソッド
Shared Sub OutputHandler(ByVal o As Object, ByVal args As DataReceivedEventArgs)
output.AppendLine(args.Data) ' 出力されたデータを保存
End Sub
End Class
' コンパイル方法:vbc pipeasync.vb
|
|
子プロセスの標準出力を非同期に読み込むVBのプログラム(pipeasync.vb) |
実行する場合には、子プロセスで処理したいテキスト、子プロセスの実行ファイル名、オプションの部分を修正する必要がある。
|
このプログラムでは、子プロセスの出力をStringBuilderクラス(System.Text名前空間)により蓄積している。これは単純な文字列の連結よりも処理が高速だ。またC#版では、イベント・ハンドラの記述で匿名メソッドを使用することにより、プログラムをもう少しシンプルにできる。
利用可能バージョン:.NET Framework 2.0以降
カテゴリ:クラス・ライブラリ 処理対象:Windows環境
使用ライブラリ:Processクラス(System.Diagnostics名前空間)
使用ライブラリ:DataReceivedEventArgsオブジェクト(System.Diagnostics名前空間)
使用ライブラリ:StringBuilderクラス(System.Text名前空間)
関連TIPS:コンソール・アプリケーションの出力を取り込むには? |
|
generated by
|
|
Insider.NET 記事ランキング
本日
月間