ねののお庭。

かりかりもふもふ。

【C#】ConsoleアプリがMain関数の終了と同時に終了しないようにする。

タイトルが分かりずらいですね、はい。

コンソールアプリ、Main関数が終了すると同時にアプリは終了します。 それはそう、当たり前。 なんですが裏のスレッドでなんか動いてるからアプリは落ちてほしくない、けどMainは終わっとるし別にawaitするべきものもない、みたいなケースは稀に良くあります。 でそういう場合はアプリが落ちないようにConsole.ReadLine();とか仕込むのが常套手段だったりするわけです。

ただまぁ、書き捨てコードにしろConsole.ReadLine();等で対処するのなんか嫌...となったので、ちょっと考えました。 で、出来たのが以下。

同期用と非同期用あります。 まぁ、C#7.1以降はエントリーポイントでも普通にasync/await使えるので前者いるか?って感じではあるけど一応。 上のクラス適当にコピペして、Main関数の最後に

await Exit.WaitAsync();

Exit.Wait();

ってすればアプリが落ちるのを防げます。Console.ReadLine();等で対処するよりはいいでしょう。

Console.CancelKeyPress

ここから余談です。

アプリケーションを終了(キャンセル)させるのにCtrl+C叩くのは日常茶飯事なわけですが、そのイベントはConsole.CancelKeyPressで拾えます。 このハンドラはdelegate void ConsoleCancelEventHandler(object? sender, ConsoleCancelEventArgs e);という型で定義されてます。 上のスクリプトから分かる通り、Ctrl+Cが叩かれたらConsoleCancelEventArgs eCanceltrueに設定しています。

Console.CancelKeyPress += (sender, e) =>
{
    e.Cancel = true;
};

このCancel、「アプリケーションのキャンセル(Ctrl+C)をキャンセルする」という二重否定の類のものとなってるようです。 つまり、Cancelfalseではアプリケーションが落ち、逆にtrueではアプリが落ちないで継続する、という具合です。 既定はfalse

docs.microsoft.com

別にアプリは落ちてくれていいのでtrueに設定する必要なくない?という感じなのですが、

await Exit.WaitAsync();

Console.WriteLine("end.")

とかあった場合即時終了されるとConsole.WriteLine("end.")に到達しないのでCancel=trueに設定するわけです。

と思いきや、別の理由からもCancel=trueに設定してないとマズそうです。 自分は次のような問題を踏みました。

Cancel=falseに設定して即時終了される事を期待していても、Console.WriteLine("end.")に到達する。 そしてMainの終端に到達しても、アプリが終了しない。 加えて何度Ctrl+Cを叩いても、アプリを落とせなくなる。

なんともけったいな状況に陥ります。 最終的にターミナルごと死んでもらったりしないといけなくなります。嫌すぎる。 なんでなん、という挙動ですが、とりあえずは受け入れる他ない。

というわけで即時終了してほしくてもConsole.CancelKeyPressに触れるなら Cancel=trueに設定した方が良さそうです。

(thaks to @pCYSl5EDgo)