(小技) c#でコンソールプログラムを同一プロセスで呼び出す方法

**やってみたらできちゃった**ので、一応メモ代わりに投稿しておきます。
~~どれだけ需要があるのかわかりませんが…~~

あと、既出だったら申し訳ありません。

1. [概要](#概要)
1. [実装可能な条件](#実装可能な条件)
1. [サンプルコード](#サンプルコード)
1. [制限事項](#制限事項)
1. [その他](#その他-というタイトルの駄文)


# 【概要】
コンソールプログラムを**同一プロセスで、新たにプロセスを起こすことなく**呼び出すことができます。

# 【実装可能な条件】
どんなプログラムでも呼び出せるわけではありません。以下の条件を満たす必要があります。

1. 親プログラムのプロジェクトの依存関係に、子プログラムのプロジェクトが追加されており、かつ
1. 子プログラムの `Program` クラスと `Main` メソッドが共に `pubic` であること。

+ 実行したい子プログラムが .NET のプログラムではないのならどちらの条件も 1 の条件が満たせないのでアウトです。
+ 親プログラムと子プログラムのプロジェクトが同じソリューションに属していればどちらの条件も容易に満たすことができます。
+ 他人の作ったプログラムや、他のソリューションで開発したプログラムを呼び出すのは、1 の条件が満たせないので現実的ではない[^2]と思われます。

# 【サンプルコード】

以下は親プログラム側のコードです。
```c#
using System;
using System.Diagnostics;

namespace ParentProgram
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine($"I am the parent program. (process-id: {Process.GetCurrentProcess().Id})");
            Console.WriteLine("----------  start of child program  ----------");
            ChildProgram.Program.Main(new[] { "first", "second", "third" });
            Console.WriteLine("----------  end of child program  ----------");
            Console.Beep();
            Console.WriteLine("completed");
            _ = Console.ReadLine();
        }
    }
}
```

以下は子プログラム側のコードです。
```c#
using System;
using System.Diagnostics;
using System.Linq;

namespace ChildProgram
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine($"I am the child program. (process-id: {Process.GetCurrentProcess().Id})");
            Console.WriteLine($"args: [{string.Join(", ", args.Select(arg => $"\"{arg.Replace(@"\", @"\\").Replace(@"""", @"\""")}\""))}]");
        }
    }
}
```

以下が実行結果としてコンソールに表示された内容です。同一プロセスで実行されていることがわかります。
```
I am the parent program. (process-id: 18476)
----------  start of child program  ----------
I am the child program. (process-id: 18476)
args: ["first", "second", "third"]
----------  end of child program  ----------
completed
```

これらのプログラムのソリューションは下の場所にあります。
[https://github.com/rougemeilland/Experiment.DirectlyCallChildConsoleProgram](https://github.com/rougemeilland/Experiment.DirectlyCallChildConsoleProgram)

# 【制限事項】
子プログラムを呼び出すことはできますが以下の制限があります。(他にもあるかもしれません)

+ 依存関係を登録しなければならないので、親プログラムと子プログラムが同じソリューションに属していることがほぼ必須であると思われます。
+ 同一プロセス上で動作する以上は標準入出力も親プログラムと子プログラムで共有することになるので、「子プログラムの標準入出力を別のプログラムにリダイレクトして、云々」というようなことができません。

<a id="etc"></a>
# 【その他 (というタイトルの駄文)】
何でこんなことを思いついたかという話になります。

今、とあるコンソールコマンド群を作っており、あるコマンドプログラムから別のコマンドプログラムを起動して云々、みたいなことをやっています。
しかし、Visual Studio ではプロジェクトごとにアセンブリの出力先が別になってしまうため、デバッグの際に親プロセスから子プロセスのプログラムのパス名の解決が面倒です。
リリース後は子プログラムのアセンブリは親プログラムと同じ場所に配置する予定だったので面倒はないのですが、デバッグビルド時のみとはいえ開発中のファイル配置を意識したコーディングは持ち込みたくありません。

かなり昔に同じような問題に直面して **「これ、親プログラムの依存関係に子プログラムを追加したら子プログラムの実行可能ファイルが親プログラムと同じ場所にも出力されてデバッグ時に助かるんじゃね?」** てなことを考えてやってみたのですが、当時のバージョンの Visual Studio はライブラリ以外を依存関係には登録できなかった[^1]ので、実現できませんでした。

そして現在になってやはり同じ問題にぶつかって、駄目元と思って**親プログラムの依存関係に親プログラムのプロジェクトを追加しようとしてみたらできちゃった**わけです。ちゃんと子プロセスの実行可能ファイルが親プログラムのプロジェクトの出力ディレクトリにも格納されていました。

そして、前々から不思議に思っていて今も理解できていないのですが、コンソールプログラムをビルドしても exe ファイルの他に何故か dll ファイルもできてしまいます。

それを思い出して、**「あれ? これもしかして直接 Main を呼び出せちゃうんじゃね?」** と思ってやってみたら呼び出せてしまいました。~~需要があるのかはさておき。~~

まぁ、素直に別プロセスとして実行するのが無難な気がしますが、標準入出力のリダイレクトとかが必要ない状況なら、手軽に呼び出せるので役に立つこともある気がします。きっと。おそらく。多分。May be。

[^1]: もしかしたら、Visual Studio の問題ではなく、使用しているフレームワークの種類の問題だったのかもしれません。ちなみに昔使用していたフレームワークは .Net Framework 4.7.x で、現在使用しているのは .NET6.0/7.0 です。

[^2]: 呼び出したい子プログラムが nuget パッケージになっていれば依存関係が登録されるのでいけるかもしれませんが、未検証です。~~というよりこれがそこまでしてやりたいテクニックかどうかですね…~~