Skip to content

Instantly share code, notes, and snippets.

@trueroad
Last active August 31, 2021 12:27
Show Gist options
  • Save trueroad/d309d1931100634c2cd1058a0620c663 to your computer and use it in GitHub Desktop.
Save trueroad/d309d1931100634c2cd1058a0620c663 to your computer and use it in GitHub Desktop.
Windows 用 Emacs (Cygwin/MinGW) の manifest を書き換えて UTF-8 対応を改善する実験

Windows 用 Emacs (Cygwin/MinGW) の UTF-8 対応改善実験

https://gist.github.com/trueroad/d309d1931100634c2cd1058a0620c663

Windows 用 Emacs 27.2 は、Unicode に対応していると言えばいるのですが、 一部の機能で Unicode の文字を使うとおかしくなるところがあります。 そこで、Emacs の実行ファイルをムリヤリ書き換えて Active Code Page を CP65001 (UTF-8) に変更することにより、 改善ができないか実験してみます。

これは、あくまでも実験です。 書き換えた結果、不具合が出るかもしれませんのでご注意ください。

A 系と W 系

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

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-systemcp932 ではなく nil になっているため、 これを UTF-8 だとして解釈して化けています。 変数 local-coding-systemcp932 に変えれば CP932 だと解釈するようになるので化けなくなります。 しかし、Cygwin レイヤを経由した文字列は UTF-8 なので、 こんどはそっちが化けてしまうことになり、 変えることができません。 ( A 系 API を W 系 API に変更するパッチを当てて化けないようにする試み もあります。)

なお、この例は MinGW Emacs だと変数 local-coding-systemcp932 になるので化けません (Cygwin レイヤも無いので UTF-8 を扱う必要も無く cp932 で問題ありません)。

manifest を書き換える

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/binemacs-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 で出てくるフォント名も化けなくなりました。

MinGW Emacs

GNU が配っている Windows 用バイナリなどは MinGW です。 MinGW Emacs の場合、ファイルシステムまわりは W 系 API を使い、 Unicode 文字によるファイル名でも扱えるようにしてあるようです。 しかし、プロセスなどではそうではないようで、 サブプロセス起動時に UTF-8 の引数を渡しても文字化けしてしまいます。 例えば、 (call-process "notepad" nil 0 nil "☃.txt") を評価すると、 メモ帳で文字化けしたファイル名のファイルを開こうとしてしまいます。

なお、この例は Cygwin Emacs なら書き換えなくても UTF-8 引数を扱えるため正しく動作します。

manifest を書き換える

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 が混在すると、 混乱して下手するとマズイ状況になるかもしれません。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity type="win32"/>
<application>
<windowsSettings>
<activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
</windowsSettings>
</application>
</assembly>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment