https://gist.github.com/trueroad/d309d1931100634c2cd1058a0620c663
Windows 用 Emacs 27.2 は、Unicode に対応していると言えばいるのですが、 一部の機能で Unicode の文字を使うとおかしくなるところがあります。 そこで、Emacs の実行ファイルをムリヤリ書き換えて Active Code Page を CP65001 (UTF-8) に変更することにより、 改善ができないか実験してみます。
これは、あくまでも実験です。 書き換えた結果、不具合が出るかもしれませんのでご注意ください。
Win32 API は俗に A 系と W 系と言われる 2 系統の API が存在するものがあります。
例えば MessageBox ()
という API は #define
されたエイリアスであり、
その実体は MessageBoxA ()
と MessageBoxW ()
のように、
名称の末尾が A または W の 2 種類どちらかを指しています。
A 系はマルチバイト文字列を、W 系は UTF-16 ワイド文字列を扱うという違いがあり、
A 系は使用する言語(日本語、英語など)によって扱う文字コードが異なります。
これまでは言語ごとにどのコードページ(文字コードなど)を使うか固定であり、
日本語の場合は CP932 と呼ばれる Shift_JIS
の変種を使うようになっていました。
これが Windows 10 1803 から「ワールドワイド言語で Unicode UTF-8 を使用」
という設定によって日本語であっても、
システム全体を CP932 ではなくて CP65001 (UTF-8)
にすることができるようになりました。
ただし、この設定は実験的なもので、アプリが対応していようがいまいが
すべて CP65001 にしてしまうため、おかしくなるアプリも多数あり、
なかなか使いにくいものでした。
しかし、Windows 10 1903 以降では、EXE ファイルごとに
どちらを使うか指定できる(CP932 と CP65001 が混在できる)ようになりました。
デフォルトの Active Code Page は CP932 のままであり、
A 系 API で扱う文字列は Shift_JIS の変種なのですが、
CP65001 に対応している EXE ファイルは、
対応している旨を manifest に記載しておくことによって、
そのプロセスの Active Code Page が CP65001 となり、
A 系 API で扱う文字列を UTF-8 にすることができます。
今回はバイナリ配布されている Emacs をそのままで再ビルドせずに ムリヤリ manifest を書き換えてみます。 そのため、書き換え用のツールが必要になります。
- Visual Studio に付属している
mt.exe
- Visual Studio Community エディションなどについています
Cygwin Emacs は MinGW の Emacs と違って、 ファイルシステムやプロセスの類は Cygwin レイヤが何とかしてくれるため、 特に何もしなくても最初から UTF-8 対応しています。 ですが、フォント関係など Cygwin レイヤを使わずに直接 Win32 API の A 系 API を叩いているところが一部あり、 そうしたところでは UTF-8 でなく CP932 になってしまって化けることがありました。
例えばフォント設定で、
(set-frame-font "MS Gothic-12" nil t)
のようにしていた場合、フレームパラメータの font
を得ようとして
(frame-parameter nil 'font)
を評価すると、
"-outline-\202l\202r \203S\203V\203b\203N-normal-normal-normal-mono-16-*-*-*-c-*-iso8859-1"
というようにフォント名が完全に化けた結果になってしまいます。
同様に
M-x describe-font
すると出てくるフォント名も化けます。
これは A 系 API でフォント名を取得しているため、
通常の日本語 Windows では CP932 の文字列が得られるのに対して、
変数 local-coding-system
が cp932
ではなく nil になっているため、
これを UTF-8 だとして解釈して化けています。
変数 local-coding-system
を cp932
に変えれば
CP932 だと解釈するようになるので化けなくなります。
しかし、Cygwin レイヤを経由した文字列は UTF-8 なので、
こんどはそっちが化けてしまうことになり、
変えることができません。
(
A 系 API を W 系 API に変更するパッチを当てて化けないようにする試み
もあります。)
なお、この例は MinGW Emacs だと変数 local-coding-system
が cp932
になるので化けません
(Cygwin レイヤも無いので UTF-8 を扱う必要も無く cp932
で問題ありません)。
Cygwin 64 bit のパッケージである emacs-w32 27.2-1 で実験してみます。
カレントディレクトリに /usr/bin
から
emacs-w32.exe
をコピーしておきます。
また、本リポジトリの utf-8.manifest
も用意しておきます。
そして、mt.exe
にパスが通っているコマンドプロンプトで以下を実行します。
mt -inputresource:emacs-w32.exe;#1 -out:emacs-w32.manifest
mt -manifest emacs-w32.manifest -manifest utf-8.manifest -out:merged-w32.manifest
copy emacs-w32.exe emacs-w32-utf8.exe
mt -manifest merged-w32.manifest -outputresource:emacs-w32-utf8.exe;#1
そうすると manifest が書き変わった emacs-w32-utf8.exe
ができます。
これを Cygwin の /usr/bin
へコピーしておきます。
また、以下のようにして /usr/bin
で emacs-w32.pdmp
のシンボリックリンク
emacs-w32-utf8.pdmp
を貼っておきます。
# cd /usr/bin
# ln -s emacs-w32.pdmp emacs-w32-utf8.pdmp
以上で書き換えた emacs-w32-utf8
ができました。
emacs-w32-utf8
を起動して、
(frame-parameter nil 'font)
を評価すると、
"-outline-MS ゴシック-normal-normal-normal-mono-16-*-*-*-c-*-iso8859-1"
となって、化けなくなりました。
M-x describe font
で出てくるフォント名も化けなくなりました。
GNU が配っている Windows 用バイナリなどは MinGW です。
MinGW Emacs の場合、ファイルシステムまわりは W 系 API を使い、
Unicode 文字によるファイル名でも扱えるようにしてあるようです。
しかし、プロセスなどではそうではないようで、
サブプロセス起動時に UTF-8 の引数を渡しても文字化けしてしまいます。
例えば、
(call-process "notepad" nil 0 nil "☃.txt")
を評価すると、
メモ帳で文字化けしたファイル名のファイルを開こうとしてしまいます。
なお、この例は Cygwin Emacs なら書き換えなくても UTF-8 引数を扱えるため正しく動作します。
GNU 公式バイナリの 27.2 (64 bit) で実験してみます。
カレントディレクトリに emacs.exe
をコピーしておきます。
また、本リポジトリの utf-8.manifest
も用意しておきます。
そして、mt.exe
にパスが通っているコマンドプロンプトで以下を実行します。
mt -inputresource:emacs.exe;#1 -out:emacs.manifest
mt -manifest emacs.manifest -manifest utf-8.manifest -out:merged.manifest
copy emacs.exe emacs-utf8.exe
mt -manifest merged.manifest -outputresource:emacs-utf8.exe;#1
そうすると manifest が書き変わった emacs-utf8.exe
ができます。
これを元の emacs.exe
があったフォルダにコピーしておきます。
以上で書き換えた emacs-utf8
ができました。
emacs-utf8
を起動して、
(call-process "notepad" nil 0 nil "☃.txt")
を評価すると、
文字化けせずに正しいファイル名のファイルを開こうとするようになりました。
なお、MinGW の場合は、msvcrt が CP65001 に対応していないため、 どこかで何かしらの不具合が出る可能性があります (例えばこれ、 とかこれ) 。 msvcrt の代わりに ucrt を使えば問題なくなると思いますが、 そのためにはライブラリ類も含めてすべて msvcrt ではなく ucrt を使うように変更してビルドする必要があり、 かなりの手間だと思います。 また、msvcrt を使う DLL と ucrt を使う DLL が混在すると、 混乱して下手するとマズイ状況になるかもしれません。