0. はじめに
現在の Windows においてはコマンドプロンプト cmd.exe
は単なるコマンドインタプリタに過ぎず,コンソール入出力の機能を担うのはコンソールホスト conhost.exe
に分離されています12。本記事のタイトルでは「コマンドプロンプト」と題していますが,実際に本記事で修正したのはウィンドウズターミナルであり,厳密には内部で使用しているコンソールホストのオープンソース版であるオープンコンソール OpenConsole.exe
に当たることを断っておきます。
1. 問題提起
ある日突然 Windows10 のコマンドプロンプトで全角罫線記号が半角表示されるようになりました3。残念ながら Windows 11 になっても改善されず,むしろウィンドウズターミナルでは症状が悪化したように思います。
一例を示しましょう。丸数字と全角罫線記号を用いたサンプルです。当たり前ですが,メモ帳では正しく表示されます。
コンソールホストを使用したコマンドプロンプトとオープンコンソールを使用したウィンドウズターミナルの結果を以下に示します。いずれもコマンドインタプリタとしてコマンドプロンプトを使用しているため,両者を区別するため「コンソールホスト」と「オープンコンソール」と呼ぶようにします。なお,いずれも固定幅フォントの BIZ UDゴシック を指定しています。
ここでコンソールホストとオープンコンソールの細かな差異に気づきます。コンソールホストの罫線およびオープンコンソールの丸数字について,グリフ自体は全角形状にも関わらず文字幅は半角分しかないため,右の文字がちょうど半分重なって見えます。一方,オープンコンソールの罫線はグリフそのものが半角形状になっており,右の文字と重なりません。
後者の差異については簡単に解決できます。オープンコンソールではフォントの持つグリフ情報を用いないで直接画面上に罫線図形を描画する機能があり,しかもデフォルトで有効になっているようです。おそらく,この機能は文字間隔や行間が広い場合でも上下左右の罫線が綺麗に繋がって見えるようにするためのものと推察されますが,本記事の目的のためには無効化する必要があります。
こうして組み込みグリフを無効化すると罫線の表現がコンソールホストと全く同じになることが分かります。組み込みグリフを有効化すると,罫線の描画にフォントのラスタライザを使わないのでアンチエイリアシングが働かず,線がシャープに描かれています。
この後,オープンコンソールを含めてウィンドウズターミナル自体をビルドすることになりますが,同様に組み込みグリフを無効化するのを忘れないで下さい。
2. ウィンドウズターミナルをビルドする
2.1 開発環境
筆者がビルドに成功したときの開発環境を示します。
- Windows 11 23H2
- Visual Studio Community 2022 17.14.14
- git version 2.50.1.windows.1
- Power Shell 7.5.2Manual configuration
なお,これ以外にも下記のコンポーネントが必要ですが,Visual Studio を立ち上げると必要に応じて自動的にインストールされるため,事前にインストールしておく必要はありません。
- Windows 11 SDK (10.0.22621.0)
- C++ によるデスクトップ開発
- ユニバーサル Windows プラットフォームツール
- C++ (v143) ユニバーサル Windows プラットフォームツール
- .NET Framework Targeting Pack
加えてアプリケーションを公開するときに以下の拡張機能が必要となりますが,これはビルドが終わった後でインストールしても構いません。
- Microsoft Visual Studio Installer Projects 2022
2.2 ソースコード一式
まず git ツールを使って最新のソースコードをダウンロードします。なお,筆者がビルドに成功したときのウィンドウズターミナルのバージョンは 1.23.12102.0 でした。
git clone https://github.com/microsoft/terminal.git --recursive
2.3 Unicode 文字情報のデータベース
下記アドレスから Unicode 文字情報のデータベースをダウンロードします。ZIP ファイルを展開すると ucd.nounihan.grouped.xml
というファイルが作られます。この XML ファイルを後で使用します。
https://www.unicode.org/Public/UCD/latest/ucdxml/ucd.nounihan.grouped.zip
2.4 文字幅データ生成ツールの修正
Unicode の文字幅についてのデータは terminal\src\types\CodepointWidthDetector.cpp
に記載されています。過去のバージョンでは手作業で書き換えられるようなシンプルなデータ構造でしたが4,対応する Unicode の文字数が増えたせいか(現在は1,114,112文字)まるでプロセッサのページング機構のような多層構造のテーブルとして実装されており,とても人が手作業で修正できるような代物ではありません。幸いなことにデータ生成用のツールが付随しているので,これを修正して使います。
ツールのソースコードは terminal\src\tools\GraphemeTableGen\Program.cs
にあります。中身を見ると,Unicode の 0x2500~0x259F のエリアはちょうど罫線記号が割り当てられており,デフォルトでは ambiguous という属性になっていますが,これを強制的に narrow に変更しているので,この部分をコメントアウトします。
// U+2500 to U+257F: Box Drawing block
// U+2580 to U+259F: Block Elements block
// By default, CharacterWidth.Ambiguous, but by convention .Narrow in terminals.
- Fill(0x2500, 0x259F, TrieValue(ClusterBreak.Other, CharacterWidth.Narrow));
+// Fill(0x2500, 0x259F, TrieValue(ClusterBreak.Other, CharacterWidth.Narrow));
ツールをビルドします。ソースコードとプロジェクトファイルのあるフォルダで dotnet
コマンドを用いてビルドします。Visual Studio 2022 をインストールしていれば .NET 8.0 が問題なく使えるはずです。
dotnet build -c release
ビルドしたツールを実行します。このとき先ほどダウンロードした Unicode 文字情報のデータベースのファイル ucd.nounihan.grouped.xml
を引数として指定します。なお標準出力に c++ のソースコードを出力するので適当なファイル(この例では table.cpp
)にリダイレクトします。
bin\release\net8.0\GraphemeTableGen.exe ucd.nounihan.grouped.xml > table.cpp
実行ファイルの生成ディレクトリは環境によって多少異なるかもしれません。
2.5 ソースコードの修正箇所
まず,下記ソースコードの朱記部分 #ff8095
を先ほど生成した table.cpp
と差し替えます。
// This is a great reference for the s_joinRules table:
// https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.html
-// Generated by GraphemeTableGen
-// on 2024-12-04T15:47:45Z, from Unicode 16.0.0, 9224 bytes
-// clang-format off
- ~ 中略 ~
-// clang-format on
// Decodes the next codepoint from the given UTF-16 string.
// Returns the start of the next codepoint. Assumes `it < end`.
対応する table.cpp
の内容 #8bdf4c
を示します。
+// Generated by GraphemeTableGen
+// on 2025-08-20T14:21:22Z, from Unicode 16.0.0, 9238 bytes
+// clang-format off
+ ~ 中略 ~
+// clang-format on
次に ambiguous 属性の文字幅の設定値を全角分に書き換えます。そもそも Unicode における文字幅の問題は ambiguous 属性の文字幅の取り扱いが東アジアでは全角分,それ以外の地域では半角分と変わってしまうことに起因しています567。おそらく日本語版 Windowws 10 の初期版では ambiguous 属性の文字幅の取り扱いは全角分だったものの,アップデートの途中で半角分に変更されてしまったものと推察されます。ここを全角分に書き換えてしまえば東アジア版としては問題なくなります。なお正しくはロケールに応じて切り替える,もしくは設定値を変更できるように作りたいところですが,筆者の実力不足のため実現できていません。
struct CodepointWidthDetector
{
/* 中略 */
- int _ambiguousWidth = 1;
+ int _ambiguousWidth = 2;
};
2.6 ターゲットの選定ほか環境設定
ウィンドウズターミナルのビルドについては既に多くの先行事例891011が知られていますが,備忘録代わりに筆者の例を残しておきます。
まず Visual Studio を使用して terminal
フォルダの OpenConsole.sln
を開きます。下記の警告メッセージが現れた場合は「構成マネージャーを開く」を押します。もしも現れなかった場合でも念のためメニューバーの「ビルド」から「構成マネージャー」を開いて構成とプラットフォームを確認しましょう。
ソリューション構成は「Release」プラットフォームは「x64」を選びました。ビルドおよび配置のチェックマークには触っていません。
足りないコンポーネントをインストールしなさいと注意されるので,メッセージに従って「インストール」します。
必要なコンポーネントを追加インストールします。
2.7 ビルド
ビルドを開始する前に念のため変更箇所を確認しましょう。今回手を加えたファイルは計3つで M (modified) のマークになっています。今回新規追加したものは計2つで A (append) のマークになっています。
「ソリューションのビルド」を行うと20分前後かかります。
2.8 アプリケーションの配置
アプリケーションの配置に失敗することがありました。参考文献121314を見ても原因が違うようで解決に至りませんでした。
DEP0700: アプリケーションの登録に失敗しました。
[0x80073CF6] AppxManifest.xml(30,27):
エラー 0x80070002: スプラッシュ画面画像 [taef.png] が見つからないため、パッケージ
WindowsTerminal.TestHost_aq7gd5554gxm8 をインストールまたは更新できません。
アプリケーションのスプラッシュ画面として使用できる画像がパッケージに含まれて
いることと、パッケージ マニフェストがパッケージ内の適切な場所、つまり
このスプラッシュ画面画像がある場所をポイントしていることを確認してください。
エラーメッセージをよく見ると TestHost_*
で失敗しているようなので,構成マネージャーを開いて TestHostApp
の配置のチェックを消したところ,エラーメッセージは出なくなりました。
ところが,この記事を書くためにもう一度最初からビルド手順をやり直そうとしたところ,構成マネージャーから TestHostApp
の欄自体が消えていたのです・・・謎です。
こうして配置が完了するとスタートメニューに「Terminal Dev」として現れます。本物のウィンドウズターミナルも上書きされずに残っているので引き続き使用可能です。
3. アプリケーションを公開する
3.1 公開用パッケージを作る
アプリケーションの配置に成功すると,下記のフォルダにウィンドウズターミナルの実行ファイル wtd.exe
が作られます。※本物は wt.exe
ですから共存可能なのですね。
c:\users\***\AppData\local\Microsoft\WindowsApps\wtd.exe
ところが上記の wtd.exe
は単なるリンクに過ぎず,実体はプロジェクトフォルダの末端に置かれています。
terminal\src\cascadia\CascadiaPackage\bin\x64\Release\AppX
このため,開発が一段落したのでストレージを解放しようと,うっかり terminal
フォルダを削除してしまうと動かなくなります。加えて,他の PC でも今回作成したウィンドウズターミナルを使いたいのですが,PC 毎にビルドして回るのも面倒です。このため,公開用のパッケージ(インストーラー)を作成してインストールすることにしました。
3.2 インストーラープロジェクトの導入
拡張機能マネージャーから「Microsoft Visual Studio Installer Projects 2022」をインストールします。
3.3 アプリパッケージの作成
ソリューションエクスプローラーより「CascadiaPackage」を選択し,右クリックして「公開」「アプリパッケージの作成」を選択すると下記ダイアログが現れるので,配布方法の選択で「サイドローディング」を選択します。
アプリパッケージへの署名を問われるので「はい、証明書を選択します」を選び,さらに「作成」を押します。
テスト証明書にサインします。
証明書の有効期限は作成からきっかり一年間みたいですね。このアプリパッケージに署名しますか?と問われるので,「はい、現在の証明書を使用します」を選択し,さらに「信頼する」を押します。
バージョンはオリジナルのウィンドウズターミナルに合わせました。今回は x64 のリリース版しかビルドしていないので「x64」の「Release」のみ選択します。
こうして「作成」ボタンを押すと,出力場所として指定したフォルダ直下の下記フォルダにパッケージ一式が置かれます。
CascadiaPackage_1.23.12102.0_x64_Test
このフォルダごとコピーするなり,アーカイブして持って行けます。
インストールするときは上記フォルダの中にある Install.ps1
を実行すれば OK です。こうしてインストールすると,実体はいわゆる Microsoft Storeからインストールされたアプリのフォルダ C:\Program Files\WindowsApps
に置かれます。なお,当該フォルダは隠し属性のため,デフォルトでは見えません。
4. 実行画面
ということで自作ターミナルを起動してみると,丸数字も罫線記号も見事に全角表示されるようになりました。タブを消せばまるで旧来のコマンドプロンプトのように見えます。タイトルバーの文字列をカタカナの「コマンドプロンプト」に変更するのも容易です。
$\color{red}{\Huge\textsf{MISSION COMPLETE!!}}$
5. 隘路事項
当初は下記のようにヘッダファイルを一行だけ書き換えればよいと思っていました。
- int _ambiguousWidth = 1;
+ int _ambiguousWidth = 2;
これで丸数字などは全角表示されるようになったものの,罫線記号のみ相変わらず半角表示のままでした。文字幅データは複雑なデータ構造をしているため,罫線記号の文字幅は直感的に良く分かりません。このため文字幅データを調べるテストコードを書きました。
#include <iostream>
#include <cstdint>
#include <bitset>
#include "table.cpp"
int main() {
for(int cp = 0; cp < 0x110000; cp++) {
int flag = ucdLookup((char32_t)cp);
std::cout << std::format("{:06X}", cp) << '\t' << std::bitset<8>(flag) << std::endl;
}
return 0;
}
うっかり std::format
を使ってしまったので C++20 以降でビルドする必要があります15。ただし,Visual C++ では /std:c++20
ではダメで /std:c++latest
を指定する必要がありました。
cl /EHsc /std:c++latest test.cpp
こうして文字幅データを出力してみました。丸数字と罫線記号の部分を抜粋して示します。flag の上位2ビットで文字幅を決定しており,01 のとき半角,10 のとき全角,11 のとき ambiguous という意味です。本来,罫線記号は ambiguous のはずですが,何故か半角になっていることに気づきました。原因を探っていくと,文字幅データ生成ツール(ツール自体は C# で作成)の中で罫線記号の文字幅を半角に上書している箇所を見つけました。ツール修正後は罫線記号も ambiguous になっています。
glyph | code point | flag | |
---|---|---|---|
修正前 | 修正後 | ||
① | 002460 | 11000000 | 11000000 |
② | 002461 | 11000000 | 11000000 |
③ | 002462 | 11000000 | 11000000 |
④ | 002463 | 11000000 | 11000000 |
─ | 002500 | 01000000 | 11000000 |
━ | 002501 | 01000000 | 11000000 |
│ | 002502 | 01000000 | 11000000 |
┃ | 002503 | 01000000 | 11000000 |
文字幅データ生成ツール(修正前)より,罫線記号の文字幅を半角に上書きしている箇所を示します。
// U+2500 to U+257F: Box Drawing block
// U+2580 to U+259F: Block Elements block
// By default, CharacterWidth.Ambiguous, but by convention .Narrow in terminals.
Fill(0x2500, 0x259F, TrieValue(ClusterBreak.Other, CharacterWidth.Narrow));
参考までに flag の値から文字幅を求めるコードは下記の通りです。
constexpr int ucdToCharacterWidth(const int val) noexcept
{
return val >> 6;
}
6. TO DO(やり残したこと)
- ambiguous の文字幅を強制的に 1 から 2 に書き換えたのは東アジア限定の乱暴な修正であり,Pull リクエストは受け付けて貰えないでしょう。せめて切り替えられるようにしたいところです。オープンコンソールはさまざまな設定(フォントの種類や大きさなど)をリアルタイムに変更できるようになっており,これを呼び出しているウィンドウズターミナル本体と何らかの通信手段を介して設定情報をやり取りしているはずなのです。ココに手を入れるのはシンドそうだなあ・・・
- ウィンドウズターミナルというか,オープンコンソールのテキスト描画は独自の Atlas エンジンが担っています。Direct 2D を用い,高速かつ高品質なテキストレンダリングを行っています。組み込みグリフはフォントのラスタライザを用いず,罫線記号を単なる図形として描画している訳ですが,この機能を無効化しないと罫線記号が全角表示できませんでした。要するに組み込みグリフは描画する文字幅を半角と決めつけているのです。ココにも手を付けようとしたのですが挫折しました。
7. 付録
サンプルデータです。Qiita のコードブロック自体は ambiguous の文字幅を半角にしています・・・
0123456789
①②③④⑤
┏━━┯━━┓
┃abcd│1234┃
┠──┼──│
┃あい│漢字┃
┗━━┷━━┛
-
fall creators update にしたら、全角罫線の幅が半分になった【Ver1709】- microsoft ↩
-
windows Terminalの問題「特定の絵文字が半角(サイズ1/4)」「罫線の縦線が削除」が解決した。異文字セレクタ付けただけ - Qiita ↩
-
Unicodeで半角全角を扱う Ambiguous(曖昧さ)とUncertainty(不確実性)の恐怖 - Qiita ↩
-
Windows Terminal Build(github.com/Microsoft/Terminal) - Qiita ↩
-
2019/05/14版_Windows Terminal をビルドしてみる(VS2019)・設定とキーバインド変更 - hatenablog ↩