ProcessStartInfo.RedirectStandardOutputプロパティをTrueにしてプロセスの出力がProcessインスタンスのStandardOutputプロパティ(StreamReaderクラス)に書き込まれるようにすることで、出力データを取得できるようになります。
下の例ではDOSのDIRコマンドを実行して、その出力結果を取得しています。
'Processオブジェクトを作成 Dim p As New System.Diagnostics.Process() 'ComSpec(cmd.exe)のパスを取得して、FileNameプロパティに指定 p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec") '出力を読み取れるようにする p.StartInfo.UseShellExecute = False p.StartInfo.RedirectStandardOutput = True p.StartInfo.RedirectStandardInput = False 'ウィンドウを表示しないようにする p.StartInfo.CreateNoWindow = True 'コマンドラインを指定("/c"は実行後閉じるために必要) p.StartInfo.Arguments = "/c dir c:\ /w" '起動 p.Start() '出力を読み取る Dim results As String = p.StandardOutput.ReadToEnd() 'プロセス終了まで待機する 'WaitForExitはReadToEndの後である必要がある '(親プロセス、子プロセスでブロック防止のため) p.WaitForExit() p.Close() '出力された結果を表示 Console.WriteLine(results)
//Processオブジェクトを作成 System.Diagnostics.Process p = new System.Diagnostics.Process(); //ComSpec(cmd.exe)のパスを取得して、FileNameプロパティに指定 p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec"); //出力を読み取れるようにする p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardInput = false; //ウィンドウを表示しないようにする p.StartInfo.CreateNoWindow = true; //コマンドラインを指定("/c"は実行後閉じるために必要) p.StartInfo.Arguments = @"/c dir c:\ /w"; //起動 p.Start(); //出力を読み取る string results = p.StandardOutput.ReadToEnd(); //プロセス終了まで待機する //WaitForExitはReadToEndの後である必要がある //(親プロセス、子プロセスでブロック防止のため) p.WaitForExit(); p.Close(); //出力された結果を表示 Console.WriteLine(results);
ヘルプによると、上の例の様にWaitForExitをReadToEndの後にしないと、親プロセスと子プロセスの両方でブロック処理が発生し、デッドロックの原因となるということなので、注意が必要です。
補足:上記のコードは内部コマンドを実行しているため、ProcessStartInfo.FileNameプロパティにComSpec(cmd.exe)を、ProcessStartInfo.Argumentsプロパティに実行するコマンドを指定しています。XCOPY、MOVE、DELTREE、ATTRIB、FORMATなどの外部コマンド(あるいは、バッチファイル)を実行する時は、ProcessStartInfo.FileNameプロパティに外部コマンドの実行ファイル名(XCOPY.EXE、MOVE.EXE、DELTREE.EXE、ATTRIB.EXE、FORMAT.EXEなど)を、ProcessStartInfo.Argumentsプロパティにコマンドライン引数を指定した方がよいでしょう。
通常はcmd.exeでも外部コマンドを実行できますが、コマンドライン引数が複雑になる恐れがあります。例えば、実行ファイル名とその引数にスペース文字が含まれる場合は、スペース文字が含まれる実行ファイル名と引数をダブルコーテーションで囲み、さらに全体(実行ファイル名 + すべての引数)をダブルコーテーションで囲む必要があります。詳しくは、「CMD.exe (Command Shell)」等をご覧ください。
補足:XCOPYを実行する時は、ProcessStartInfo.RedirectStandardInputプロパティをTrueにしないとうまくいかないようです。詳しくは「xcopy help」をご覧ください。
上の例のようにReadToEndメソッドを使って出力データを読み取ると、データをすべて読み取るまでReadToEndメソッドでブロックされ、次の行に進むことがありません。しかし、ReadToEndメソッドの代わりにProcess.BeginOutputReadLineメソッドを使うと、非同期で出力データを読み取ることができるようになり、実行がブロックされることがなくなります。この時、出力データはProcess.OutputDataReceivedイベントハンドラで取得できます。この方法は、.NET Framework 2.0以降でサポートされています。
補足:OutputDataReceivedイベントハンドラで取得できる文字列には行末の改行文字が含まれません。
ちなみに、非同期で読み取り中にProcess.CancelOutputReadメソッドを呼び出すと、読み取り操作をキャンセルすることができます。
以下に、先ほどのコードと同じことを非同期で行うように変更した例を示します。なおこの例は、コンソールアプリケーションになっています。
Class Program 'エントリポイント Public Shared Sub Main(args As String()) 'Processオブジェクトを作成 Dim p As New System.Diagnostics.Process() '出力をストリームに書き込むようにする p.StartInfo.UseShellExecute = False p.StartInfo.RedirectStandardOutput = True 'OutputDataReceivedイベントハンドラを追加 AddHandler p.OutputDataReceived, AddressOf p_OutputDataReceived p.StartInfo.FileName = _ System.Environment.GetEnvironmentVariable("ComSpec") p.StartInfo.RedirectStandardInput = False p.StartInfo.CreateNoWindow = True p.StartInfo.Arguments = "/c dir c:\ /w" '起動 p.Start() '非同期で出力の読み取りを開始 p.BeginOutputReadLine() p.WaitForExit() p.Close() Console.ReadLine() End Sub 'OutputDataReceivedイベントハンドラ '行が出力されるたびに呼び出される Private Shared Sub p_OutputDataReceived(sender As Object, _ e As System.Diagnostics.DataReceivedEventArgs) '出力された文字列を表示する Console.WriteLine(e.Data) End Sub End Class
class Program { //エントリポイント static void Main(string[] args) { //Processオブジェクトを作成 System.Diagnostics.Process p = new System.Diagnostics.Process(); //出力をストリームに書き込むようにする p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; //OutputDataReceivedイベントハンドラを追加 p.OutputDataReceived += p_OutputDataReceived; p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec"); p.StartInfo.RedirectStandardInput = false; p.StartInfo.CreateNoWindow = true; p.StartInfo.Arguments = @"/c dir c:\ /w"; //起動 p.Start(); //非同期で出力の読み取りを開始 p.BeginOutputReadLine(); p.WaitForExit(); p.Close(); Console.ReadLine(); } //OutputDataReceivedイベントハンドラ //行が出力されるたびに呼び出される static void p_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) { //出力された文字列を表示する Console.WriteLine(e.Data); } }
今まで紹介した方法とほぼ同じやり方で、エラー出力を読み取ることもできます。エラー出力を読み取る場合は、、RedirectStandardOutputの代わりにRedirectStandardError、StandardOutputの代わりにStandardError、BeginOutputReadLineの代わりにBeginErrorReadLine、OutputDataReceivedの代わりにErrorDataReceivedを使います。
しかし出力とエラーの両方を読み取る場合は、注意が必要です。両方を同期で読み取るとデッドロックが発生する可能性があります。これを防ぐには、どちらか一方か両方を非同期で読み取るようにします。
以下の例では、出力とエラーの両方を非同期で読み取っています。
Class Program 'エントリポイント Public Shared Sub Main(args As String()) 'Processオブジェクトを作成 Dim p As New System.Diagnostics.Process() '出力とエラーをストリームに書き込むようにする p.StartInfo.UseShellExecute = False p.StartInfo.RedirectStandardOutput = True p.StartInfo.RedirectStandardError = True 'OutputDataReceivedとErrorDataReceivedイベントハンドラを追加 AddHandler p.OutputDataReceived, AddressOf p_OutputDataReceived AddHandler p.ErrorDataReceived, AddressOf p_ErrorDataReceived p.StartInfo.FileName = _ System.Environment.GetEnvironmentVariable("ComSpec") p.StartInfo.RedirectStandardInput = False p.StartInfo.CreateNoWindow = True p.StartInfo.Arguments = "/c dir c:\ /w" '起動 p.Start() '非同期で出力とエラーの読み取りを開始 p.BeginOutputReadLine() p.BeginErrorReadLine() p.WaitForExit() p.Close() Console.ReadLine() End Sub 'OutputDataReceivedイベントハンドラ '行が出力されるたびに呼び出される Private Shared Sub p_OutputDataReceived(sender As Object, _ e As System.Diagnostics.DataReceivedEventArgs) '出力された文字列を表示する Console.WriteLine(e.Data) End Sub 'ErrorDataReceivedイベントハンドラ Private Shared Sub p_ErrorDataReceived(sender As Object, _ e As System.Diagnostics.DataReceivedEventArgs) 'エラー出力された文字列を表示する Console.WriteLine("ERR>{0}", e.Data) End Sub End Class
class Program { //エントリポイント static void Main(string[] args) { //Processオブジェクトを作成 System.Diagnostics.Process p = new System.Diagnostics.Process(); //出力とエラーをストリームに書き込むようにする p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; //OutputDataReceivedとErrorDataReceivedイベントハンドラを追加 p.OutputDataReceived += p_OutputDataReceived; p.ErrorDataReceived += p_ErrorDataReceived; p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec"); p.StartInfo.RedirectStandardInput = false; p.StartInfo.CreateNoWindow = true; p.StartInfo.Arguments = @"/c dir c:\ /w"; //起動 p.Start(); //非同期で出力とエラーの読み取りを開始 p.BeginOutputReadLine(); p.BeginErrorReadLine(); p.WaitForExit(); p.Close(); Console.ReadLine(); } //OutputDataReceivedイベントハンドラ //行が出力されるたびに呼び出される static void p_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) { //出力された文字列を表示する Console.WriteLine(e.Data); } //ErrorDataReceivedイベントハンドラ static void p_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) { //エラー出力された文字列を表示する Console.WriteLine("ERR>{0}", e.Data); } }
プロセスに文字列を入力するには、ProcessStartInfo.RedirectStandardInputプロパティをTrueにして、StandardInputプロパティで取得できるストリームに文字列を書き込みます。これを利用すると、コマンドプロンプトに実行するコマンドを入力することで、複数のコマンドを実行することができます。
以下の例では、「dir c:\ /w」と「dir d:\ /w」を実行しています。出力は、非同期で読み取っています。
Class Program 'エントリポイント Public Shared Sub Main(args As String()) 'Processオブジェクトを作成 Dim p As New System.Diagnostics.Process() '入力できるようにする p.StartInfo.UseShellExecute = False p.StartInfo.RedirectStandardInput = True '非同期で出力を読み取れるようにする p.StartInfo.RedirectStandardOutput = True AddHandler p.OutputDataReceived, AddressOf p_OutputDataReceived p.StartInfo.FileName = _ System.Environment.GetEnvironmentVariable("ComSpec") p.StartInfo.CreateNoWindow = True '起動 p.Start() '非同期で出力の読み取りを開始 p.BeginOutputReadLine() '入力のストリームを取得 Dim sw As System.IO.StreamWriter = p.StandardInput If sw.BaseStream.CanWrite Then '「dir c:\ /w」を実行する sw.WriteLine("dir c:\ /w") '「dir d:\ /w」を実行する sw.WriteLine("dir d:\ /w") '終了する sw.WriteLine("exit") End If sw.Close() p.WaitForExit() p.Close() Console.ReadLine() End Sub 'OutputDataReceivedイベントハンドラ '行が出力されるたびに呼び出される Private Shared Sub p_OutputDataReceived(sender As Object, _ e As System.Diagnostics.DataReceivedEventArgs) '出力された文字列を表示する Console.WriteLine(e.Data) End Sub End Class
class Program { //エントリポイント static void Main(string[] args) { //Processオブジェクトを作成 System.Diagnostics.Process p = new System.Diagnostics.Process(); //入力できるようにする p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardInput = true; //非同期で出力を読み取れるようにする p.StartInfo.RedirectStandardOutput = true; p.OutputDataReceived += p_OutputDataReceived; p.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec"); p.StartInfo.CreateNoWindow = true; //起動 p.Start(); //非同期で出力の読み取りを開始 p.BeginOutputReadLine(); //入力のストリームを取得 System.IO.StreamWriter sw = p.StandardInput; if (sw.BaseStream.CanWrite) { //「dir c:\ /w」を実行する sw.WriteLine(@"dir c:\ /w"); //「dir d:\ /w」を実行する sw.WriteLine(@"dir d:\ /w"); //終了する sw.WriteLine("exit"); } sw.Close(); p.WaitForExit(); p.Close(); Console.ReadLine(); } //OutputDataReceivedイベントハンドラ //行が出力されるたびに呼び出される static void p_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) { //出力された文字列を表示する Console.WriteLine(e.Data); } }