.NET Framework 別のプログラムを呼び出す

Last-modified: 2006-11-04 (土) 16:08:05

ウィンドウを表示させない

System::Diagnostics::Process * encoderProcess;
encoderProcess = new System::Diagnostics::Process();
encoderProcess->StartInfo->FileName = fileName;
encoderProcess->StartInfo->Arguments = argument;
encoderProcess->StartInfo->CreateNoWindow = true;
encoderProcess->StartInfo->UseShellExecute = false;
encoderProcess->Start();

コンソール出力横取り

呼び出したプログラムがコンソールプログラムだった場合に
そのプログラムがコンソールへ何かしら出力を行うのだが、
上記のようにウィンドウを表示させない状態では、何が何だかわからない。
そこで。
コンソールプログラムの出力を横取り、というかリダイレクトする。
サンプルプログラムは以下のようなもの。

System::Diagnostics::Process * toolProcess;
toolProcess = new System::Diagnostics::Process();
toolProcess->StartInfo->FileName  = S"ipconfig.exe";
toolProcess->StartInfo->Arguments = S"/all";
toolProcess->StartInfo->UseShellExecute = false;
toolProcess->StartInfo->CreateNoWindow = true;
/* (1) コンソール画面への出力を無理矢理受け取る設定 */
toolProcess->StartInfo->RedirectStandardOutput = true;
/* (2) コンソールプログラムをスタート */
toolProcess->Start();
/* (3) コンソール画面への出力をStreamReaderで受け取る */
System::IO::StreamReader * reader = toolProcess->StandardOutput;
/* (4) コンソール画面への出力をIDEのデバッグウィンドウに書き出す */
System::Diagnostics::Debug::WriteLine( reader->ReadToEnd() );

これ、順番が大事。
(1)~(4)の順番は守る必要があります。
順番を守らずに、たとえば(2)と(3)を入れ替えると、実行時に以下の例外が発生します。

System.InvalidOperationException: StandardOut がリダイレクトされていません。

ま、なんか、それくらいいいじゃんって感じですけど、
私はこれが原因で1時間ほど悩みました。
・・・順番て・・
いや、(1)と(2)が入れ替わっちゃいけないのはわかるんですけどね。
だって(1)は「StartInfo」ですもんね。

コンソール出力横取りの超絶注意点~その1

取得したストリームを閉じない限り呼び出したプログラムは終了しなくなります。
というわけで、上記の例に追加コード。(4)の後にくっつけます。

/* (3) コンソール画面への出力をStreamReaderで受け取る */
System::IO::StreamReader * reader = toolProcess->StandardOutput;
/* (4) コンソール画面への出力をIDEのデバッグウィンドウに書き出す */
System::Diagnostics::Debug::WriteLine( reader->ReadToEnd() );
/* (5) ストリームを閉じる。閉じないと、プロセスが終了しない */
reader->Close();

当然ながら、ストリームを閉じた後にreader->ReadToEnd()を呼ぶと例外が発生します。

System.ObjectDisposedException: 閉じている TextReader からは読み取れません。

また、呼び出したプログラムの終了を待つために
WaitForExit()を使う場合、(5)の後ろに入れます。

/* (3) コンソール画面への出力をStreamReaderで受け取る */
System::IO::StreamReader * reader = toolProcess->StandardOutput;
/* (4) コンソール画面への出力をIDEのデバッグウィンドウに書き出す */
System::Diagnostics::Debug::WriteLine( reader->ReadToEnd() );
/* (5) ストリームを閉じる。閉じないと、プロセスが終了しない */
reader->Close();
/* (6) プロセスの終了を待つ */
toolProcess->WaitForExit();

こうしないと、(6)で永遠に待ち続けます。無限ループです。
いやはや、こんなにも順番の大事な仕様があってもいいのか・・?

コンソール出力横取りの超絶注意点~その2

実行中のプロセスを止めたいときは「Kill」するのだが、
WaitForExit()の前でKill()しないとダメなことは明らかだが、
ReadToEnd()もWaitForExit()と同様に完了復帰型の関数なので、
関数の処理が終了するまでメイン処理に戻ってこれない。
結果として、Kill()に処理が渡らない。
気をつけよう。

注意点

呼び出した子プロセスが終了するよりも早くに親プロセスが死んだら、
子プロセスは生き続けることがあるので、注意。