前置き
現在開発中のアプリではRustから windows クレートを使用して win32API を利用しています。
Rust の文字列型にはString
と&str
がありますがこれらは UTF-8 で処理されます。
一方、win32api は、言語によって異なる CodePage に対応した -A タイプの API と、UTF-16 に対応した -W タイプの API があります。
開発中のアプリでは -W タイプ(UTF-16)の API を使っていますが、 UTF-16 は Rust から扱うと少し不便なことが多いです。 そこで、プロセスの CodePage を UTF-8 にすることで実装を簡単にできるのでは、と思い、試してみました。
先に書いておきますが、ある程度はできたものの一部解決できてない問題があるため、結局 UTF-16 を使うように戻しました。
方法
Microsoft 公式にある下記記事を参考にします( fusion マニフェストのほう)。assemblyIdentity の name や version はアプリに合わせました。
Windows アプリで UTF-8 コード ページを使用する - Windows apps Microsoft Learn
開発環境への組み込み方
上記サイトにある通りマニフェストを適用するためには mt.exe を実行する必要があります(他の方法もあるとは思いますがこれが一番簡単そう)。
プログラムのデバッグ時は、デバッグビルド→マニフェスト適用→デバッグ実行という手順を踏むことになりますが、この手順は頻繁に行うため1操作でできるようにしたいところです。 最初はRustのビルドスクリプトで実現しようと思ったのですが、どうもビルド後のコマンド実行はできない(?)ようです。 それ用のツールもあるらしいのですが、今回は、開発環境として使っている VisualStduio Code のlaunch.jsonで設定できるタスク(tasks.json)で実現できそうなのでこれを使ってみました。
launch.json と tasks.json
私はデバッガとして Microsoft の C/C++ for Visual Studio Codeを使っています(なので CodeLLDB の場合と書き方が変わるかもしれません)。
launch.json に preLaunchTask としてタスクを登録します。タスクの内容は tasks.json に記載します。今回の場合は、デバッグビルドとマニフェスト適用がタスクとなります。 なお、preLaunchTask には複数のタスクは設定できないので、まとめるためのタスクをもう一つ追加で作り、それに dependsOn で複数のタスクを設定することで複数タスクの実行が可能になります(下記の参考リンク)。 また、デバッグビルド→ビルド後の実行ファイルにマニフェスト適用という流れなので、順番を指定する dependsOrder も設定する必要があります。
参考:Tasks in Visual Studio Code
launch.json
の内容
{
"version": "0.2.0",
"configurations": [
{
~ 略 ~
"preLaunchTask": "debug tasks",
~ 略 ~
}
],
}
tasks.json
の内容
{
"version": "2.0.0",
"tasks": [
{
"label": "debug build",
"type": "shell",
"command": "cargo",
"args": [
"build"
],
"problemMatcher": [
"$rustc"
]
},
{
"label": "set codepage",
"type": "shell",
"command": "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.22000.0\\x64\\mt.exe",
"args": [
"-manifest",
"${workspaceFolder}\\set_codepage_to_utf8.manifest",
"-outputresource:${workspaceFolder}\\target\\debug\\my_app.exe;#1"
],
},
{
"label": "debug tasks",
"dependsOrder": "sequence",
"dependsOn": [
"debug build",
"set codepage",
]
}
]
}
できたけど問題あり
上記を設定しデバッグ実行します。すると、マニフェストが適用されたプロセスが UTF-8対応され、-AタイプのAPIを使って日本語などの表示が正しく表示などが行えるように、、、なったと思ったのですが、なぜか文字化けしている表示が一部出てきました。
正しく表示されたものは、ウィンドウの名前(CreateWindowExA()
のlpWindowName
)やコンボボックスへの文字列追加(SendMessageA()
でCB_ADDSTRING
を送信)などです。
文字化けが起こってしまったものは、ステータスバーへのテキスト設定(SendMessageA()
でSB_SETTEXTA
を送信)やスタティックコントロールへの文字列描画(DrawTextA()
)などです。
原因については調べたのですが、あまりわかっていません。一応、下記ページの内容が関連しているのではないかなと思っています。
メッセージの自動翻訳 - Win32 apps Microsoft Learn.htm
SB_SETTEXTA
は下記ページに記載されていないメッセージなので対応されておらず、スタティックコントロールへの文字列描画(DrawTextA()
)は内部でどんなメッセージが使用されているか不明ですが、メインのウィンドウから呼ばれているためウィンドウクラスが違うのかなあと思っています。
その他(やってみてわかったことなど)
- win32api には -W タイプや -A タイプがなく UTF-16 を強制される API が多少ある( DirectX 関連とか)。
- UTF-8 を使えるようになっても、win32api では null 文字終端が必要なので、Rust の
String
と&str
がそのまま使えるわけではない。Cstring
を使う必要がある。 - win32apiを利用しない部分でもUTF-8対応になる。
println!()
などの出力もUTF-8対応にできる。
まとめ
ある程度実現できたものの残りの問題が解決できず今回は元に戻って UTF-16 を使うことにしました。
また、その他で書いたように、仮に問題が解決できたとしても UTF-16 を完全に使わないようにはできず、また、UTF-8 にしてもある程度の前処理が必要となるため、思ったほどの効果が得られないことがわかりました(UTF-16 を扱うための構造体を実装済みだったことも大きいです)。この取り組みはしばらく放置することになりそうです。