歡迎來(lái)閱讀第三篇Windows 命令行系列文章。在這篇,我們開(kāi)始深入Windows 控制臺(tái)和命令行,它是什么,你可以用它可以做什么……和它不能做什么!
系列文章:
命令行產(chǎn)生的背景 Windows 命令行的發(fā)展 深入Windows命令行(本篇)在開(kāi)始開(kāi)發(fā)Windows NT操作系統(tǒng)的那時(shí)候,大概是1989年,那時(shí)候還沒(méi)有GUI(圖形化用戶界面),也沒(méi)有桌面操作系統(tǒng),只有最原始的全屏的命令行界面,類似于MS-DOS的可視化界面越來(lái)越重要!Windows GUI 開(kāi)始開(kāi)發(fā)的時(shí)候是在開(kāi)發(fā)團(tuán)隊(duì)需要開(kāi)發(fā)一個(gè)基于控制臺(tái)的應(yīng)用的背景下誕生的!Windows 控制臺(tái)是第一個(gè)Windows NT的GUI應(yīng)用,并且可以保證兼容運(yùn)行繼續(xù)使用已有的Windows應(yīng)用。
Windows 控制臺(tái)最初的代碼到現(xiàn)在(2018年)已經(jīng)有30年的歷史……古老的東西,事實(shí)上,今天還有很多開(kāi)發(fā)者在使用它!
控制臺(tái)程序能做什么?
就像之前的文章說(shuō)的,終端的工作其實(shí)很簡(jiǎn)單:
處理用戶輸入:可以支持的輸入設(shè)備包括鍵盤、鼠標(biāo)、觸摸板、筆等等。轉(zhuǎn)換輸入的數(shù)據(jù)到中間字符的或者ANSI/VT編碼格式發(fā)送字符數(shù)據(jù)到已連接的應(yīng)用程序或設(shè)備處理應(yīng)用程序輸出:允許從已連接的應(yīng)用程序輸出文本更新屏幕上面的顯示,基于應(yīng)用程序接受顯示(比如顯示文本,移動(dòng)光標(biāo),設(shè)置字體顏色等)系統(tǒng)協(xié)調(diào)處理:運(yùn)行處理作業(yè)請(qǐng)求管理設(shè)備和資源支持調(diào)整窗口尺寸、最大化窗口、最小化窗口等中斷請(qǐng)求或當(dāng)信道關(guān)閉或結(jié)束處理但是,Windows 控制臺(tái)能做的事情有些不同:
深入Windows控制臺(tái)內(nèi)部
Windows控制臺(tái)是一種傳統(tǒng)的Win32可執(zhí)行文件,雖然它最初是用“C”編寫的,但隨著團(tuán)隊(duì)現(xiàn)代化和模塊化控制臺(tái)的代碼庫(kù),大部分代碼都已正在遷移到現(xiàn)代C++了。
對(duì)于那些關(guān)心此類事物的人:許多人都在詢問(wèn)Windows是用C還是C++編寫的。答案是 - 盡管NT是基于對(duì)象的設(shè)計(jì) - 像大多數(shù)操作系統(tǒng)一樣,Windows幾乎完全用C語(yǔ)言編寫!為什么? C++在內(nèi)存占用和代碼執(zhí)行開(kāi)銷方面引入了開(kāi)銷。即使在今天,使用C++編寫的代碼的其所隱藏的開(kāi)銷也會(huì)令人大吃一驚,但早在1990年代后期,此時(shí)內(nèi)存價(jià)格約為60$/MB(是的......每個(gè)MEGABYTE為60美元!)時(shí),vtable等隱藏機(jī)制的內(nèi)存開(kāi)銷非常高。此外,虛方法間接調(diào)用和對(duì)象解引用的開(kāi)銷可能導(dǎo)致當(dāng)時(shí)的C++代碼存在非常顯著的性能和規(guī)模損耗。雖然你仍然需要當(dāng)心,現(xiàn)代C++在現(xiàn)代計(jì)算機(jī)上的性能開(kāi)銷并不是一個(gè)值得關(guān)注的問(wèn)題,同時(shí)考慮到其安全性、可讀性和可維護(hù)性方面的優(yōu)勢(shì),這通常是一種可接受的折衷...這就是為什么我們將Console的代碼穩(wěn)步升級(jí)到現(xiàn)代C++這樣做的原因!
那么,Windows 控制臺(tái)內(nèi)部是什么樣?
在 Windows 7 之前,Windows 控制臺(tái)實(shí)例托管于核心的客戶-服務(wù)器運(yùn)行子系統(tǒng)(Client Server Runtime Subsystem,CSRSS)!然而,在 Windows 7 中,考慮到安全性和可靠性因素,控制臺(tái)從CSRSS 中剝離出來(lái),組件了一個(gè)包含如下二進(jìn)制文件的新家庭:
conhost.exe - 用戶模式的 Windows 控制臺(tái) UX 和命令行管道condrv.sys - 一個(gè)提供基礎(chǔ)通信結(jié)構(gòu)的核心驅(qū)動(dòng),連接 conhost 和命令行 Shell/工具/應(yīng)用之間的通信控制臺(tái)當(dāng)前的內(nèi)部結(jié)構(gòu)總體結(jié)構(gòu)圖就像這樣:
控制臺(tái)的核心組件包含如下內(nèi)容(自下而上):
ConDrv.sys - 核心模式驅(qū)動(dòng)請(qǐng)求執(zhí)行 API 調(diào)用控制臺(tái)實(shí)例的數(shù)據(jù)呈現(xiàn)從控制臺(tái)發(fā)送到命令行應(yīng)用的文本為控制臺(tái)及其連接的命令行應(yīng)用提供高性能通信通道在控制臺(tái)及附著于其上的命令行應(yīng)用這間反復(fù)傳遞 IO 控制 (IOCTL) 消息管理控制臺(tái) IOCTL 消息ConHost.exe - Win32 圖形界面(GUI)應(yīng)用:管理控制臺(tái)容器在屏幕上的布局、大小、位置等。顯示并處理設(shè)置界面等。調(diào)用 Windows 消息隊(duì)列,處理 Windows 消息并將用戶輸入轉(zhuǎn)換為鍵盤和鼠標(biāo)事件,并將之存儲(chǔ)于輸入緩沖區(qū)。API Server: 轉(zhuǎn)換 API 調(diào)用時(shí)從命令行應(yīng)用收到的 IOCTL 消息,并將文本記錄從控制臺(tái)發(fā)給命令行應(yīng)用。API: 實(shí)現(xiàn) Win32 控制臺(tái) API,以及所有要求控制臺(tái)執(zhí)行的操作背后的邏輯。Input Buffer: 保存由用戶輸入產(chǎn)生的鍵盤和鼠標(biāo)事件記錄VT Parser: 如果啟動(dòng),則從文本中解析 VT 序列,根據(jù)找到的信息產(chǎn)生等效的 API 調(diào) I用Output Buffer: 保存控制臺(tái)呈現(xiàn)的文本。本質(zhì)上是一個(gè)二維的 CHAR_INFO 結(jié)構(gòu)數(shù)組,其每個(gè)元素都包含了字符數(shù)據(jù)及其屬性(緩存區(qū)之下的更多信息)Other: 未包含在上層呈現(xiàn),包含從注冊(cè)表或快捷文件中存儲(chǔ)/檢索基礎(chǔ)設(shè)置值。ConHost Core - 控制臺(tái)的內(nèi)部控制和管道Console UX App Services - 控制臺(tái)的 UX 和 UI 層Windows控制臺(tái)API
從上述的控制臺(tái)架構(gòu)圖中可以看出,與NIX終端不同的是,控制臺(tái)發(fā)送/接收API調(diào)用和/或數(shù)據(jù)序列化為IO控制(IOCTL)消息,而不是序列化后的文本! 甚至從(主要是Linux)命令行應(yīng)用程序接收的文本中所嵌入的ANSI/VT序列也被提取、解析并轉(zhuǎn)換為API調(diào)用!
這種差異揭示了*NIX和Windows之間關(guān)鍵的基本哲學(xué)差異:在*NIX中,“一切都是文件”,然而在Windows中,“一切都是對(duì)象”!
兩種方法都有利有弊,我們將概括之,但避免在這里進(jìn)行長(zhǎng)篇大論。請(qǐng)記住,哲學(xué)中的這一關(guān)鍵差異是Windows和* NIX之間諸多差異的基礎(chǔ)!
在 *NIX系統(tǒng)中,一切都是文件
在60年代末和70年代初Unix被第一次實(shí)現(xiàn)的時(shí)候,其中一個(gè)核心原則就是任何東西都可以被抽象成文件流,一個(gè)關(guān)鍵目標(biāo)是簡(jiǎn)化對(duì)設(shè)備和外設(shè)的訪問(wèn)處理:如果所有的設(shè)備都在系統(tǒng)中以文件系統(tǒng)的形式存在,那么現(xiàn)存的代碼就可以不做修改地直接訪問(wèn)這些設(shè)備。
這個(gè)原則影響深遠(yuǎn):你可以通過(guò)偽文件系統(tǒng)或虛擬文件系統(tǒng)來(lái)瀏覽和查詢大量的基于*NIX的系統(tǒng)和機(jī)器配置,它們僅僅是”表現(xiàn)得“像是“文件”或“文件夾”,實(shí)際可能是機(jī)器配置或硬件。
例如,在Linux中,你可以通過(guò)訪問(wèn) /proc/cpuinfo 虛擬文件節(jié)點(diǎn)來(lái)查看CPU的一些信息:
這個(gè)模型是如此簡(jiǎn)單和一致,但它也存在一些額外開(kāi)銷:從這些偽文件中提取或查詢特殊的文本信息并從執(zhí)行命令中返回,經(jīng)常需要一些工具的輔助,比如:sed,awk,perl,python等。這些工具經(jīng)常被用來(lái)寫腳本和命令來(lái)解析文本內(nèi)容、查找特殊模式、區(qū)域和值。這些腳本可以變得非常復(fù)雜,難以維護(hù)和碎片化。如果文本的結(jié)構(gòu)、布局或格式發(fā)生變更,那么許多腳本也需要隨之更新。
在Windows中,任何事物都是對(duì)象
當(dāng)Windows NT被設(shè)計(jì)和構(gòu)建時(shí),“對(duì)象”被視為軟件設(shè)計(jì)的未來(lái):“面向?qū)ο蟆钡恼Z(yǔ)言比洞穴里的兔子更快出現(xiàn) - Simula和Smalltalk已經(jīng)建立起來(lái),而C ++正變得越來(lái)越流行。其他面向?qū)ο蟮恼Z(yǔ)言,如Python,Eiffel,Objective-C,ObjectPascal / Delphi,Java,C#等許多其他語(yǔ)言都在快速發(fā)展緊隨其后。
不可避免的是,它成型于面向?qū)ο蟠蠛脮r(shí)期(大約1989年)中,Windows NT的設(shè)計(jì)理念是“一切都是對(duì)象”。事實(shí)上,NT內(nèi)核最重要的部分之一是“對(duì)象管理器”!
Windows NT公開(kāi)了一組豐富的Win32 API,可以調(diào)用這些API來(lái)從操作系統(tǒng)獲取和/或操作對(duì)象。開(kāi)發(fā)人員使用Win32 API來(lái)收集和呈現(xiàn)* NIX偽文件和工具提供的類似信息,但是通過(guò)對(duì)象和結(jié)構(gòu)。并且因?yàn)榻馕銎鳎幾g器和分析器理解對(duì)象的結(jié)構(gòu),所以通常可以更早地捕獲許多編碼錯(cuò)誤,從而幫助驗(yàn)證程序員的意圖在語(yǔ)法和邏輯上是否正確。隨著時(shí)間的推移,這也可以減少系統(tǒng)破損,波動(dòng)和“攪動(dòng)”。
所以,回到我們關(guān)于Windows控制臺(tái)的中心討論:NT團(tuán)隊(duì)決定構(gòu)建一個(gè)“控制臺(tái)”,它在幾個(gè)關(guān)鍵領(lǐng)域區(qū)別于傳統(tǒng)的* NIX終端:
控制臺(tái)API:Windows Console可以通過(guò)豐富的Console API進(jìn)行操作和控制,而不是依賴程序員生成“難以驗(yàn)證”的ANSI / VT序列的能力。公共服務(wù):為避免每個(gè)命令行shell一次又一次地重新實(shí)現(xiàn)相同的服務(wù)(例如命令歷史記錄,命令別名),控制臺(tái)本身提供了一些這些服務(wù),可通過(guò)Console API訪問(wèn)Windows控制臺(tái)的問(wèn)題
雖然Console的API已經(jīng)證明在Windows命令行工具和服務(wù)領(lǐng)域非常流行,但以API為中心的模型對(duì)命令行方案提出了一些挑戰(zhàn):
只有Windows實(shí)現(xiàn)了Console API
許多Windows命令行工具和應(yīng)用程序廣泛使用Console API。
問(wèn)題呢?這些API僅適用于Windows。
因此,結(jié)合其他差異化因素(例如過(guò)程生命周期差異等),Windows命令行應(yīng)用程序并不總是易于移植到* NIX,反之亦然。
因此,Windows生態(tài)系統(tǒng)開(kāi)發(fā)了自己的,通常類似但通常不同的命令行工具和應(yīng)用程序。這意味著用戶在使用Windows時(shí)必須學(xué)習(xí)一組命令行應(yīng)用程序和工具,shell,腳本語(yǔ)言等,而在使用* NIX時(shí)則需要學(xué)習(xí)另一組。
這個(gè)問(wèn)題沒(méi)有簡(jiǎn)單的快速解決方案:Windows控制臺(tái)和命令行不能簡(jiǎn)單地丟棄并被bash和iTerm2取代 - 有數(shù)以億計(jì)的應(yīng)用程序和腳本依賴于Windows控制臺(tái)和Cmd / PowerShell shells。
像Cygwin這樣的第三方工具可以很好地將許多核心GNU工具和兼容性庫(kù)移植到Windows,但是它們無(wú)法運(yùn)行未移植的,未經(jīng)修改的Linux二進(jìn)制文件。這非常重要,因?yàn)樵S多Ruby,Python,Node包和模塊依賴于或包裝Linux二進(jìn)制文件,或者依賴于* NIX運(yùn)轉(zhuǎn)狀態(tài)。
這些原因促使微軟通過(guò)在 Windows的子系統(tǒng)Linux(WSL)上本地運(yùn)行真正的,未經(jīng)修改的Linux二進(jìn)制文件和工具來(lái)擴(kuò)展Windows的兼容性。使用WSL的用戶現(xiàn)在可以在同一臺(tái)機(jī)器上并行下載和安裝一個(gè)或多個(gè)Linux發(fā)行版,并使用apt / zypper / npm / gem / etc.安裝和運(yùn)行絕大多數(shù)Linux命令行工具以及他們喜歡的Windows應(yīng)用程序和工具。
但是,還有一些控制臺(tái)提供的東西尚未被非Microsoft終端采用:具體來(lái)說(shuō),Windows控制臺(tái)提供命令歷史記錄和命令別名服務(wù),從而無(wú)需每個(gè)命令行shell(特別是)重新實(shí)現(xiàn)相同的功能。
把 Windows 命令行遠(yuǎn)程化是困難的
正如我們?cè)?Command-Line Backgrounder 一文中所討論的那樣,終端最初與它們所連接的計(jì)算機(jī)是分開(kāi)的。快進(jìn)到今天,這種設(shè)計(jì)仍然存在:大多數(shù)現(xiàn)代終端和命令行應(yīng)用程序/shell 等等是由進(jìn)程或機(jī)器邊界分隔的。
在基于 *NIX 的平臺(tái)上,終端和命令行應(yīng)用程序的分離并通過(guò)簡(jiǎn)單的字符進(jìn)行通信的概念導(dǎo)致 *NIX 命令行易于從遠(yuǎn)程計(jì)算機(jī)/設(shè)備訪問(wèn)和操作:只要終端和命令行應(yīng)用程序可以通過(guò)某種類型的有序串行通信基礎(chǔ)架構(gòu)(TTY/PTY 等)傳輸字符流,遠(yuǎn)程操作 *NIX 機(jī)器的命令行是非常簡(jiǎn)單的。
但是在 Windows 上,許多命令行應(yīng)用程序依賴于調(diào)用 Console API,并假設(shè)它們與控制臺(tái)本身在同一臺(tái)機(jī)器上運(yùn)行。這使得遠(yuǎn)程操作 Windows 命令行 shell/工具等變得很困難:在遠(yuǎn)程計(jì)算機(jī)上運(yùn)行的命令行應(yīng)用程序如何調(diào)用在用戶本地計(jì)算機(jī)的控制臺(tái)上的 API 呢?更糟糕的是,如果遠(yuǎn)程命令行應(yīng)用程序通過(guò) Mac 或 Linux 機(jī)器上的終端訪問(wèn),它如何調(diào)用 Console API 呢?!
很抱歉開(kāi)個(gè)玩笑,但我們將在以后的文章中更詳細(xì)地闡釋這個(gè)主題!
啟動(dòng)控制臺(tái)或者不!
通常,在基于 *NIX 的系統(tǒng)上,當(dāng)用戶想要啟動(dòng)一個(gè)命令行工具時(shí),他們首先會(huì)啟動(dòng)一個(gè)終端。然后終端啟動(dòng)一個(gè)默認(rèn)的 shell ,或者可以配置為啟動(dòng)一個(gè)特定的應(yīng)用程序/工具。終端和命令行應(yīng)用程序通過(guò)偽終端(PTY)交換字符流進(jìn)行通信,直到一個(gè)或兩個(gè)字符終止。
然而,在 Windows 系統(tǒng)上,事情就不一樣了:Windows 用戶永遠(yuǎn)不會(huì)啟動(dòng)控制臺(tái)(conhost.exe)——然而他們會(huì)啟動(dòng)像是 Cmd.exe,PowerShell.exe,wsl.exe 等等這樣的命令行 shell 和應(yīng)用程序。Windows 系統(tǒng)將新啟動(dòng)的應(yīng)用程序連接到當(dāng)前控制臺(tái)(如果是從命令行啟動(dòng)的話),或者連接到新創(chuàng)建的控制臺(tái)實(shí)例。
# 現(xiàn)在要說(shuō)的?
是的,在 Windows 系統(tǒng)中,用戶啟動(dòng)命令行應(yīng)用程序,而不是控制臺(tái)本身。
如果用戶從現(xiàn)有的命令行 shell 啟動(dòng)命令行應(yīng)用程序,Windows 通常會(huì)將新啟動(dòng)的 .exe(可執(zhí)行文件) 附加到當(dāng)前控制臺(tái)。否則,Windows 會(huì)將一個(gè)新的控制臺(tái)實(shí)例與新推出的應(yīng)用程序綁定在一起。
小白說(shuō):很多人說(shuō)“命令行程序在控制臺(tái)運(yùn)行”。這不是真的,而且導(dǎo)致很多關(guān)于控制臺(tái)和命令行應(yīng)用程序如何工作的困惑!命令行應(yīng)用程序和它們的控制臺(tái)都在各自獨(dú)立的 Win32 進(jìn)程中運(yùn)行。請(qǐng)通過(guò)指出“命令行工具/應(yīng)用程序連接到控制臺(tái)運(yùn)行”(或類似的)來(lái)幫助糾正這種誤解。謝謝!
聽(tīng)起來(lái)不錯(cuò),對(duì)吧?嗯…不;這里有一些問(wèn)題:
1.控制臺(tái)和命令行應(yīng)用程序通過(guò)經(jīng)由驅(qū)動(dòng)程序的 IOCTL 消息進(jìn)行通信,而不是通過(guò)文本流進(jìn)行通信
2.windows 要求 ConHost.exe 必須是連接到命令行應(yīng)用程序的控制臺(tái)程序
3.Windows 控制了控制臺(tái)和命令行應(yīng)用程序通信之間通信“管道”的創(chuàng)建
這些都是明顯的限制:如果你想為 Windows 創(chuàng)建一個(gè)替代控制臺(tái)的應(yīng)用程序,該怎么辦?你將如何發(fā)送鍵盤、鼠標(biāo)、筆等等外設(shè)的信息?如果你無(wú)法訪問(wèn)連接你新控制臺(tái)和命令行應(yīng)用程序的通信“管道”,用戶將怎么對(duì)命令行應(yīng)用程序進(jìn)行操作?
遺憾的是,這些情況并不好:有一些很棒的用于 Windows 的第三方控制臺(tái)(和服務(wù)器應(yīng)用程序)(例如 ConEmu/Cmder, Console2/ConsoleZ, Hyper, Visual Studio Code, OpenSSH 等),他們必須通過(guò)離奇的跳轉(zhuǎn)才能像正常的控制臺(tái)一樣運(yùn)行!
舉例來(lái)說(shuō),第三方控制臺(tái)必須在屏幕外啟動(dòng)一個(gè)命令行應(yīng)用程序,例如(-32000,-32000)。然后,他們必須向屏幕外控制臺(tái)發(fā)送擊鍵信息,然后收集屏幕外控制臺(tái)的文本內(nèi)容并在自己的 UI 上重新繪制它們!
我知道,這很瘋狂,對(duì)吧? !這證明了這些應(yīng)用程序創(chuàng)造者們的獨(dú)創(chuàng)性和決心,這些程序甚至還在有效的運(yùn)行!
這顯然是我們急于補(bǔ)救的一種情況。請(qǐng)繼續(xù)關(guān)注這部分內(nèi)容的更多信息——在這方面有一些好消息!
Windows 控制臺(tái) & VT
如上所述,Windows 控制臺(tái)提供了大量 API。使用控制臺(tái) API,命令行應(yīng)用程序和工具可寫入文本,更改文本顏色,移動(dòng)光標(biāo)等。并且,由于控制臺(tái) API 的存在,Windows 控制臺(tái)幾乎不需要支持 ANSI/VT 序列,這些序列在其他平臺(tái)上提供非常類似的功能。
實(shí)際上,在 Windows 10 之前,Windows 控制臺(tái)僅實(shí)現(xiàn)了對(duì) ANSI/VT 序列的最低限度支持:
從2014年開(kāi)始,微軟組建了一個(gè)新的 Windows 控制臺(tái)團(tuán)隊(duì),使得這一切都發(fā)生了變化。控制臺(tái)團(tuán)隊(duì)的最高優(yōu)先級(jí)事項(xiàng)之一是實(shí)現(xiàn)對(duì) ANSI/VT 序列的全面支持,以便渲染在 Windows 子系統(tǒng)之Linux(WSL)和遠(yuǎn)程 *NIX 機(jī)器上運(yùn)行的 *NIX 應(yīng)用程序的輸出。您可以在本系列的上一篇文章中閱讀更多關(guān)于這個(gè)故事的內(nèi)容。
控制臺(tái)團(tuán)隊(duì)迅速為 Windows 10 的控制臺(tái)添加了對(duì) ANSI/VT 序列的全面支持,使用戶能夠使用和享用大量 Windows 和 Linux 命令行工具和應(yīng)用程序。
該團(tuán)隊(duì)繼續(xù)改進(jìn)和完善每個(gè)操作系統(tǒng)發(fā)布版本上的控制臺(tái)對(duì) VT 的支持,并對(duì)您在我們的 GitHub 問(wèn)題跟蹤器上提交的任何問(wèn)題表示感謝。
處理Unicode
一個(gè)快速的Unicode回顧:
Unicode或ISO/IEC 10646是一個(gè)國(guó)際標(biāo)準(zhǔn),定義了地球上幾乎每個(gè)書寫系統(tǒng)中所使用的每個(gè)字符/字形,以及當(dāng)今使用的許多非腳本符號(hào)和字符大小的圖像(例如表情符號(hào))。目前(2018年7月),Unicode 11定義了137439個(gè)字符,包含146個(gè)現(xiàn)代和歷史文字系統(tǒng)!
Unicode還定義了幾種字符編碼,包括UTF-8, UTF-16, 和UTF-32:
UTF-8: 前127個(gè)編碼點(diǎn)使用1字節(jié)(主要為了維持與ASCII的兼容性),其他字符可選附加長(zhǎng)度1-4字節(jié)UTF-16/UCS-2: 每個(gè)字符兩個(gè)字節(jié)。UCS-2 (被Windows內(nèi)部使用)z支持對(duì)前65536編碼點(diǎn)(統(tǒng)稱為基本多語(yǔ)言平面-BMP)。UTF-16通過(guò)17個(gè)額外的字符平面擴(kuò)展了UCS-2。UTF-32: 每個(gè)字符4字節(jié)由于UTF-8的高效的存儲(chǔ)要求以及在HTML頁(yè)面中的廣泛使用,它是目前最流行的編碼。
UTF-16/UCS-2都是常見(jiàn)的,盡管在已存儲(chǔ)文檔(例如網(wǎng)頁(yè)、代碼等)中其使用比例正在降低。UTF-32是很少使用的,因?yàn)樗男实颓掖鎯?chǔ)需要相當(dāng)大的空間。
很好,所以我們有有效并且高效的方式來(lái)表示和存儲(chǔ)Unicode字符了!
所以?
哎呀,Windows控制臺(tái)及其API是在創(chuàng)建Unicode之前創(chuàng)建的!
Windows控制臺(tái)將文本(隨后在屏幕上繪制)存儲(chǔ)為每個(gè)單元需要2個(gè)字節(jié)的UCS-2字符。
命令行應(yīng)用程序使用控制臺(tái)API將文本寫入到控制臺(tái)中。處理文本的控制臺(tái)API有兩種形式 - 帶有A后綴處理的單字節(jié)/字符串的函數(shù),帶有W后綴處理雙字節(jié)(wchar)/字符串的函數(shù):
例如,WriteConsoleOutputCharacter()函數(shù)編譯為ASCII項(xiàng)目的WriteConsoleOutputCharacterA(),或Unicode項(xiàng)目的WriteConsoleOutputCharacterW()。如果需要指定處理方式,代碼中可以直接調(diào)用... A或...W后綴的函數(shù)。
注意:每個(gè)W API至少支持UCS-2,因?yàn)檫@是在進(jìn)行A/W拆分時(shí)就存在的事情,我們認(rèn)為這樣做會(huì)很棒。但許多W API已更新為在同一渠道上也支持UTF-16
。并非所有W API都可以支持UTF-16,但所有W API至少可以支持UCS-2。
此外,控制臺(tái)不支持一些較新的Unicode功能,包括零寬度連接符(ZWJ),該符號(hào)被用于連接阿拉伯語(yǔ)和印度語(yǔ)中的其他單獨(dú)字符,并將表情符號(hào)字符組合成一個(gè)可視字形!
那么如果你想在控制臺(tái)上輸出一個(gè)ninjacat表情符號(hào)或復(fù)雜的多字節(jié)中文/阿拉伯字符會(huì)怎樣呢? 糟糕的是,你做不到!
Console API不僅不支持長(zhǎng)度超過(guò)2字節(jié)/字形的Unicode字符(NinjaCat表情符號(hào)需要8個(gè)字節(jié)!),但Console內(nèi)部的UCS-2緩沖區(qū)不能存儲(chǔ)該數(shù)據(jù)的額外字節(jié),更糟糕的是 ,Console當(dāng)前的基于GDI的渲染器甚至無(wú)法繪制字形,即使緩沖區(qū)可以存儲(chǔ)它!
可嘆! 這就是遺留代碼的樂(lè)趣。
但是,我也會(huì)希望你們到此打住 - 我們將在本系列的新一篇文章中回到這個(gè)主題。 敬請(qǐng)關(guān)注!
所以,我們?cè)谀睦?再一次,親愛(ài)的讀者,如果你讀過(guò)以上的所有內(nèi)容,謝謝你,也祝賀你 —— 你現(xiàn)在比你的大多數(shù)朋友都更了解 Windows 控制臺(tái),甚至可能比你想知道的還要多!祝你幸運(yùn)!
在這篇文章中,我們涵蓋了很多內(nèi)容:
Windows控制臺(tái)的主要構(gòu)建模塊:
API Server —— 通過(guò) IOCTL 消息向驅(qū)動(dòng)程序發(fā)送或從驅(qū)動(dòng)程序接收序列化的 API 調(diào)用和文本數(shù)據(jù)。API——控制臺(tái)的功能函數(shù)。Buffers —— 輸入緩沖用于存儲(chǔ)用戶輸入,輸出緩沖用于存儲(chǔ)輸出和顯示文本。輸入緩沖存儲(chǔ)用戶輸入,輸出緩沖存儲(chǔ)輸出和顯示文本。VT Parser —— 將嵌入文本流的 ANSI/VT 序列轉(zhuǎn)換為 API 調(diào)用Console UX —— 控制臺(tái)的用戶界面狀態(tài)、設(shè)置和功能Other —— Misc 生命周期、安全性等。Condrv.sys —— 控制臺(tái)通信驅(qū)動(dòng)程序ConHost.exe —— 控制臺(tái)用戶體驗(yàn)、內(nèi)部構(gòu)件和管道:控制臺(tái)做什么?
向連接的命令行應(yīng)用程序發(fā)送用戶輸入接收并顯示連接的命令行應(yīng)用程序輸出控制臺(tái)與 *NIX 終端有什么不同
*NIX:“一切都是文件/文本流”Windows:“一切都是對(duì)象,可以通過(guò) API 進(jìn)行訪問(wèn)”控制臺(tái)存在的問(wèn)題
大部分都在 Windows 10 中得到了修復(fù)只有 ConHost.exe 可以附加到命令行應(yīng)用程序第三方終端被迫創(chuàng)建屏幕外控制臺(tái),并向它發(fā)送按鍵和屏幕信息,或從中接收按鍵和屏幕信息遠(yuǎn)程操作 Windows 命令行應(yīng)用程序和工具存在困難來(lái)自 Windows 的端口命令行 APP 的工作變得更多控制臺(tái)和命令行應(yīng)用程序通過(guò)序列化 API 調(diào)用請(qǐng)求和文本組成的 IOCTL 消息進(jìn)行通信只有 Windows 命令行應(yīng)用程序能調(diào)用控制臺(tái) API應(yīng)用程序調(diào)用 Windows API 與控制臺(tái)交互對(duì) IOCTL 的依賴打破了“字符交換”原則的終端設(shè)計(jì)使從非 Windows 機(jī)器操作遠(yuǎn)程 Windows 命令行工具變得困難啟動(dòng) Windows 命令行應(yīng)用程序是“不常用的”Windows一直不識(shí)別ANSI/VT序列控制臺(tái)對(duì) Unicode 的支持有限,目前正在努力處理存儲(chǔ)和展現(xiàn)現(xiàn)代 UTF-8 和需要零寬度連接符的字符在本系列的后續(xù)文章中,我們將深入探討控制臺(tái),并討論如何處理這些問(wèn)題……和更多其他內(nèi)容!
像往常一樣,請(qǐng)繼續(xù)關(guān)注我們。
本文由oschina作者參與翻譯,如有侵權(quán),請(qǐng)聯(lián)系刪除。