VSCode + MinGW-w64 via MSYS2 で WIN32API の Unicode ビルド
本記事は C++ Advent Calendar 2023 の6日目の記事だ。
5日目は @uyamae 氏の C++ をclang で解析するときに情報をvcxproj から取得する方法 だった。
Windows 上でサクッと C++ ビルド環境を用意したい
さて、Win32API のコードをビルドしたいけど Visual Studio を入れるまでもない場合、 MinGW の GCC C++ コンパイラ (g++) と Visual Studio Code (VSCode) の組み合わせでも割と快適にビルドできる。
しかし、 VSCode のドキュメントの手順 そのままでは マルチバイト・Unicode周りでハマったので、非ASCII 圏で問題ないように設定する方法を紹介する。
先に解決方法だけ示しておくと、
-
tasks.json
の g++ ビルド時の引数を書き加えてやりつつ"command": "C:\\msys64\\ucrt64\\bin\\g++.exe", "args": [ "-fdiagnostics-color=always", + "-DUNICODE", + "-D_UNICODE", + "-municode", + "-std=c++23", "-g", "${file}", "-o", "${fileDirname}\\${fileBasenameNoExtension}.exe" ],
-
C/C++ extension for VS Code の設定から、 以下の IntelliSense 設定も変えればよい
-
定義:
_DEBUG UNICODE _UNICODE
-
C 標準: c23
-
C++ 標準: C++23
-
VSCode + MSYS2 上の MinGW-w64 の組み合わせでの設定
順を追って説明しよう。
まず、前述の VSCode のドキュメントの手順 で環境を構築していく。
具体的には以下の通り。
-
Visual Studio Code をインストール
-
vscode に C/C++ extension for VS Code 拡張をインストール
-
MSYS2 の最新リリース から最新のインストーラー (
msys2-x86_64-yyyymmdd.exe
) をインストール -
MSYS2 MINGW64 の方を立ち上げて以下のコマンドを実行し、ツールチェインをインストール
pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain
- boost 使いたければ、
mingw-w64-ucrt-x86_64-boost
あたりもインストールしておこう
- boost 使いたければ、
-
OS の環境変数
PATH
にC:\msys64\ucrt64\bin
を追加 -
VSCode を開きなおして適当なフォルダを開き以下のような
hellowin32api.cpp
を作成#include
#include int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR pCmdLine, int showCmd) { MessageBox(NULL, _T("こんにちは、世界."), _T("メッセージ"), MB_OK); return 0; } -
エディタの右上にある Play Debug ボタンのドロップダウンから
Run C/C++ File
を選択し、検出されたコンパイラ一覧から C/C++: g++.exe build and debug active file を選択する。
実行した結果を見てみると…
んん~?
文字化けしちゃうね。
文字化けしないようにワイド文字列(Unicode)でビルド
IntelliSense 上では LPTSTR
が wchar_t*
となっているのに、実際のビルド結果は char*
となっている。
UNICODE
が定義されていないので、そりゃそうか。
MBCS 扱いでビルドされているのに、ソースコードが UTF-8 になっているので化けているようだ。
今時「マルチバイト文字」即ち MBCS でのビルドはないので、「ワイド文字」即ち Unicode を使うべく、 windows.h
用の UNICODE
と tchar.h
用の _UNICODE
をそれぞれ定義する。
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>
...
今度はビルドエラー
C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/13.2.0/../../../../lib/libmingw32.a(lib64_libmingw32_a-crtexewin.o): in function `main':
C:/M/B/src/mingw-w64/mingw-w64-crt/crt/crtexewin.c:67:(.text.startup+0xbd): undefined reference to `WinMain'
collect2.exe: error: ld returned 1 exit status
WinMain
が定義されていないと出る。
_tWinMain
マクロで定義される wWinMain
がエントリーポイントになる想定だったが、ダメだったようだ。
MinGW-w64 の g++ の場合、エントリーポイントを unicode 対応させるには -municode
のオプションの定義が必要らしい。
ビルド時の引数設定を書き換えるためには、 tasks.json
を書き換えてやる。 ついでに UNICODE
と _UNICODE
の定義や、 C++23 を使う設定も書き加えてやろう。(std::format とか使いたいじゃん?)
"command": "C:\\msys64\\ucrt64\\bin\\g++.exe",
"args": [
"-fdiagnostics-color=always",
+ "-DUNICODE",
+ "-D_UNICODE",
+ "-municode",
+ "-std=c++23",
"-g",
"${file}",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
],
IntelliSense とビルドの設定のズレも気になるので、 コマンドパレット (Ctrl+Shift+P
) から C/C++: 構成の編集 (UI)
を選択して開いた C/C++ 拡張の設定 から、以下のように設定しておく。
-
定義:
_DEBUG UNICODE _UNICODE
-
C 標準: c23
-
C++ 標準: gnu++23
ここまで設定して改めてビルドと実行してみると、
無事意図通り出力できた。
ちなみに C++ ではあまり一般的ではないが、先日の C# の場合と同様 .editorconfig
を使ってブロック手前に改行コードが存在する整形方式(オールマンスタイル)から、改行コードが無い整形方式(K&Rスタイル)に変更できる。
C++ EditorConfg の書式に則って .editorconfig
ファイルを作成し、以下のように記載しよう。
[*.{c++,cc,cpp,cxx,h,h++,hh,hpp,hxx,inl,ipp,tlh,tli}]
cpp_new_line_before_open_brace_namespace = same_line
cpp_new_line_before_open_brace_type = same_line
cpp_new_line_before_open_brace_function = same_line
cpp_new_line_before_open_brace_block = same_line
cpp_new_line_before_open_brace_lambda = same_line
cpp_new_line_scope_braces_on_separate_lines = false
cpp_new_line_before_catch = false
cpp_new_line_before_else = false
cpp_new_line_before_while_in_do_while = false
おまけ:強い心で MBCS を排除しよう
今時非 Unicode のビルドなんて一切気にする必要はないぜ、という強い心を持つなら、 tchar.h
のインクルードも _UNICODE
の定義もいらず、以下のように書いてしまってよい。
#define UNICODE
#include <windows.h>
int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR pCmdLine, int showCmd)
{
MessageBox(NULL, L"こんにちは、世界.", L"メッセージ", MB_OK);
return 0;
}
コンパイラオプションで定義しているので #define UNICODE
は二重定義なのだけれど、「このソースは MBCS を排除している」という気持ちを示すために各ソースコードの先頭に書いて示しておくといいんじゃないかな。