Processクラスを使うことで子プロセスを起動することができ、起動した子プロセスの標準ストリーム(standard streams)を扱うこともできます。 子プロセスの標準入力(standard input, stdin
)や標準出力(standard output, stdout
)などの標準ストリームを扱う場合は、ProcessStartInfoクラスで標準ストリームをリダイレクトするためのオプションを指定して子プロセスを起動します。
具体的には、RedirectStandardInput/RedirectStandardOutputなどのプロパティをtrue
にすることで、子プロセスの標準ストリームをリダイレクトさせます。 これにより、親プロセス側から子プロセスの標準入力へ書き込む/標準出力を読み込むことができます。 .NET Frameworkでの場合、標準ストリームをリダイレクトするにはデフォルトでtrue
になっているUseShellExecuteプロパティもfalse
にする必要があります。
リダイレクトした標準ストリームは、起動した子プロセス(Process)のStandardInput/StandardOutputなどのプロパティを介して操作することができます。 StandardInput/StandardOutputプロパティはStreamWriter/StreamReaderとなっているため、WriteLine/ReadLineといったメソッドにより標準入出力への読み書きを行います。 (§.標準入力への書き込み、§.標準出力・標準エラーからの読み込み)
また、標準出力と同様にRedirectStandardErrorで標準エラー(standard error, stderr
)をリダイレクトすることにより、StandardErrorプロパティから子プロセスの標準エラーを読み取ることもできます。
ここでは、子プロセスを起動し、標準ストリームを扱う方法について解説します。 子プロセスの起動方法、および起動に際した標準ストリーム以外のオプションについてはプロセス §.子プロセスの起動オプションを、特に子プロセスのコマンドライン引数についてはプロセス §.子プロセスのコマンドライン引数を参照してください。 自プロセスの標準ストリームについては自プロセスの標準入出力を参照してください。
標準入力への書き込み
子プロセスの標準入力への書き込みを行うには、ProcessStartInfoのRedirectStandardInputプロパティをtrue
にした上で子プロセスを起動します。 次に、起動した子プロセスのStandardInputプロパティに設定されるStreamWriterを取得します。 これに対して書き込みを行うことにより、子プロセスの標準入力へ書き込むことができます。
例として、親プロセスparent.exeから子プロセスchild.exeを起動し、子プロセスの標準入力にテキストを書き込む場合は次のようになります。
このようにして子プロセスの標準入力に書き込みを行うことができます。 子プロセスの標準入力はStreamWriterとして取得できるため、ファイル等への書き込みと同様にStreamWriterのメソッドを使って書き込みを行うことができます。
なお、この例でのchild.exeは標準入力から読み込めなくなるまで読み込みを続けるようになっています。 標準入力の末尾(EOF)に到達するまで読み込み続けるようなプロセスを起動する場合、親プロセスは子プロセスの終了を待機する一方、子プロセスは標準入力が閉じられるまで読み込みを続けようとし、互いに待機状態となったまま処理が進行しないという状況が起こり得ます。 このため親プロセス側では、子プロセスの終了を待機する前にusingステートメントやCloseメソッドの呼び出しによって標準入力を閉じる必要があります。
標準入力を含め標準ストリームをリダイレクトするためにはProcessStartInfo.UseShellExecuteがfalse
である必要があります。 .NET Core/.NET 5以降ではデフォルトでfalse
となっている一方、.NET Frameworkではtrue
となっているため、明示的に指定する必要があります。
子プロセスを起動する際にコマンドライン引数を指定する方法についてはプロセス §.子プロセスのコマンドライン引数を、その他起動時のオプションについてはプロセス §.子プロセスの起動およびプロセス §.子プロセスの起動オプションを参照してください。
標準出力・標準エラーからの読み込み
子プロセスの標準出力からの読み込みを行う場合は、ProcessStartInfoのRedirectStandardOutpoutプロパティをtrue
にして子プロセスを起動します。 次に、起動した子プロセスのStandardOutputプロパティに設定されるStreamReaderを取得します。 これに対して読み込みを行うことにより、子プロセスの標準出力を読み込むことができます。
標準エラーの場合も同様に、RedirectStandardErrorプロパティをtrue
にし、StandardErrorプロパティのStreamReaderから読み込みます。
例として、親プロセスparent.exeから子プロセスchild.exeを起動し、子プロセスの標準出力・標準エラーからテキストを読み込む場合は次のようになります。
このようにして子プロセスの標準出力・標準エラーから読み込みを行うことができます。 子プロセスの標準出力・標準エラーはStreamReaderとして取得できるため、ファイル等からの読み込みと同様にStreamReaderのメソッドを使って読み込みを行うことができます。
標準出力・標準エラーを含め標準ストリームをリダイレクトするためにはProcessStartInfo.UseShellExecuteがfalse
である必要があります。 .NET Core/.NET 5以降ではデフォルトでfalse
となっている一方、.NET Frameworkではtrue
となっているため、明示的に指定する必要があります。
子プロセスを起動する際にコマンドライン引数を指定する方法についてはプロセス §.子プロセスのコマンドライン引数を、その他起動時のオプションについてはプロセス §.子プロセスの起動およびプロセス §.子プロセスの起動オプションを参照してください。
標準入出力への読み書き
子プロセスの標準入力のリダイレクトと標準出力・標準エラーのリダイレクトは同時に組み合わせることもできます。 これにより、子プロセスの標準入力に書き込み、子プロセスで何らかの処理を行った結果を標準出力から読み込む、さらに子プロセスの標準エラー出力も読み込む、といったことができます。
さらに、自プロセスの標準ストリームの入出力先を子プロセスの標準ストリームへリダイレクトすることにより、子プロセスの標準ストリームとパイプすることもできます。
自プロセスと子プロセスの標準ストリームをパイプする
ConsoleクラスのSetIn・SetOut・SetErrorの各メソッドを使うことにより、自プロセスの標準ストリームをリダイレクトすることができます。 (自プロセスの標準入出力 §.標準ストリームのリダイレクト) これと子プロセスの標準ストリームのリダイレクトを組み合わせることにより、自プロセスと子プロセスの標準ストリームをパイプ(pipe)することができます。
標準ストリームをパイプするとは、自プロセスの標準出力の出力先を子プロセスの標準入力の入力元とする、また逆に子プロセスの標準出力の出力先を自プロセスの標準入力の入力元とすることで、これによりプロセス間で標準ストリームを介したやりとりをすることができます。
自プロセス | ⇄ | 子プロセス |
---|---|---|
標準出力 Console.Out |
→ | 標準入力 Console.In |
標準入力 Console.In |
← | 標準出力 Console.Out |
Console.SetIn/SetOut/SetErrorの各メソッドを使って自プロセスの標準ストリームをリダイレクトすると、Consoleクラスでの入出力先はリダイレクト先へと変更されます。 Consoleクラスのデフォルトの入出力ストリームは、プロセスを起動したターミナルやコマンドプロンプト、IDEの出力ウィンドウなどに接続されているため、標準出力・標準エラーをリダイレクトすると以降そこには一切出力されなくなります。
イベントハンドラを使った標準出力の非同期読み込み
Process.BeginOutputReadLine/BeginErrorReadLineメソッドを使うと、標準出力・標準エラーに出力されるたびにイベントを発生させることができ、イベントハンドラによって非同期的に読み込むことができます。
このメソッドを呼び出すと、標準出力・標準エラーに書き込まれた内容はOutputDataReceivedイベント/ErrorDataReceivedイベントとして通知されます。 このイベントは、開始メソッドの名称Begin*ReadLineが示すように、標準出力・標準エラーから1行読み込まれるごとに(改行文字が出力されるたびに)発生します。 また、Processクラスのイベントであるため、ProcessStartInfoクラスで起動オプションと同時にイベントハンドラを設定することはできません。
イベントハンドラとなるDataReceivedEventHandlerデリゲート型では、DataReceivedEventArgs.Dataプロパティを参照することにより、標準出力・標準エラーに出力された内容を得ることができます。 イベントは1行ごとに発生するため、DataReceivedEventArgs.Dataプロパティからは改行文字を除いた1行分の内容が取得されます。
子プロセスが終了して標準出力・標準エラーが閉じられた場合は、Dataプロパティにnull
/Nothing
が設定された状態でイベントが発生するため、これを元にデータの終端あるいは子プロセスの終了を検知することができます。 なお、イベントハンドラの引数sender
には子プロセスのProcessが渡されます。
BeginOutputReadLine/BeginErrorReadLineを使った非同期読み込みは、同期的に読み込む場合と同様にProcessStartInfo.RedirectStandardOutput/ RedirectStandardErrorにtrue
を指定して子プロセスを起動します。 また、子プロセスを起動したあと、読み込みを開始する時点でProcess.BeginOutputReadLine/BeginErrorReadLineメソッドを呼び出します。
一度非同期的な読み込みを開始すると、以降はStandardOutput/StandardErrorを使った同期的な読み込みは例外InvalidOperationExceptionをスローするようになり、一切できなくなります。
一度開始した非同期的に読み込みは、CancelOutputRead/CancelErrorReadメソッドを呼び出すことで中止することができます。 また、中止したあとに再度BeginOutputReadLine/BeginErrorReadLineメソッドを呼び出すことで読み込みを再開することもできます。
CancelOutputRead/CancelErrorReadメソッドを呼び出すとイベントの発生が中止され、再度BeginOutputReadLine/BeginErrorReadLineメソッドを呼び出すとイベントの発生が再開されます。 ただし、中断している間も標準出力・標準エラーの非同期的な読み込み自体は継続して行われているため、再開後は再開以降に標準出力・標準エラーへ書き込まれた分のイベントが発生します。 (再開までの間に書き込まれた分のイベントは発生しない)
標準ストリームのエンコーディング
ProcessStartInfoでは、子プロセスの標準ストリームで使用するエンコーディングを指定することができます。 子プロセスの標準ストリームが自プロセスとは異なるエンコーディングを使用しているなど、標準ストリームでのエンコーディングを指定する必要がある場合には、以下のプロパティで指定します。
StandardOutputEncoding/StandardErrorEncodingプロパティでは、標準出力・標準エラーで用いるエンコーディングを指定できます。 .NET Standard 2.1/.NET Core 2.1以降では、StandardInputEncodingプロパティで標準入力のエンコーディングも指定できます。
StandardInputEncodingプロパティにUTF-8などのエンコーディングを指定すると、子プロセスの標準入力にBOM(Byte Order Mark)が出力される場合があります。 この動作はランタイムの種類や実行環境によって異なります。 詳細についてはEncodingクラスとBOMありなしの制御 §.標準ストリームを参照してください。
標準ストリームでのバイナリデータの読み書き
子プロセスの標準ストリームとして取得できるオブジェクトはStreamWriter/StreamReaderであるため、バイナリ形式での読み書きには向いていません。 また、非同期読み込みでのイベント引数DataReceivedEventArgsも文字列型(≒テキスト形式)として受信されるようになっています。
子プロセスの標準ストリームに対してバイナリ形式の読み書きを行う場合は、まず標準ストリームのStreamWriter/StreamReaderからBaseStreamプロパティを参照することにより、標準ストリームのStreamを取得します。
次に、BaseStreamプロパティから取得したStreamに対して直接読み書きを行うか、StreamからBinaryWriter/BinaryReaderなどを作成して読み書きすることにより、標準ストリームでバイナリ形式のデータを扱うことができるようになります。
StandardInput/StandardOutput/StandardErrorで取得できるStreamWriter/StreamReaderは内部でバッファリングが行われます。 このため、StandardInput/StandardOutput/StandardErrorを使った読み書きと、BaseStreamから取得したStreamへの読み書きを混在させると入出力内容に不整合が起こる(入出力されるデータの一部の順序が前後する)可能性があり、両者の使用は排他的とする必要があります。
つまり、BaseStreamから取得したStreamで読み書きを行う場合は、それのみを使って読み書きを行う、あるいはどちらか一方をFlushするまではもう一方を使って読み書きしないようにします。
Windows上では、BaseStreamプロパティから取得したStreamを閉じたあとにStandardOutput/StandardErrorを閉じると、StreamWriterがFlushしようとして例外ObjectDisposedExceptionがスローされます。 そのため、StreamWriterとBaseStreamプロパティから取得したStreamの両方をCloseしないように(二重にCloseする動作とならないように)注意する必要があります。
自プロセスの標準ストリームのStreamを取得する方法については自プロセスの標準入出力 §.標準ストリームの取得を参照してください。
Streamクラスを使った読み書きの方法についてはストリームの基本とStreamクラス、BinaryWriterクラス/BinaryReaderクラスを使った読み書きの方法についてはBinaryReaderクラス・BinaryWriterクラスを参照してください。