コマンドプロンプトの表示を抑制する

リンカのオプション`SUBSYSTEM`に値`WINDOWS`を指定することで、コマンドプロンプトの表示を抑制することができます。

## はじめに

C言語で`Win32API`などを利用して、何も考えずに実行ファイルを生成すると、以下のようにコンソールが表示されてしまいます。

![window.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425840/a9ba175a-78cd-16e4-54aa-789921082b86.png)

上記のようなコンソール表示はデバッグに役立ちますが、本番のアプリケーションでも表示されてしまうと見栄えがわるくなってしまうので、表示を抑制したい方もいると思います。
本記事ではそのような方を対象に、いくつかの抑制方法を紹介していきます。

ちなみに上記のアプリの作成には以下のコードを利用しています。

```main.c
#include <windows.h>

int WINAPI WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpszCmdLine,
    int nCmdShow
) {
    MessageBox(NULL, TEXT("Sample Text"), TEXT("Sample Window"), MB_OK);
    return 0;
}
```


## IDEから指定する場合

たぶん一番メジャーな方法だと思います。

### VisualStudio2019

`プロジェクト(P) > プロパティ(P) > 構成プロパティ > リンカー > システム > サブシステム` の値に `Windows (/SUBSYSTEM:WINDOWS)`を指定するとコマンドプロンプトの表示を抑制することができます。

![Sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425840/e6674470-83ef-4fd5-92dd-9b273a821527.png)

また、これからプロジェクトを作成する場合は、`Windows デスクトップ アプリケーション`を選択すると、デフォルトでリンカの設定が`Windows (/SUBSYSTEM:WINDOWS)`になるのでおすすめです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425840/1d5a962c-1feb-4039-6f36-ef0747e85048.png)


## ビルドツールから指定する場合

### MSBuild

（設定ファイルを直接弄ります）

プロジェクトファイルの`Project`要素に`ItemDefinitionGroup > Link > SubSystem`要素を作成します。
値には`Windows`を指定します。

これだけでOKです。

```Sample.vcxproj.diff
  <Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
      <ProjectConfiguration Include="Debug|Win32">
        <Configuration>Debug</Configuration>
        <Platform>Win32</Platform>
      </ProjectConfiguration>
    </ItemGroup>
    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.default.props" />
    <PropertyGroup>
      <ConfigurationType>Application</ConfigurationType>
      <PlatformToolset>v142</PlatformToolset>
    </PropertyGroup>
+   <ItemDefinitionGroup>
+     <Link>
+       <SubSystem>Windows</SubSystem>
+     </Link>
+   </ItemDefinitionGroup>
    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
    <ItemGroup>
      <ClCompile Include="main.c" />
    </ItemGroup>
    <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Targets" />
  </Project>
```

### CMake

`CMakeLists.txt`に`CMAKE_EXE_LINKER_FLAGS`をセットする処理を追加するだけです。

```CMakeLists.txt.diff
  cmake_minimum_required(VERSION 3.1)
  project(hello_world C)
+
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS")
+
  add_executable(main main.c)
```

## コンパイラから指定する場合

小さなプロジェクトなら、こちらの方が手軽ですね。

### CL

リンカオプション`/link`を利用して、`/SUBSYSTEM:WINDOWS`を指定するとコマンドプロンプトの表示を抑制することができます。

```cmd
 CL .\main.c user32.lib /link /SUBSYSTEM:WINDOWS
```

### mingw32-gcc

リンカオプション`-Wl`を利用して、`-subsystem,windows`を指定するとコマンドプロンプトの表示を抑制することができます。
ちなみにエントリポイントが`WinMain`（Windowsアプリケーション用のエントリポイント）ではなく`main`（コンソールアプリケーション用のエントリポイント）でもコンパイルが通ります（CLの場合はリンカオプション`/entry:mainCRTStartup`が追加で必要）

```bash
gcc main.c -Wl,-subsystem,windows
```

リンカを直接利用する場合も、オプション`SUBSYSTEM`に値`WINDOWS`を指定することで、コマンドプロンプトの表示を抑制することができます。

## おまけ

### 実行の途中で非表示にする

プログラムの実行途中で非表示にしたい場合は、`FreeConsole();`関数を利用します。
例えば、プログラムを実行してから3秒後にコンソールを非表示にしたい場合は、以下のようなコードに変更することで実現できます。

```main.c.diff
  #include <windows.h>
-
- int WINAPI WinMain(
-     HINSTANCE hInstance,
-     HINSTANCE hPrevInstance,
-     LPSTR lpszCmdLine,
-     int nCmdShow
- ) {
+ int main() {
+     Sleep(3 * 1000);    // wait for 3 sec.
+     FreeConsole();      // free console.
      MessageBox(NULL, TEXT("Sample Text"), TEXT("Sample Window"), MB_OK);
      return 0;
  }
```

この場合は、リンカへ渡すオプションを指定する必要はありません。


## 参考

https://suzulang.com/vc-subsystem-windows-winmain/
https://docs.microsoft.com/ja-jp/cpp/build/walkthrough-using-msbuild-to-create-a-visual-cpp-project?view=vs-2019
https://stackoverflow.com/questions/33873735/correctly-set-visual-studio-linker-flag-subsystem-in-cmake
https://docs.microsoft.com/ja-jp/cpp/build/reference/cl-invokes-the-linker?view=vs-2019
https://docs.microsoft.com/ja-jp/cpp/build/reference/linker-options?view=vs-2019