控制檯程式設計相關API 及相關程式設計
1. 編寫控制檯程式,獲得標準輸入輸出的視窗控制代碼
HANDLE GetStdHandle
(
DWORD nStdHandle
);
GetStdHandle()返回標準的輸入、輸出或錯誤的裝置的控制代碼,也就是獲得輸入、輸出 /錯誤的螢幕緩衝區的控制代碼。
其引數nStdHandle的值為下面幾種型別的一種:
STD_INPUT_HANDLE 標準輸入的控制代碼
STD_OUTPUT_HANDLE 標準輸出的控制代碼
STD_ERROR_HANDLE 標準錯誤的控制代碼
2
2. SetConsoleTextAttribute是設定控制檯字型顏色和背景色。
BOOL SetConsoleTextAttribute(
HANDLE hConsoleOutput, // 使用GetStdHandle取得的控制代碼
WORD wAttributes // 設定文字、背景色
);
wAttributes可以取下面的值
FOREGROUND_BLUE Text color contains blue.
FOREGROUND_GREEN Text color contains green.
FOREGROUND_RED Text color contains red.
FOREGROUND_INTENSITY Text color is intensified.
BACKGROUND_BLUE Background color contains blue.
BACKGROUND_GREEN Background color contains green.
BACKGROUND_RED Background color contains red.
BACKGROUND_INTENSITY Background color is intensified.
COMMON_LVB_LEADING_BYTE Leading byte.
COMMON_LVB_TRAILING_BYTE Trailing byte.
COMMON_LVB_GRID_HORIZONTAL Top horizontal.
COMMON_LVB_GRID_LVERTICAL Left vertical.
COMMON_LVB_GRID_RVERTICAL Right vertical.
COMMON_LVB_REVERSE_VIDEO Reverse foreground and background attributes.
COMMON_LVB_UNDERSCORE Underscore.
3. CONSOLE_CURSOR_INFO用於存放控制游標資訊
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize;
BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
dwSize:游標大小,1-100的值
bVisible:游標是否可見
4. SetConsoleCursorInfo設定游標的資訊
BOOL SetConsoleCursorInfo(
HANDLE hConsoleOutput, // 使用GetStdHandle取得的控制代碼
CONST CONSOLE_CURSOR_INFO *lpConsoleCursorInfo // 游標資訊
);
5. _CONSOLE_SCREEN_BUFFER_INFO: contains information about a console screen buffer
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize; // Size, in character columns and rows, of the screen buffer.
COORD dwCursorPosition; // 當前游標在螢幕的位置
WORD wAttributes; // 控制檯相當於顯示屏的座標,左上和右下的座標
SMALL_RECT srWindow; // 字元背景屬性,同SetConsoleTextAttribute
COORD dwMaximumWindowSize; // 控制檯視窗的大小
} CONSOLE_SCREEN_BUFFER_INFO ;
6. GetConsoleScreenBufferInfo取得控制檯螢幕資訊
BOOL GetConsoleScreenBufferInfo(
HANDLE hConsoleOutput, // GetStdHandle取得的控制代碼
PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo // screen buffer information
);
7. SetConsoleCursorPosition設定游標位置
BOOL SetConsoleCursorPosition(
HANDLE hConsoleOutput, // handle to screen buffer
COORD dwCursorPosition // new cursor coordinates
);
8. WriteConsoleOutput直接寫輸入緩衝區。可以寫入鍵盤、滑鼠等任何Console能夠接收的事件(Event)都可以寫進去。鍵盤操作如果是一些可顯示字元可以直接寫 ASCII字元,否則的話可能要用鍵盤掃描碼(scan code)了
BOOL WriteConsoleOutput(
HANDLE hConsoleOutput, // handle to screen buffer
CONST CHAR_INFO *lpBuffer, // data buffer
COORD dwBufferSize, // size of data buffer
COORD dwBufferCoord, // cell coordinates
PSMALL_RECT lpWriteRegion // rectangle to write
);
9. SetConsoleTitle設定控制檯的標題
BOOL SetConsoleTitle(
LPCTSTR lpConsoleTitle // new console title
);
文字介面的控制檯應用程式開發是深入學習C++、掌握互動系統的實現方法的最簡單的一種手段。然而,Visual C++的C++專用庫卻沒有TC所支援的文字(字元)螢幕控制函式,為此本系列文章從一般控制步驟、控制檯視窗操作、文字(字元)控制、滾動和移動、游標、鍵盤和滑鼠等幾個方面討論控制檯視窗介面的程式設計控制方法。
在眾多C++開發工具中,由於Microsoft本身的獨特優勢,選用Visual C++已越來越被眾多學習者所接受。顯然,現今如果還再把TC作為開發環境的話,不僅沒有必要,而且也不利於向Windows應用程式開發的過渡。然而,Visual C++的C++專用庫卻沒有TC所支援的文字螢幕(控制檯視窗)控制函式(相應的標頭檔案是conio.h)。這必然給C++學習者在文字介面設計和程式設計上帶來諸多不便。要知道,文字介面設計是一種深入學習C++、掌握互動系統的實現方法的最簡單的一種手段,它不像C++的Windows圖形介面應用程式,涉及知識過多。為此,本系列文章來討論在Visual
C++ 6.0開發環境中,如何編寫具有美觀清晰的控制檯視窗介面的C++應用程式。
一、概述
所謂控制檯應用程式,就是指那些需要與傳統DOS作業系統保持某種程式的相容,同時又不需要為使用者提供完善介面的程式。簡單地講,就是指在Windows環境下執行的DOS程式(#add
命令列程式 ,而非正DOS程式)。一旦C++控制檯應用程式在Windows 9x/NT/2000作業系統中執行後,就會彈出一個視窗。例如下列過程:
單擊Visual C++標準工具欄上的“New Text File”按鈕,開啟一個新的文件視窗。
選擇File | Save選單或按快捷鍵Ctrl+S或單擊標準工具欄的Save按鈕,彈出“儲存為”檔案對話方塊。將檔名為“Hello.cpp” (注意副檔名.cpp不能省略)。
在文件視窗中輸入下列程式碼:
#include "stdio.h" } |
單擊小型編譯工具欄中的“Build”按鈕或按F7鍵,系統出現一個對話方塊,詢問是否將此專案的工作資料夾設定原始檔所在的資料夾,單擊[是]按鈕,系統開始編譯。
單擊小型編譯工具欄中的“Execute Program”按鈕或按Ctrl+F5鍵,執行剛才的程式。
程式執行後,彈出下圖的視窗。
這就是控制檯視窗,與傳統的DOS螢幕視窗相比最主要的區別有:
(1) 預設的控制檯視窗有系統選單和標題,它是一個記憶體緩衝區視窗,緩衝區大小取決於Windows作業系統的分配;而DOS螢幕是一種物理視窗,不具有Windows視窗特性,其大小取決於ROM BIOS分配的記憶體空間。
(2) 控制檯視窗的文字操作是呼叫低層的Win32 APIs,而DOS螢幕的文字操作是通過呼叫BIOS的16(10h)中斷而實現的。
(3) 預設的控制檯視窗可以接收鍵盤和滑鼠的輸入資訊,裝置驅動由Windows管理,而DOS螢幕視窗接收滑鼠時需要呼叫33h中斷,且滑鼠裝置驅動程式由自己安裝。
二、控制檯文字視窗的一般控制步驟
在Visual C++ 6.0中,控制檯視窗介面的一般程式設計控制步驟如下:
1,呼叫GetStdHandle獲取當前的標準輸入(STDIN)和標準輸出(STDOUT)裝置控制代碼。函式原型為:
HANDLE GetStdHandle( DWORD nStdHandle );
其中,nStdHandle可以是STD_INPUT_HANDLE(標準輸入裝置控制代碼)、STD_OUTPUT_HANDLE(標準輸出裝置控制代碼)和STD_ERROR_HANDLE(標準錯誤控制代碼)。需要說明的是,“控制代碼”是Windows最常用的概念。它通常用來標識Windows資源(如選單、圖示、視窗等)和裝置等物件。雖然可以把控制代碼理解為是一個指標變數型別,但它不是物件所在的地址指標,而是作為Windows系統內部表的索引值來使用的。
2,呼叫相關文字介面控制的API函式。這些函式可分為三類。
一是用於控制檯視窗控制的函式(包括視窗的緩衝區大小、視窗前景字元和背景顏色、視窗標題、大小和位置等);
二是用於控制檯輸入輸出的函式(包括字元屬性操作函式);
其他的函式併為最後一類。
3,呼叫CloseHandle()來關閉輸入輸出控制代碼。
注意,在程式中還必須包含標頭檔案windows.h。下面看一個程式:
#include "windows.h"
void main() |
程式中,COORD和CONSOLE_SCREEN_BUFFER_ INFO是wincon.h定義的控制檯結構體型別,其原型如下:
// 座標結構體 typedef struct _COORD { SHORT X; SHORT Y; } COORD; // 控制檯視窗資訊結構體 typedef struct _CONSOLE_SCREEN_BUFFER_INFO { COORD dwSize; // 緩衝區大小 COORD dwCursorPosition; // 當前游標位置 WORD wAttributes; // 字元屬性 SMALL_RECT srWindow; // 當前視窗顯示的大小和位置 COORD dwMaximumWindowSize; // 最大的視窗緩衝區大小 } CONSOLE_SCREEN_BUFFER_INFO ; |
還需要說明的是,雖然在C++中,iostream.h定義了cin和cout的標準輸入和輸出流物件。但它們只能實現基本的輸入輸出操作,對於控制檯視窗介面的控制卻無能為力,而且不能與stdio.h和conio.h友好相處,因為iostream.h和它們是C++兩套不同的輸入輸出操作方式,使用時要特別注意。
三、控制檯視窗操作
用於控制檯視窗操作的API函式如下:
GetConsoleScreenBufferInfo 獲取控制檯視窗資訊 GetConsoleTitle 獲取控制檯視窗標題 ScrollConsoleScreenBuffer 在緩衝區中移動資料塊 SetConsoleScreenBufferSize 更改指定緩衝區大小 SetConsoleTitle 設定控制檯視窗標題 SetConsoleWindowInfo 設定控制檯視窗資訊 |
此外,還有視窗字型、顯示模式等控制函式,這裡不再細說。下列舉一個示例,程式如下:
#include "windows.h"
void main() |
需要說明的是,控制檯視窗的原點座標是(0, 0),而最大的座標是緩衝區大小減1,例如當緩衝區大小為80*25時,其最大的座標是(79, 24)。
四、文字屬性操作
與DOS字元相似,控制檯視窗中的字元也有相應的屬性。這些屬性分為:文字的前景色、背景色和雙位元組字符集(DBCS)屬性三種。事實上,我們最關心是文字顏色,這樣可以構造出美觀的介面。顏色屬性都是一些預定義標識:
FOREGROUND_BLUE 藍色 FOREGROUND_GREEN 綠色 FOREGROUND_RED 紅色 FOREGROUND_INTENSITY 加強 BACKGROUND_BLUE 藍色背景 BACKGROUND_GREEN 綠色背景 BACKGROUND_RED 紅色背景 BACKGROUND_INTENSITY 背景色加強 COMMON_LVB_REVERSE_VIDEO 反色 |
與文字屬性相關的主要函式有:
BOOL FillConsoleOutputAttribute( // 填充字元屬性 HANDLE hConsoleOutput, // 控制代碼 WORD wAttribute, // 文字屬性 DWORD nLength, // 個數 COORD dwWriteCoord, // 開始位置 LPDWORD lpNumberOfAttrsWritten // 返回填充的個數 ); BOOL SetConsoleTextAttribute( // 設定WriteConsole等函式的字元屬性 HANDLE hConsoleOutput, // 控制代碼 WORD wAttributes // 文字屬性 ); BOOL WriteConsoleOutputAttribute( // 在指定位置處寫屬性 HANDLE hConsoleOutput, // 控制代碼 CONST WORD *lpAttribute, // 屬性 DWORD nLength, // 個數 COORD dwWriteCoord, // 起始位置 LPDWORD lpNumberOfAttrsWritten // 已寫個數 ); |
另外,獲取當前控制檯視窗的文字屬性是通過呼叫函式GetConsoleScreenBufferInfo後,在CONSOLE_SCREEN_ BUFFER_INFO結構成員wAttributes中得到。
五、文字輸出
文字輸出函式有:
BOOL FillConsoleOutputCharacter( // 填充指定資料的字元 HANDLE hConsoleOutput, // 控制代碼 TCHAR cCharacter, // 字元 DWORD nLength, // 字元個數 COORD dwWriteCoord, // 起始位置 LPDWORD lpNumberOfCharsWritten // 已寫個數 ); BOOL WriteConsole( // 在當前游標位置處插入指定數量的字元 HANDLE hConsoleOutput, // 控制代碼 CONST VOID *lpBuffer, // 字串 DWORD nNumberOfCharsToWrite, // 字元個數 LPDWORD lpNumberOfCharsWritten, // 已寫個數 LPVOID lpReserved // 保留 ); BOOL WriteConsoleOutput( // 向指定區域寫帶屬性的字元 HANDLE hConsoleOutput, // 控制代碼 CONST CHAR_INFO *lpBuffer, // 字元資料區 COORD dwBufferSize, // 資料區大小 COORD dwBufferCoord, // 起始座標 PSMALL_RECT lpWriteRegion // 要寫的區域 ); BOOL WriteConsoleOutputCharacter( // 在指定位置處插入指定數量的字元 HANDLE hConsoleOutput, // 控制代碼 LPCTSTR lpCharacter, // 字串 DWORD nLength, // 字元個數 COORD dwWriteCoord, // 起始位置 LPDWORD lpNumberOfCharsWritten // 已寫個數 ); |
可以看出:WriteConsoleOutput函式功能相當於SetConsoleTextAttribute和WriteConsole的功能。而WriteConsoleOutputCharacter函式相當於SetConsoleCursorPosition(設定游標位置)和WriteConsole的功能。不過在具體使用要注意它們的區別。
六、文字操作示例
下面看一個示例程式:
#include "windows.h"
void ShadowWindowLine(char *str)
void DrawBox(bool bSingle, SMALL_RECT rc) |
程式執行結果如下圖所示。(#add 要滾動到下邊,否則看不到)
需要說明的是,上述程式在不同的字元程式碼頁面(code page)下顯示的結果是不同的。例如,中文Windows作業系統的預設內碼表是簡體中文(936),在該程式碼頁面下值超過128的單字元在Windows NT/XP是顯示不出來的。下表列出了可以使用的內碼表。
內碼表(Code page) | 說 明 |
1258 | 越南文 |
1257 | 波羅的海文 |
1256 | 阿拉伯文 |
1255 | 希伯來文 |
1254 | 土耳其語 |
1253 | 希臘文 |
1252 | 拉丁文(ANSI) |
1251 | 斯拉夫文 |
1250 | 中歐文 |
950 | 繁體中文 |
949 | 韓文 |
936 | 簡體中文 |
932 | 日文 |
874 | 泰文 |
850 | 使用多種語言(MS-DOS拉丁文) |
437 | MS-DOS美語/英語 |
七、滾動和移動
ScrollConsoleScreenBuffer是實現文字區滾動和移動的API函式。它可以將指定的一塊文字區域移動到另一個區域,被移空的那塊區域由指定字元填充。函式的原型如下:
BOOL ScrollConsoleScreenBuffer( HANDLE hConsoleOutput, // 控制代碼 CONST SMALL_RECT* lpScrollRectangle, // 要滾動或移動的區域 CONST SMALL_RECT* lpClipRectangle, // 裁剪區域 COORD dwDestinationOrigin, // 新的位置 CONST CHAR_INFO* lpFill // 填充字元 ); |
利用這個API函式還可以實現刪除指定行的操作。下面來舉一個例子,程式如下:
#include #include #include HANDLE hOut; void DeleteLine(int row); // 刪除一行 void MoveText(int x, int y, SMALL_RECT rc); // 移動文字塊區域 void ClearScreen(void); // 清屏 void main() { hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取標準輸出裝置控制代碼 WORD att = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_BLUE ; // 背景是藍色,文字顏色是黃色 SetConsoleTextAttribute(hOut, att); ClearScreen(); printf("\n\nThe soul selects her own society,\n"); printf("Then shuts the door;\n"); printf("On her devine majority;\n"); printf("Obtrude no more.\n\n"); CONSOLE_SCREEN_BUFFER_INFO bInfo; GetConsoleScreenBufferInfo( hOut, &bInfo ); COORD endPos = {0, bInfo.dwSize.Y - 1}; SetConsoleCursorPosition(hOut, endPos); // 設定游標位置 SMALL_RECT rc = {0, 2, 40, 5}; _getch(); MoveText(10, 5, rc); _getch(); DeleteLine(5); CloseHandle(hOut); // 關閉標準輸出裝置控制代碼 } void DeleteLine(int row) { SMALL_RECT rcScroll, rcClip; COORD crDest = {0, row - 1}; CHAR_INFO chFill; CONSOLE_SCREEN_BUFFER_INFO bInfo; GetConsoleScreenBufferInfo( hOut, &bInfo ); rcScroll.Left = 0; rcScroll.Top = row; rcScroll.Right = bInfo.dwSize.X - 1; rcScroll.Bottom = bInfo.dwSize.Y - 1; rcClip = rcScroll; chFill.Attributes = bInfo.wAttributes; chFill.Char.AsciiChar = ' '; ScrollConsoleScreenBuffer(hOut, &rcScroll, &rcClip, crDest, &chFill); } void MoveText(int x, int y, SMALL_RECT rc) { COORD crDest = {x, y}; CHAR_INFO chFill; CONSOLE_SCREEN_BUFFER_INFO bInfo; GetConsoleScreenBufferInfo( hOut, &bInfo ); chFill.Attributes = bInfo.wAttributes; chFill.Char.AsciiChar = ' '; ScrollConsoleScreenBuffer(hOut, &rc, NULL, crDest, &chFill); } void ClearScreen(void) { CONSOLE_SCREEN_BUFFER_INFO bInfo; GetConsoleScreenBufferInfo( hOut, &bInfo ); COORD home = {0, 0}; WORD att = bInfo.wAttributes; |