基本は何も表示しないけど必要に応じてMessageBoxやConsoleを表示するアプリを.NETで作る

この記事は[corp-engr 情シスSlack（コーポレートエンジニア x 情シス）#1 Advent Calendar 2024](https://adventar.org/calendars/10163/)の18日目の記事です

https://adventar.org/calendars/10163/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/9ce5f009-18e4-5621-c328-a1bdb2145cbf.png)
(noteだとAdventarを画像ではなくiframeで埋め込めるみたいなんですが、Qiitaではできないんですね。よって画像です。)

昨日の記事はこちら。担当は私と同じくフルリモ情シスのGOETANさんでした。

https://note.com/goetan/n/neaaf15931cc9

# はじめに
神奈川県在住で名古屋の小売業の情シスをフルリモートでやっているぬーぼーと申します。所属している部署名はTHE情シスみたない部署名なのですが、情シスSlackの多くの方のようにSaaSを管理したりヘルプデスクしたり機材のキッティングしたりセキュリティやったりという業務ではなく、社内の間接部門向けのVB.NETやC#で作られた内製アプリの保守や開発が主な業務になります。情シスになる前はSIerにいましたが、立場が変わっただけで業務はあまり変わっていません。
ですので、この記事もプログラミング寄りの記事になります。ということで、今年のアドベントカレンダーはQiitaへの投稿になります。（去年の情シスアドベントカレンダーは技術的な話ではなく、働き方の話だったのではてなブログに投稿しました）



# やりたいこと
本件は、情シスなら1つや2つは必要になるであろう監視プログラムを想定しています。監視に自前のプログラム作らず、監視ツールで何とかすべきではという意見もあると思いますがここでは置いときます。
監視プログラムなどで基本的に何も表示しないプログラム作りたいことありませんか？私はあります。これは好みが分かれるところで、何もなくても動いていることがわかるようにコンソールが出たほうが好みという方もいらっしゃるとは思います。
さらに監視結果に異状があればメッセージボックスを出したい、そこから異常に対処する処理（例えばSQLでデータ修正）を実施、実施状況はコンソールに表示したいということを考えます。

# 対象環境
今回は私が普段使っているC#の話になります。おそらくVB.NETでも同じことができます。
.NETのバージョンは2024年12月時点のLTS、.NET 8とします。
開発環境はVisual Studio 2022を使いますが、VS Codeでもいけるはずです。

# やってみる
.NETで普通に今回の目的に合うプログラムを作ろうとすると「Windowsフォームアプリ」か「コンソールアプリ」でスタートすると思います。（他にWebアプリも作れますが、明らかに今回の目的とは違うので置いておきます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/6aebf24f-1829-9ed3-9bf5-24091951a07b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/35a8a2a4-e843-0a07-4e2d-5d8b3f1b628c.png)
これらはいずれも「何も表示しない」アプリではありません。何か手を加えれば「何も表示しない」を達成できるのでしょうか？
## コンソールアプリからやってみる
まず普通にコンソールアプリを実行するとコンソール（いわゆる黒い画面）が出てしまいます。が、まあこれは妥協できるかなと思ったりもしますのでやってみましょう。ですが、MessageBoxを出すことができません。`MessageBox.Show()`を記述してもエラーになってしまいます。これは、コンソールアプリのテンプレートからプロジェクトを作ると`System.Windows.Forms`が参照されていないからです。参照設定自体がされていないのでusingに書いてもダメです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/25fcebca-e5cb-00bc-e4c8-b735ab018dd3.png)
WinFormのテンプレートから作成したプロジェクトと比較すると以下の違いがあることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/9a43da45-6181-2966-984d-22c505b8e8a1.png)
csprojを編集すれば多分コンソールアプリのテンプレートから作ったものでも同じようにできると思いますが、ややこしいので止めておきます。そもそも、頑張ってコンソールアプリでMessageBox出せるようにしたところで、今回の目的である「基本何も表示しない」が達成できません。WinFormsのテンプレートとコンソールアプリのテンプレートで何が違うのかは追加でもう一つ記事を書いて[C# Advent Calendar 2024](https://qiita.com/advent-calendar/2024/csharplang)にエントリーしましたのでご覧ください。

https://qiita.com/noobow/items/0b2bc67ba73d97a509e4

## WinFormsアプリからやってみる
ではWinFormsのテンプレートからやってみるとどうでしょう？
当然ですが、WinFormsのテンプレートからプロジェクトを作って普通に実行するといわゆるWindowsアプリのウィンドが表示されてしまいます。
しかし、Formの表示は暗黙的に行われているのではなくちゃんとソースコードに書かれています。これを消してやれば何も表示されず、そこに何も表示しない状態で実行したい処理を書いてやればいいのでは？という気がしてきましたね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/d2ee2a2b-9f8f-ba92-88b5-5b9f8666b237.png)
さらにこのプロジェクトは`System.Windows.Forms`も参照されていますから、必要に応じてMessageBoxを出すこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/59a6d2d2-6df6-aa1d-dc0a-15dfdd447b1f.png)
では必要に応じて状況をコンソールに表示するにはどうしたらよいでしょう？
`Console.WriteLine("hogehoge");`と書くとコンパイルエラーにも実行時エラーにもなりませんがどこにもコンソールがないので表示されません。勝手にコンソールが出てきてくれるなんてことはありません。
実はコンソールを表示させる（割り当てる）方法があります。kernel32.dllのAllocConsole関数を使用します。
DllImportで以下のように宣言して、コンソールを表示したい場所で呼び出します。
```csharp
[DllImport("kernel32.dll")]
private static extern bool AllocConsole();
```

```csharp
// コンソールを表示する
AllocConsole();
// コンソールに出力
Console.WriteLine("hogehoge");
```

これで、
* 基本は何も表示せず処理を実行する
* 必要に応じてMessageBoxを表示する
* 必要に応じてConsoleを表示する

というアプリを作ることができました。
ちなみに、もちろん元あったFormを表示しているソースを使えば、必要に応じてFormを表示もできます。

# まとめ
1.Windowsフォームアプリケーションのテンプレートからプロジェクトを作成
2.ソースはこんな感じで編集
```csharp
using System.Runtime.InteropServices;

namespace WinFormsApp1
{
    internal static class Program
    {
        [DllImport("kernel32.dll")]
        private static extern bool AllocConsole();

        [STAThread]
        static void Main()
        {
            // 何も表示せずにやりたい処理

            // MessageBoxを表示
            MessageBox.Show("hogehoge");

            // コンソールを表示する
            AllocConsole();
            // コンソールに出力
            Console.WriteLine("hogehoge");
        }
    }
}
```

明日はこれまた同じくフルリモ情シスのkazeさんが担当です。