従来のコンソール API と仮想端末シーケンス
従来の Windows コンソール API を仮想ターミナル シーケンスに置き換えることをお勧めします。 この記事では、2 つの違いについて概要を説明し、推奨の理由について説明します。
定義
従来の Windows コンソール API サーフェスは、kernel32.dll
上の一連の C 言語関数型インターフェイスとして定義され、名前に "コンソール" が含まれています。
仮想ターミナル シーケンスは、標準入力ストリームと標準出力ストリームに埋め込まれたコマンドから成る言語と定義されています。 通常の出力可能なテキストとインターリーブされたコマンドを通知するために、仮想ターミナル シーケンスには出力不可能なエスケープ文字が使用されます。
履歴
Windows コンソールには、クライアント コマンドライン アプリケーションから出力表示バッファーとユーザー入力バッファーの両方を操作するための幅広い API サーフェスが用意されています。 ただし、他の Windows 以外のプラットフォームでは、コマンドライン環境にこうした API 駆動型アプローチは用意されておらず、その代わりに標準入力ストリームと標準出力ストリームに埋め込まれた仮想ターミナル シーケンスを使用することが選択されています (しばらくの間、Microsoft でも、DOS および Windows の初期のエディションで ANSI.SYS というドライバーを介してこの動作をサポートしていました)。
これに対し、(さまざまな言語の) 仮想ターミナル シーケンスでは、他のすべてのプラットフォームに対してコマンドライン環境操作を利用できます。 これらのシーケンスは、ECMA Standard や、多くのベンダーによる一連の拡張機能 (古くは Digital Equipment Corporation や Tektronix の各ターミナルから、xterm などのより近代的で一般的なソフトウェア ターミナルまで) に基づいています。 多くの拡張機能が仮想ターミナル シーケンス ドメイン内に存在します。一部のシーケンスは他よりも広くサポートされていますが、世界的に、これがコマンドライン エクスペリエンスのコマンド言語として標準化されており、既知のサブセットはほぼすべてのターミナルおよびコマンドライン クライアント アプリケーションによってサポートされていると言っても過言ではありません。
クロスプラットフォームのサポート
仮想ターミナル シーケンスは複数のプラットフォームでネイティブにサポートされているため、Windows を除くオペレーティング システムのバージョンやバリエーション間でターミナル アプリケーションとコマンドライン ユーティリティを簡単に移植できます。
これに対して、Windows コンソール API は Windows でのみサポートされています。 プラットフォーム間でコマンドライン ユーティリティを移植する場合は、Windows と仮想ターミナルの間 (またはその逆) に広範なアダプターまたは変換ライブラリを記述する必要があります。
リモート アクセス
仮想ターミナル シーケンスには、リモート アクセスについて大きな利点があります。 標準のリモート コマンドライン接続を設定するために必要なものを介して、転送したり、リモート プロシージャ コールを実行したりするための追加の作業は必要ありません。 パイプ、ソケット、ファイル、シリアル ポート、またはその他のデバイスを介して送信および受信のトランスポート チャネル (または 1 つの双方向チャネル) を接続するだけで、これらのシーケンスを使用するアプリケーションに必要なすべての情報をリモート ホストに完全に伝達できます。
これに対し、Windows コンソール API にはローカル マシン上でのみアクセスできます。それらすべてをリモート化するには、単純なチャネルだけでなく、リモート呼び出しとトランスポート インターフェイス レイヤー全体を構築する必要があります。
懸念事項の分離
一部の Windows コンソール API には、入力バッファーと出力バッファーへの低レベルのアクセス、または対話型のコマンドライン用の便利な関数が用意されています。 たとえば、コマンドライン クライアント アプリケーション内ではなく、コンソール サブシステムとホスト環境内でプログラムされたエイリアスとコマンド履歴などです。
これに対し、他のプラットフォームでは、アプリケーションの現在の状態のメモリと便利な機能は、コマンドライン ユーティリティまたはシェル自体の役割にされています。
コンソール ホストと API でこの役割を処理するという Windows コンソールの方法であれば、これらの機能を備えたコマンドライン アプリケーションをすばやく簡単に作成できます。また、描画状態を記憶したり、便利な機能を編集したりする必要がありません。 ただし、実装と可用性に違いがあるため、プラットフォーム、バージョン、またはシナリオ間でこれらのアクティビティをリモートで接続することはほぼ不可能になります。 このような役割の処理方法なので、このような Windows コマンドライン アプリケーションの最終的な対話型エクスペリエンスは、コンソール ホストの実装、優先順位、およびリリース サイクルに完全に依存することになります。
たとえば、構文の強調表示や複雑な選択などの高度な行編集機能は、コマンドライン アプリケーションによって編集に関する問題が処理される場合にのみ使用できます。 コンソールには、クライアント アプリケーションのように、こうしたシナリオを幅広い方法で完全に理解するために十分なコンテキストがありません。
これに対し、他のプラットフォームでは、仮想ターミナル シーケンスを使用してこれらのアクティビティを処理し、readline や ncurses などの再利用可能なクライアント側ライブラリを介して仮想ターミナル通信自体を処理しています。 最後のターミナルは、その双方向通信チャネルを介して情報を表示し、入力を受信することのみを担当します。
間違った方向の動詞
Windows コンソールには、入力ストリームと出力ストリームに対して、自然な方向とは逆方向に実行できるアクションがいくつかあります。 これを使用すると、Windows コマンドライン アプリケーション側でバッファーを管理する必要がなくなります。 また、Windows コマンドライン アプリで、ユーザーに代わって入力をシミュレートまたは挿入したり、書き込まれた内容の履歴の一部を読み戻したりするなど、高度な操作を実行できます。
これにより、1 台のマシン上の特定のユーザー コンテキストで動作する Windows アプリケーションが強化されます。それと同時に、特定のシナリオで使用される場合に、セキュリティと特権レベルまたはドメインを横断するベクトルも用意されています。 このようなシナリオには、同じマシン上のコンテキスト間、または別のマシンまたは環境へのコンテキスト間の操作などがあります。
仮想ターミナル シーケンスを使用する他のプラットフォームでは、このアクティビティは許可されていません。 従来の Windows コンソールから仮想ターミナル シーケンスに移行することをお勧めする目的は、相互運用性とセキュリティの両方の理由により、この戦略に収束させるためです。
直接ウィンドウ アクセス
Windows コンソール API サーフェスからは、ホスト ウィンドウに正確なウィンドウ ハンドルが提供されます。 そのため、コマンドライン ユーティリティから、ウィンドウ ハンドルに許可されているさまざまな Win32 API を利用して、高度なウィンドウ操作を実行できます。 これらの Win32 API を使用して、ウィンドウの状態、フレーム、アイコン、またはウィンドウに関するその他のプロパティを操作できます。
これに対し、仮想ターミナル シーケンスを使用する他のプラットフォームでは、ウィンドウに対して実行できるコマンドの数は少数です。 これらのコマンドを使用してウィンドウ サイズや表示されるタイトルの変更などを実行できますが、ストリームのその他の部分と同じ帯域で同じ制御下で実行される必要があります。
Windows が進化するにつれて、ウィンドウ ハンドルのセキュリティ制御と制限が強化されました。 さらに、特定のユーザー インターフェイス要素に対するアプリケーションによるアドレス指定可能なウィンドウ ハンドルの性質と存在は、特にデバイスのフォーム ファクターとプラットフォームのサポートの増加に伴って進化しています。 そのため、プラットフォームとエクスペリエンスが進化するにつれて、ウィンドウからコマンドライン アプリケーションへの直接アクセスは脆弱になります。
Unicode
UTF-8 は、移植性、ストレージ サイズ、および処理時間がちょうどよいバランスなので、ほとんどすべての最新プラットフォームで Unicode データのエンコードとして受け入れられています。 ただし、従来、Windows では Unicode データのプライマリ エンコードとして UTF-16 が選択されていました。 Windows では UTF-8 のサポートが増えています。また、これらの Unicode 形式を使用しても、他のエンコードを使用できなくなることはありません。
Windows コンソール プラットフォームは、既存のすべてのコード ページとエンコードをサポートしており、今後も引き続きサポートする予定です。 Windows バージョン間の互換性を最大化するには、UTF-16 を使用してください。また、必要に応じて UTF-8 を使用してアルゴリズム変換を実行してください。 コンソール システムでは、UTF-8 のサポートが増えています。
コンソールでの UTF-16 のサポートは、すべてのコンソール API の W バリアントを介して追加の構成なしで使用できます。また、他の Microsoft および Windows プラットフォームの機能および製品の wchar_t
および W バリアントとの通信を介して、UTF-16 を既に利用しているアプリケーションにとっては、より可能性の高い選択肢です。
コンソールでの UTF-8 のサポートは、SetConsoleOutputCP および SetConsoleCP メソッドを使用してコードページを 65001
または CP_UTF8
に設定した後、コンソール ハンドルに対してコンソール API の A バリアントを介して使用できます。 事前にコード ページを設定する必要があるのは、コントロール パネルの [地域] セクションの非 Unicode アプリケーションの設定で、[Use Unicode UTF-8 for worldwide language support]\(世界各国の言語をサポートするために Unicode UTF-8 を使用する\) を選択していない場合のみです。
Note
現在のところ、UTF-8 は、WriteConsole および WriteFile メソッドを使用した標準出力ストリームで完全にサポートされています。 入力ストリームのサポートは入力モードによって異なり、徐々に改善される予定です。 特に、入力の既定の "cooked" モードでは、UTF-8 がまだ完全にはサポートされていません。 この作業の現在の状態は、GitHub の microsoft/terminal#7777 にあります。 回避策は、未解決の問題が解決されるまで、ReadConsoleW または ReadConsoleInputW を介して入力を読み取るために、アルゴリズム変換可能な UTF-16 を使用することです。
推奨事項
Windows に対するすべての新規および継続的な開発では、ターミナルとの対話方法として仮想ターミナル シーケンスが推奨されます。 これにより、Windows コマンドライン クライアント アプリケーションは、他のすべてのプラットフォームでのアプリケーション プログラミングのスタイルに統合されます。
Windows コンソール API を使用する場合の例外
初期環境を確立するには、Windows コンソール API の限られたサブセットが依然として必要です。 Windows プラットフォームは、プロセス、シグナル、デバイス、およびエンコードの処理の点で他のプラットフォームと異なることがまだあります。
プロセスへの標準ハンドルは、引き続き GetStdHandle および SetStdHandle を使用して制御されます。
ハンドル上で、仮想ターミナル シーケンスのサポートをオプトインするコンソール モードの構成は、GetConsoleMode および SetConsoleMode を使用して処理されます。
コード ページまたは UTF-8 サポートの宣言は、SetConsoleOutputCP および SetConsoleCP メソッドを使用して実行されます。
AllocConsole、AttachConsole、および FreeConsole を使用してコンソール デバイス セッションに参加または離脱するには、ある程度の全体的なプロセス管理が必要になる可能性があります。
シグナルとシグナル処理は、引き続き SetConsoleCtrlHandler、HandlerRoutine、および GenerateConsoleCtrlEvent を使用して実行されます。
コンソール デバイスのハンドルとの通信は、WriteConsole および ReadConsole を使用して実行できます。 これらは、次の形式のプログラミング言語ランタイムを介して利用することもできます: - C ランタイム (CRT): ストリーム I/O (printf、scanf、putc、getc など)、または他のレベルの I/O 機能。 - C++ 標準ライブラリ (STL): cout や cin などの iostream。 - .NET ランタイム: Console.WriteLine などの System.Console。
ウィンドウ サイズの変更を認識する必要があるアプリケーションの場合、キー イベントとインターリーブされたウィンドウを受信するには、ReadConsoleInput を使用する必要があります。ReadConsole 単独でそれらが破棄されるからです。
列、グリッドの描画、またはディスプレイの塗りつぶしを試行するアプリケーションの場合、ウィンドウ サイズの検索は GetConsoleScreenBufferInfo を使用して実行する必要があります。 疑似コンソール セッションでは、ウィンドウとバッファーのサイズが一致します。
今後の計画と擬似コンソール
プラットフォームから Windows コンソール API が削除される予定はありません。
それどころか、Windows コンソール ホストでは、既存の Windows コマンドライン アプリケーション呼び出しを仮想ターミナル シーケンスに変換し、リモートまたはプラットフォーム間で別のホスト環境に転送する疑似コンソール テクノロジを利用できます。
この変換は完全ではありません。 Windows の場合にユーザーに表示される内容のシミュレートされた環境を保守するには、コンソール ホスト ウィンドウが必要です。 これで、このシミュレートされた環境のレプリカが疑似コンソール ホストに投影されます。 すべての Windows コンソール API 呼び出しは、従来のコマンドライン クライアント アプリケーションのニーズに対応するために、シミュレートされた環境内で操作されします。 最終的なホストには、影響のみが反映されます。
そのため、プラットフォーム間の完全な互換性と、Windows とおよびその他の場所でのすべての新機能とシナリオの完全なサポートを望むコマンドライン アプリケーションの場合、仮想ターミナル シーケンスに移行し、すべてのプラットフォームに合わせせコマンドライン アプリケーションのアーキテクチャを調整することをお勧めします。
コマンドライン アプリケーションのこの Windows の移行の詳細については、エコシステムのロードマップに関する記事を参照してください。