Windows10 の 1903 以降で、Windows API の -A 系列で、UTF-8 文字列を使えるようになったと記載を見かけたので試してみました。結論としては、「確かに使える!」です。条件が許せば、 UTF-8 と UTF-16 を考慮したプログラムで、Shift-JIS 文字を忘れることができるかもしれません。
設定方法
Visual Studio 2019 で、 C++ の Win32 プロジェクトを用いる場合で説明します。
必要になるのはマニフェストファイルで、以下の内容のテキストファイルを作成します。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity type="win32" name="..." version="6.0.0.0"/>
<application>
<windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
</assembly>
このファイルをプロジェクトに追加して、プロジェクトのプロパティを変更します。マニフェストツール / 入出力 を開いて、追加のマニフェストファイルに追加したファイルを設定します。
文字セットの設定は UNICODE、ANSI どちらでもうまく動作しました。ただし使用しているAPI セットはきちんと合わせることが必要です。DefWindowProc, GetMessage, DispatchMessageA あたりで変に混在状態だと以下のタイトル文字化けという症状が出ました。注意しましょう。
動作確認
CreateWindowA 関数を使ってみたところ、これでうまくタイトル文字列が表示されます!
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance;
auto u8str = u8"日本語UTF8タイトル";
HWND hWnd = CreateWindowA(
szWindowClass,
u8str,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
nullptr, nullptr, hInstance, nullptr);
文字列の変換で、CP_ACP を使っている場合では、入力文字を Shift-JIS という解釈から UTF-8 解釈というようにモードが変わります。そのため以下のコードで UTF-8 から UTF-16 への変換ができます。
const auto stru8 = u8"日本語";
len = MultiByteToWideChar(CP_ACP, 0, stru8, -1, nullptr, 0);
wbuf.resize(len);
MultiByteToWideChar(CP_ACP, 0, stru8, -1, wbuf.data(), len);
しかし、 OutputDebugStringA でデバッグ出力に UTF-8 文字列を出力することはできませんでした。先ほどの、“日本語UTF8タイトル” という文字列をそのまま呼び出した結果が以下です。
ここでも UTF-8 文字列で受け取って表示してくれるととてもよかっただけに残念なポイントでした。
判定ができるか?
以下の GetACP 関数を用いることで一応の判定は可能かもしれません。この関数は現在のANSI コードページを返却します。上記で UTF-8 のコードページを設定している場合 65001 が返ってきます。なお、設定がない場合には日本語の Windows 環境では 932 が返ってきます。
UINT value = GetACP();
もっと簡単な設定方法
マニフェストファイルを追加のマニフェストファイルとして処理しなくても、リソースファイルに追加する方法でも可能なことが分かりました。ファイルを作成するところは同様で、単純にプロジェクトに追加するだけで済みます。
コメント
FAT32(VFAT)フォーマットのUSBメモリ内の日本語名のファイルを扱おうとすると、
最後の最後、実際にファイルにアクセスする段階で、
cp932とutf-8(cp65001)でのファイルパスに齟齬が出て、
ファイルが見つからないというエラーになりますね。
コレ、意外と困りものです。
コメントありがとうございます。
当時の環境とは異なっているのですが、ファイル名に関して手元で試してみました。
Windows11 の環境となってしまいますが、記事中の内容に加えて CreateFileA の API で UTF-8 文字列を与えてファイルを開いて読み取りを試行しました。
NTFSフォーマットされたドライブ上の日本語のファイル名、FAT32フォーマットされているUSBメモリ上の日本語のファイル名、というパターンで試してみましたが、正常にファイルを開いて読み取りまでできました。もしかすると OS のバージョン等にも依存するのですかね。
わざわざ追試ありがとうございます。
SystemLocaleがANSIのままのWindows 10上で再現します。
コンソール等に表示されるパスまでは文字化けしない(当たり前ですね)のですが、manifestでActiveCodePageをutf-8にしたアプリからみると、きちんと読み取れていない、といった感じです。
事の発端は、mpvというメディアプレーヤーで、以前のUSBメモリ上のメディアファイルが再生できないということでした。
変更履歴をたどり、組み込まれたmanifestからActiveCodePageの項目を削除すると問題が生じませんでした。
ちなみに、このアプリはもとからW系のAPIを使っているので、ActiveCodePageをutf-8にする設定自体は特に必要なものではなく、lua等の内部スクリプトのためになされたようです。
utf-8に変更したWindows 10/11で新たに書き込んだUSBメモリだと、コードページの齟齬がなく、問題が生じないということなんでしょうかね。
直接の関係はなさげなトピックでお騒がせしました。