アプリケーションの標準出力をテキストファイルに出力してみた(3)

どもです。

前々回前回と、別プロセスの標準出力の内容をファイルに出力する方法について書いてきました。今回は、その内容の最後であり、一番やりたかった、書きたかった内容になります。

0. 作業環境

今回のエントリでは、以下の環境で作業を行います。

開発環境
項目 内容
OS Windows10 Pro(22H2)
CPU i7-8700
メモリ 16GB
IDE Visual Studio Commnuity 2022(64bit)
version 17.3.6

1. 書きたかったこと

「一番書きたかったこと」とは

別プロセスで起動したプログラムが終了したら、起動元プログラムも終了するようにするための方法

です。

書いてしまえば、「そんなこと!?」と思うかもしれません。しかし、いざ実現してみようと思うと、意外に厄介でした。

2. 厄介な理由

別プロセスが終了したら起動元プログラムも終了する、ということが「厄介」になった理由は、

起動した別プロセスへの標準入力が必要になるから

です。

どういうことかは、以下のコードを読みながら説明します。

using (var stdin = proc.StandardInput)			//「別プロセスの標準入力」の取得
{
	do
	{
		var inputData = Console.ReadKey();	//「入力内容」の取得
		if (Environment.NewLine.Contains(inputData.KeyChar))
		{
			stdin.WriteLine();
		}
		else
		{
			stdin.Write(inputData.KeyChar);	//読み出した入力内容を書き込む
		}
	} while (true == _isContinue);
}

これは、前回紹介したコードになります。このコードは、よくよく読む、あるいは実際に動かすとよくわかるのですが、「『入力内容』の取得」の処理が原因で、別プロセスが完了しても、呼び出し元のプロセスが終了しません。即ち、ユーザが何かキーを押下しないと、

var inputData = Console.ReadKey();	//「入力内容」の取得

から制御が返らないのです。そのため、呼び出し元プロセスが完了できないのです。

ここで、

別プロセスが終了したら、それを画面上で通知し、ユーザーにキー操作を促す。

という方法でも、十分に対応可能です。しかし、それでは結局「ユーザーの入力が必要」となってしまい勝手が悪く、問題の解決になっていません。

また、

ReadKeyメソッドにタイムアウト機能を持たせる(タイムアウト機能を持つReadKeyメソッドを実装する)

という方法も思いつきます。ネットで調べてみると、この処理の実現方法は見つかります。しかし、見つけた方法は、いずれも少し複雑です。できれば、シンプルな方法で対応したいです。なので、この方法も問題の解決という意味では、最適解ではありませんでした。

こんなカンジで、「厄介」です。

3. 対応

この「厄介」な問題への対応ですが、実にシンプルな解決方法がありました。それは、

キー入力があった場合にのみ、別プロセスの標準入力に、入力内容を渡す。

という方法です。どういうことかは、実際のコードと一緒に説明します。

3.1. 対応のコード

先述の「方法」を実現する実装は、以下のようになります。

using (var stdin = proc.StandardInput)
{
	do
	{
		if (Console.KeyAvailable)		//キー入力の有無を判定する。
		{
			var inputData = Console.ReadKey();
			if (Environment.NewLine.Contains(inputData.KeyChar))
			{
				stdin.WriteLine();
			}
			else
			{
				stdin.Write(inputData.KeyChar);
			}
		}
		else
		{
			Thread.Sleep(10);
		}
	} while (true == _isContinue);
}

このコードでは、KeyAvailableでユーザによるキー入力の有無を判定しています。キー入力があった場合には、入力されたキー情報を取得し、別プロセスの標準入力に書き込みます。キー入力が無かった場合には、少し(コードでは10ms)待ちます(CPU負荷軽減のための処理なので、無くても良いです)。

KeyAvailableプロパティの詳細は、Microsoftのサイトを参照してください。

繰り返しの判定処理の変数_isContinueは、別プロセスの完了通知を受け取るイベントハンドラの中で更新します(今回のエントリでは、省略しています)。

このように実装を変更することで、標準入力への入力完了待ちが発生することなく、別プロセスが完了したら、そのまま呼び出し元プロセスも完了することができます。

4. まとめ

今回まで、3回にわたって「アプリケーションの標準出力をテキストファイルに出力」する方法について書いてきました。

単に「アプリケーションの標準出力をテキストファイルに出力」するだけであれば、それほど難しくなく、また大変ではありません。少し調べれば、対応方法は沢山見つかります。ところが、全てのアプリケーションが標準出力のみ、というわけではありません。標準入力、ユーザからの入力を必要とするアプリケーションもあります。そのため、標準入力への入力も受け取りつつ、かつ標準出力の内容もファイルへ出力する、という方法が必要です。

しかし、標準入力を受け付けるようにすると、どうしても入力待ちが発生してしまいます。これにより、別プロセスが終了しても、呼び出し元プロセスが完了しない、という問題が発生してしまいました。

この問題に対して、これまでのエントリでは、KeyAvailableプロパティによるキー入力判定という方法を提案、解決しました。

実際に動かしている様子や結果を載せていないので説得力に欠けるとは思いますが…これまで紹介してきた方法が誰かの助けになれば幸いです。

ではっ!

ex. 公開しています。

GitHubで、今回作成した一連のコードを公開しています。サンプルプログラムも一緒に公開していますので、使い方や結果と併せて参考にしていただければ幸いです。