[Win32 API] GUI アプリケーションでコンソール出力する & 文字コードの注意点

GUI アプリケーションのデバッグ時等にコンソール出力する方法です。

`AllocConsole()` 関数を使用する方法では**文字コードの注意**が必要です (※後述) 。

※本記事では文字列をすべて Unicode で扱うことにします。
※本記事では WSL 上の MinGW-w64 でコンパイルすることを想定しています。

# 1. 方法1: CUI (コンソール) アプリケーションとしてコンパイルする

GUI アプリケーションであっても CUI アプリケーションとしてコンパイルし、コンソールから実行するとそのコンソールに文字列を入出力できます。

※以下の例では標準出力のみ。

```C++:main.cpp
#include <windows.h>
#include <iostream>
#include <fcntl.h>

// 
int AppMain() {

	// 出力の文字コード指定
	_setmode(fileno(stdout), _O_U8TEXT);

	// 
	const int id = MessageBoxW(NULL, L"Test", L"Info", MB_ICONINFORMATION | MB_YESNO);

	// 
	std::wcout << L"MessageBox: " << id << std::endl;

	_wsystem(L"PAUSE");

	return 0;

}

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
	return AppMain();
}
```

```bash:build.sh
#!/bin/bash

# g++ ver.5.1 以降を想定
x86_64-w64-mingw32-g++ -Wall -std=c++14 \
	-finput-charset=UTF-8 -fexec-charset=CP932 \
	-municode \
	-static-libgcc -static-libstdc++ \
	-o main.exe \
	main.cpp
```

1. ソースコードは UTF-8
2. 実行ファイル内の文字列は CP932 (Shift_JIS)
3. 標準出力は UTF-8
4. Windows のコンソール上では Unicode が (特定の条件下で) 自動で変換されるため、文字化けしない

参考「[_setmode | Microsoft Docs](https://docs.microsoft.com/ja-jp/cpp/c-runtime-library/reference/setmode?view=msvc-160)」
参考「[C言語のワイド文字入出力 — Windows Console 編 | 雑記帳](https://blog.miz-ar.info/2017/01/wide-stdio-on-windows-console/)」

# 2. 方法2: 別途コンソール画面を生成する

`AllocConsole()` 関数を使用することでコンソール画面を表示できます。

現在アクティブなコンソール出力デバイスは `CONOUT$` で表され、`_wfreopen_s()` 関数を用いて `stdout` の出力先の再割り当てをすることで標準出力として使えるようになります。

注意点として、`stdout` の出力デバイス等の再割り当てを行う場合、**出力モードを** `w` **でなく** `w+` **にしないと** Windows のコンソール上で Unicode 文字列が自動変換されず、文字化けします。

※以下の例では標準出力のみ。

```C++:main.cpp
#include <windows.h>
#include <iostream>
#include <fcntl.h>
#include <cstdlib>

// 
int AppMain() {

	// コンソール画面生成
	AllocConsole();

	FILE *stream;

	_wfreopen_s(&stream, L"CONOUT$", L"w+", stdout);

	// 出力の文字コード指定
	_setmode(fileno(stdout), _O_U8TEXT);

	// 
	const int id = MessageBoxW(NULL, L"Test", L"Info", MB_ICONINFORMATION | MB_YESNO);

	// 
	std::wcout << L"テスト MessageBox: " << id << std::endl;

	_wsystem(L"PAUSE");

	return 0;

}

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
	return AppMain();
}
```

```bash:build.sh
#!/bin/bash

# g++ ver.5.1 以降を想定
x86_64-w64-mingw32-g++ -Wall -std=c++14 \
	-finput-charset=UTF-8 -fexec-charset=CP932 \
	-municode \
	-Wl,-subsystem,windows \
	-static-libgcc -static-libstdc++ \
	-o main.exe \
	main.cpp
```

参考「[AllocConsole function - Windows Console | Microsoft Docs](https://docs.microsoft.com/en-us/windows/console/allocconsole)」
参考「[freopen_s, _wfreopen_s | Microsoft Docs](https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/freopen-s-wfreopen-s?view=msvc-160)」
参考「[Windows GUIプログラムでデバッグ情報をコンソールを表示させる | プログラミングテクニック集キヤミー](https://cammy.co.jp/technical/2016/04/29/windows-gui%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%A7%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E6%83%85%E5%A0%B1%E3%82%92%E3%82%B3%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%AB%E3%82%92%E8%A1%A8%E7%A4%BA/)」(※出力モードが `w` になっていますが、`w+` にすべきです)
参考「[C言語のワイド文字入出力 — Windows Console 編 | 雑記帳](https://blog.miz-ar.info/2017/01/wide-stdio-on-windows-console/)」