控制台輸入輸出機制


本文涉及的某些概念在前文中有所提及,如果有不太清楚的描述,建議參考前文。

本文屬於控制台編程第三篇文章,前兩篇鏈接如下。

一、內容概述

針對不同的控制需求和靈活度,控制台提供了高層和底層兩種不同的輸入輸出接口。

高層輸入輸出接口簡化了字符輸入/輸出的復雜性,但限制了對輸入緩沖和屏幕輸出的訪問控制。

底層輸入輸出接口需要更多的編碼及針對不同輸入輸出函數的選擇,但具有更大的靈活性。

控制台提供的高層、底層輸入輸出接口不是互斥的,同一個應用程序可以按照實際需求同時調用高層、底層輸入輸出函數。

本文主要參考msdn上的Input and Output Methods一文,並結合我自己的理解,加以整理擴充。

本文主要涉及控制台輸入、輸出函數的功能,不同的輸入輸出模式對函數的影響等。

二、控制台模式

 控制台模式按照操作划分,可分為輸入模式、輸出模式。輸入模式主要與控制台的輸入緩沖相關,主要影響輸入操作及調用;輸出模式與控制台的屏幕緩沖有關,主要影響輸出操作及調用。輸入模式可分為影響高層輸入接口的模式、影響底層輸入接口的模式。輸出模式僅包括影響高層輸出接口的模式,對底層輸出函數無影響。

應用程序可以使用 GetConsoleMode函數和SetConsoleMode函數來獲取、設置輸入緩沖的輸入模式或者屏幕緩沖的輸出模式。對於用於多個屏幕緩沖的控制台程序,每個屏幕緩沖的輸出模式可以不同。應用程序可以隨時調用SetConsoleMode來修改控制台的輸入輸出模式。

1. 高層控制台模式(輸入模式、輸出模式)

控制台在創建時會默認啟用以下輸入模式:

  • 行輸入模式(ENABLE_LINE_INPUT)
  • 自動處理輸入模式(ENABLE_PROCESSED_INPUT)
  • 輸入回顯模式(ENABLE_ECHO_INPUT)

自動啟用以下輸出模式:

  • 自動處理輸出模式(ENABLE_PROCESSED_OUTPUT)
  • 自動換行模式(ENABLE_WRAP_AT_EOL_OUTPUT)

除了自動換行模式外,其他模式都是一起使用。也就是說要么全部啟用行輸入模式、自動處理輸入模式、輸入回顯模式、自動處理輸出模式,要么全部禁用。全部啟用的情況下,應用程序本身需要做的處理很少,僅需要處理簡單的字符I/O即可。全部禁用的情況,應用程序需要處理所有的輸入輸出事件。各個輸入輸出模式的詳細含義可參考下表:

模式 描述

ENABLE_PROCESSED_INPUT

自動處理輸入模式

用於控制是否將系統控制鍵及系統事件放到輸出緩沖中。

在自動換行輸出模式下,退格backspace和換行會被系統自動處理。退格鍵引起光標向前移動一個字符,並且不影響光標當前所在位置的字符。回車鍵被作為行結束的標志。

ENABLE_LINE_INPUT

自動換行模式

用於控制台輸入函數響應方式。

啟用的情況下,在用戶輸出回車換行時會觸發ReadFileReadConsole函數返回。

禁用的情況下,當輸入緩沖中有字符時,ReadFileReadConsole函數會直接返回。

ENABLE_ECHO_INPUT

輸入回顯模式

用於控制用戶輸入字符是否回顯。主要回顯會把輸入回顯到活動屏幕緩沖。

輸出回顯模式只能在自動換行模式下使用。

活動屏幕緩沖的輸出模式會影響回顯字符效果。

ENABLE_PROCESSED_OUTPUT

自動處理輸出模式

用於控制是否自動處理寫入到屏幕緩沖中的ANSI控制字符(比如回退backspace、tab鍵、/a蜂鳴聲、回車換行符、/t填充字符等)。

實際處理下:TAB鍵會引起光標移動到下一個tab位置(相鄰兩個tag間隔為8個字符)。/a轉義字符會引起系統蜂鳴聲。

ENABLE_WRAP_AT_EOL_OUTPUT

輸出自動換行模式

用於控制輸出到達行尾時是否自動切換到下一行的起始位置。

如果到達控制台窗口區域的下邊界,則會引起窗口自動下移一行。

如果到達控制台屏幕緩沖的底部,則會引起屏幕緩沖向上滾動一行,屏幕緩沖會丟掉最上面的一行數據,窗口區域不變。

禁用的情況下,行尾的字符會被后續暑促胡字符覆蓋掉。

2. 底層控制台輸入模式

控制台輸入緩沖中的事件類型取決於控制台的輸入模式。

控制台的輸入模式決定了控制台如何處理CTRL+C組合鍵。

由於底層控制台輸出函數都會指定輸出字符的屬性和操作,所以控制台輸出模式對底層輸出函數無效。

控制台底層輸入模式包括以下幾種:

模式 描述

ENABLE_MOUSE_INPUT

使能鼠標輸入

用於控制是否在輸入緩沖中記錄鼠標事件。

默認情況下,控制台輸入緩沖是記錄鼠標事件,不記錄窗口輸入事件。

改變這些輸入模式,僅會對后續輸入操作有影響。輸出緩沖中的未處理的鼠標事件和窗口事件時不會被刷新的。

是否使能對鼠標的顯示沒有影響。

ENABLE_WINDOW_INPUT

使能窗口輸入

用於控制是否在輸入緩沖中記錄窗口縮放事件。

默認情況下,控制台輸入緩沖是記錄鼠標事件,不記錄窗口輸入事件。

改變這些輸入模式,僅會對后續輸入操作有影響。輸出緩沖中的未處理的鼠標事件和窗口事件時不會被刷新的。

ENABLE_PROCESSED_INPUT

自動處理輸入

用於控制高層控制台輸入輸出函數。

但在該模式啟用的情況下,CTRL+C組合鍵不會記錄在控制台的輸入緩沖中,會直接調用已注冊的控制回調函數,可參考Console Control Handlers

三、高層控制台輸入輸出接口

 高層控制台輸出輸出接口簡化了從控制台讀寫字符流的方式。高層輸入操作從控制台輸入緩沖中讀入字符,並保存在指定的緩沖區中。高層輸出操作將指定緩沖區的字符輸出到屏幕緩沖的光標所在位置,並按照實際輸入字符數目移動光標。高層控制台I/O僅有四個函數:ReadFileWriteFile 、ReadConsoleWriteConsole。這兩組函數功能相似,主要有兩個區別。

  • ReadConsoleWriteConsole函數支持ANSI和UNICODE;而ReadFileWriteFile函數僅支持ANSI。
  • ReadFileWriteFile函數支持文件、管道、串口通信等機制;而ReadConsoleWriteConsole函數僅支持控制台句柄,不能使用重定向之后的句柄。對於需要重定向輸入輸出的應用建議慎重選擇。

使用高層控制台I/O時,會依賴於屏幕緩沖的屬性來顯示字符屬性。也可使用控制台模式影響高層I/O處理邏輯。

使用高層控制台輸入函數(ReadConsole、ReadFile)接收的鍵盤輸入僅包括可符號化的輸入(轉化為ANSI或者UNICODE字符)、某些控制鍵,但不包括鍵盤上的功能鍵、方向鍵。由鼠標、窗口、獲取焦點、菜單輸入的事件均會被忽略。

如果行輸入模式啟用的情況下(默認設置),高層控制台輸入函數在未遇到回車鍵Enter之前時不會返回;在行輸入模式禁用的情況下,緩沖區中只要有數據就會返回。不管行輸出模式是否啟用,除非遇到給定輸入緩沖區已滿或者無輸出字符的清下,輸入函數會盡可能多的讀入字符。未讀取的字符會被緩沖,直到下次讀取為止。函數返回值表示實際讀入的字符數目。在回顯輸入模式啟用的情況下,這些函數讀入的字符都會回顯到活動屏幕緩沖的當前光標位置。

高層控制台輸出函數(WriteFile、WriteConsole)可以向活動或非活動屏幕緩沖寫字符流。

使用(WriteFile、WriteConsole)或者ReadConsole、ReadFile回顯的字符都會插入到屏幕緩沖光標的當前位置。光標移動位置受控制台輸出模式控制。

四、底層控制台輸入輸出接口

控制台底層I/O提供更加強大的機制來訪問輸入緩沖和屏幕緩沖。應用程序可以使用控制台底層I/O來完成下面的幾個功能:

  • 接收=鼠標事件及緩沖區變化事件
  • 接收鍵盤輸入事件的擴展信息
  • 向輸入緩沖添加輸入記錄
  • 從輸入緩沖中讀取輸入記錄,但保留處理之后的輸入記錄
  • 讀取輸入緩沖待處理事件的數目
  • 清空輸入緩沖
  • 從屏幕緩沖的指定光標位置讀寫ANSI或UNICODE字符
  • 從屏幕緩沖的指定光標位置讀寫自定義字符串(前景色、背景色等)
  • 在屏幕緩沖指定位置讀寫矩形區域的字符數據(字符、顏色屬性)
  • 在屏幕緩沖指定位置寫連續多個重復的ANSI或UNICODE字符(字符、顏色屬性)

1. 底層輸入函數

控制台的底層輸入函數的緩沖區中包含鍵盤、鼠標、緩沖變化、獲得焦點及菜單消息等事件的一系列輸入記錄。底層輸入函數可直接訪問控制台的輸入緩沖。控制台主要提供以下幾個底層輸入函數:

函數名 說明

ReadConsoleInput

從輸入緩沖中讀取並移除輸入記錄。

本函數僅在輸入緩沖中有輸入記錄時返回。系統會將所有的輸入記錄保存到函數調用給定的緩沖區中,在以下兩種情況下函數會返回:輸入緩沖中無新的輸入記錄、給定的緩沖區已滿。未讀入的輸入記錄會保留在輸入緩沖中,等待下次輸入一並處理。

函數最后一個參數返回實際讀入的輸入記錄個數。

 PeekConsoleInput

從輸入緩沖中讀取輸入記錄,但不移除輸入記錄。

輸入緩沖的所有輸入記錄都會復制到給定的緩沖區中。如果輸入緩沖中無輸入記錄,本函數直接返回。

功能、參數、返回值,均和ReadConsoleInput類似。

 GetNumberOfConsoleInputEvents 獲取輸入緩沖中未讀取的輸入記錄的個數。 
 WriteConsoleInput

在輸入緩沖尾部添加一個新的輸入記錄。

輸入緩沖會按照實際需要自動擴充大小,以保存所有輸入記錄。

 FlushConsoleInputBuffer 清空輸入緩沖中所有的輸入記錄。

 控制台的輸入緩沖的句柄可用於線程同步等待函數。如果輸入緩沖中有未讀取的輸入記錄時,該輸入緩沖句柄就會變為“signaled”(標志)狀態,可用於同步線程的等待處理,當輸入緩沖清空之后,該句柄的標志狀態會自動清除;若輸入緩沖中無輸入記錄,使用該句柄調用線程同步等待函數是高效的,cpu占用很低。也就是說應用程序可以使用單獨的線程等待並處理輸入緩沖中的事件。

2. 底層輸出函數

 底層輸出函數可以直接訪問控制台屏幕緩沖的字符。主要包含以下幾個函數:

函數 描述
ReadConsoleOutputCharacter 從屏幕緩沖中讀入ANSI或UNICODE字符串。
WriteConsoleOutputCharacter 向屏幕緩沖中寫入ANSI或UNICODE字符串。
ReadConsoleOutputAttribute 從屏幕緩沖中復制包含文本屬性的字符串。
WriteConsoleOutputAttribute 向屏幕緩沖中寫入包含文本屬性的字符串。
FillConsoleOutputCharacter 向屏幕緩沖中連續寫入多個ANSI或UNICODE字符。
FillConsoleOutputAttribute 向屏幕緩沖中連續寫入多個包含文本屬性的字符。
ReadConsoleOutput 從屏幕緩沖中讀取指定區域的字符(包含文本屬性)。
WriteConsoleOutput 向屏幕緩沖中寫入指定區域的字符(包含文本屬性)。

其中包含文本屬性的字符包括要顯示的字符、前景色、背景色等。上面函數中讀取指定區域的函數,把屏幕緩沖、源緩沖、目標緩沖當成是由CHAR_INFO結構構成的二維數組,區域以字符寬度為單位長度,區域使用SMALL_RECT結構體標記。

五、控制台其他屬性

1. Code Page

 Code Page指的是從256個字符碼到不同字符的映射。不同的Code Page包含不同的特殊字符,這些字符可能被一種或多種語言共享。

和控制台相關的Code主要有兩類:用於輸入的和用於輸出的。控制台使用輸入Code Page將鍵盤輸入映射成相關的字符,並使用輸出Code Page將輸出字符轉化為控制台上可顯示的數據。應用程序可使用SetConsoleCPGetConsoleCP函數來設置並獲取控制台的輸入Code Page,使用 SetConsoleOutputCPGetConsoleOutputCP函數來設置獲取控制台的輸出Code Page。

當前主機可用的Code Page保存在如下注冊表中:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage。

2.  控制處理機制

當控制台收到CTRL+CCTRL+BREAKCTRL+CLOSE信號時會自動調用默認控制處理函數,通常該控制處理函數會直接動用ExitProcess退出當前進程。如果關心這些事件,可以使用SetConsoleCtrlHandler函數添加或者移除額外的HandlerRoutine回調函數,來完成用戶自定義的額外處理。

當進程收到對應的控制信號,會按照注冊事件相反的次序依次調用回調函數,直到其中某個控制回調函數返回TRUE(表示事件已經處理)為止。如果所有控制回調函數都未返回TRUE,則調用默認的控制處理函數。

CTRL+C、CTRL+BREAK信號

CTRL+C、CTRL+BREAK組合鍵會被系統特殊處理。當某個控制台在獲取輸入焦點之后,CTRL+C、CTRL+BREAK不會被當做鍵盤輸入,而是作為終止信號(SIGINT、SIGBREAK),這個信號會傳遞給所有與控制關聯的進程。

CTRL+C組合鍵可以通過以下方式修改控制台的響應處理:

  • 使用函數SetConsoleMode禁用ENABLE_PROCESSED_INPUT輸入模式,可以讓系統把CTRL+C作為鍵盤輸入而不是終止信號。
  • 使用SetConsoleCtrlHandler 函數可讓程序忽略CTRL+C的終止信號。

CTRL+CLOSE信號

用戶關閉控制台時,所有與控制台關聯的進程均會收到CTRL+CLOSE信號,應用程序可以在程序退出前釋放相關資源。當應用程序收到CTRL+CLOSE信號,在執行完相應的資源釋放后可以做如下三種處理:

  • 調用ExitProcess退出當前進程
  • 返回FALSE,讓系統調用默認的控制處理函數,退出當前進程。
  • 返回TRUE,屏蔽其他回調處理函數,並退出當前進程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM