控制台輸入輸出機制實例


本文是針對 控制台輸入輸出機制 一文的實例說明。相關理論內容建議參考之。

實例a 控制台高層輸入輸出接口實例

本實例首先使用控制台默認輸入輸出模式,調用ReadFile和WriteFile函數,用於說明用於控制台的字符串輸入輸出;之后修改控制台輸入模式,關閉行輸入模式和回顯輸入模式,重復使用ReadFile和WriteFile函數。最后再程序退出時恢復控制台默認的輸入輸出模式及字符顏色。

代碼中使用NewLine函數在行輸入模式禁用情況下模擬換行處理,即將控制台屏幕緩沖的光標移動到下一行開始位置。

代碼如下:

// 控制台高層輸入輸出接口實例,HighLevelIoFuncDemo
// 1. WriteFile輸出字符串,ReadFile讀取字符串
// 2. 關閉行輸入和回顯輸入之后的字符輸入輸出處理
// 3. 手工實現換行及滾屏處理
// 建議使用vs2005以上版本編譯
#include <windows.h> 

void NewLine(void); 
void ScrollScreenBuffer(HANDLE, INT); 

HANDLE hStdout, hStdin; 
CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 

int main(void) 
{ 
    LPSTR lpszPrompt1 = "Type a line and press Enter, or q to quit: ";
    LPSTR lpszPrompt2 = "Type any key, or q to quit: ";
    CHAR chBuffer[256]; 
    DWORD cRead, cWritten, fdwMode, fdwOldMode; 
    WORD wOldColorAttrs; 

    // Get handles to STDIN and STDOUT. 
    hStdin = GetStdHandle(STD_INPUT_HANDLE); 
    hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
    if (hStdin == INVALID_HANDLE_VALUE || 
        hStdout == INVALID_HANDLE_VALUE) 
    {
        MessageBox(NULL, TEXT("GetStdHandle"), TEXT("Console Error"), 
            MB_OK);
        return 1;
    }

    // Save the current text colors. 
    if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) 
    {
        MessageBox(NULL, TEXT("GetConsoleScreenBufferInfo"), 
            TEXT("Console Error"), MB_OK); 
        return 1;
    }

    wOldColorAttrs = csbiInfo.wAttributes; 

    // Set the text attributes to draw red text on black background. 
    if (! SetConsoleTextAttribute(hStdout, FOREGROUND_RED | 
        FOREGROUND_INTENSITY))
    {
        MessageBox(NULL, TEXT("SetConsoleTextAttribute"), 
            TEXT("Console Error"), MB_OK);
        return 1;
    }

    // Write to STDOUT and read from STDIN by using the default 
    // modes. Input is echoed automatically, and ReadFile 
    // does not return until a carriage return is typed. 
    // 
    // The default input modes are line, processed, and echo. 
    // The default output modes are processed and wrap at EOL.
    while (1) 
    { 
        if (! WriteFile( 
            hStdout,               // output handle 
            lpszPrompt1,           // prompt string 
            lstrlenA(lpszPrompt1), // string length 
            &cWritten,             // bytes written 
            NULL) )                // not overlapped 
        {
            MessageBox(NULL, TEXT("WriteFile"), TEXT("Console Error"), 
                MB_OK); 
            return 1;
        }

        if (! ReadFile( 
            hStdin,    // input handle 
            chBuffer,  // buffer to read into 
            255,       // size of buffer 
            &cRead,    // actual bytes read 
            NULL) )    // not overlapped 
            break; 
        if (chBuffer[0] == 'q') break; 
    } 

    // Turn off the line input and echo input modes 
    if (! GetConsoleMode(hStdin, &fdwOldMode)) 
    {
        MessageBox(NULL, TEXT("GetConsoleMode"), TEXT("Console Error"),
            MB_OK); 
        return 1;
    }

    fdwMode = fdwOldMode & 
        ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT); 
    if (! SetConsoleMode(hStdin, fdwMode)) 
    {
        MessageBox(NULL, TEXT("SetConsoleMode"), TEXT("Console Error"),
            MB_OK); 
        return 1;
    }

    // ReadFile returns when any input is available.  
    // WriteFile is used to echo input. 
    NewLine();

    while (1) 
    { 
        if (! WriteFile( 
            hStdout,               // output handle 
            lpszPrompt2,           // prompt string 
            lstrlenA(lpszPrompt2), // string length 
            &cWritten,             // bytes written 
            NULL) )                // not overlapped 
        {
            MessageBox(NULL, TEXT("WriteFile"), TEXT("Console Error"), 
                MB_OK);
            return 1;
        }

        if (! ReadFile(hStdin, chBuffer, 1, &cRead, NULL)) 
            break; 
        if (chBuffer[0] == '\r')
            NewLine();
        else if (! WriteFile(hStdout, chBuffer, cRead, 
            &cWritten, NULL)) break;
        else
            NewLine();
        if (chBuffer[0] == 'q') break; 
    } 

    // Restore the original console mode. 
    SetConsoleMode(hStdin, fdwOldMode);

    // Restore the original text colors. 
    SetConsoleTextAttribute(hStdout, wOldColorAttrs);

    return 0;
}

// The NewLine function handles carriage returns when the processed 
// input mode is disabled. It gets the current cursor position 
// and resets it to the first cell of the next row. 
void NewLine(void) 
{ 
    if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) 
    {
        MessageBox(NULL, TEXT("GetConsoleScreenBufferInfo"), 
            TEXT("Console Error"), MB_OK); 
        return;
    }

    csbiInfo.dwCursorPosition.X = 0; 

    // If it is the last line in the screen buffer, scroll 
    // the buffer up. 
    if ((csbiInfo.dwSize.Y-1) == csbiInfo.dwCursorPosition.Y) 
    { 
        ScrollScreenBuffer(hStdout, 1); 
    } 

    // Otherwise, advance the cursor to the next line.
    else csbiInfo.dwCursorPosition.Y += 1; 

    if (! SetConsoleCursorPosition(hStdout, 
        csbiInfo.dwCursorPosition)) 
    {
        MessageBox(NULL, TEXT("SetConsoleCursorPosition"), 
            TEXT("Console Error"), MB_OK); 
        return;
    }
} 

void ScrollScreenBuffer(HANDLE h, INT x)
{
    SMALL_RECT srctScrollRect, srctClipRect;
    CHAR_INFO chiFill;
    COORD coordDest;

    srctScrollRect.Left = 0;
    srctScrollRect.Top = 1;
    srctScrollRect.Right = csbiInfo.dwSize.X - (SHORT)x; 
    srctScrollRect.Bottom = csbiInfo.dwSize.Y - (SHORT)x; 

    // The destination for the scroll rectangle is one row up. 
    coordDest.X = 0; 
    coordDest.Y = 0; 

    // The clipping rectangle is the same as the scrolling rectangle. 
    // The destination row is left unchanged. 
    srctClipRect = srctScrollRect; 

    // Set the fill character and attributes. 
    chiFill.Attributes = FOREGROUND_RED|FOREGROUND_INTENSITY; 
    chiFill.Char.AsciiChar = (char)' '; 

    // Scroll up one line. 
    ScrollConsoleScreenBuffer( 
        h,               // screen buffer handle 
        &srctScrollRect, // scrolling rectangle 
        &srctClipRect,   // clipping rectangle 
        coordDest,       // top left destination cell 
        &chiFill);       // fill character and color 
}
View Code

實際代碼摘自Using the High-Level Input and Output Functions

至於ScrollScreenBuffer使用可參考實例c中代碼解釋。

如果想實現直接輸入不會顯的模式,可以參考下上述代碼。

從上面處理效果上來看,c/c++的標准輸入輸出接口基本是依賴類似處理方式實現的。

實例b 屏幕緩沖切換

前文中提及一個控制台可以有多個屏幕緩沖,這里介紹下如何為控制台創建多個屏幕緩沖,並實現不同屏幕緩沖之間的切換,同時介紹如何使用控制台輸出函數顯示UNICODE字符。實例中主要使用CreateConsoleScreenBuffer函數創建新的屏幕緩沖,使用SetConsoleActiveScreenBuffer函數切換活動屏幕緩沖,並調用WriteConsole輸出UNICODE字符。

參考代碼如下: 

// 屏幕緩沖創建、切換,ScreenBufferSwitchDemo
// 1. 創建新的屏幕緩沖,並切換
// 2. 控制台函數輸出Unicode字符
// 建議使用vs2005以上版本編譯 unicode編碼
#include <windows.h> 
#include <iostream>
using std::wcout;
using std::endl;

int _tmain(int argc, _TCHAR* argv[])
{
    // 輸出中文
    std::wcout.imbue(std::locale("chs"));

    // 設置控制台標題欄
    SetConsoleTitle(_T("ScreenBufferSwitchDemo"));

    HANDLE stdOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    HANDLE newConsoleHandle = CreateConsoleScreenBuffer(
        GENERIC_READ | GENERIC_WRITE, // 訪問權限
        FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享控制
        NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
    if (INVALID_HANDLE_VALUE == newConsoleHandle)
    {
        wcout << TEXT("創建屏幕緩沖失敗,錯誤碼") << GetLastError() << endl;
        return -1;
    }

    TCHAR arrStdText[] = TEXT("test screen buffer creation\n");
    DWORD writeCharNumbers = 0;
    WriteConsole(stdOutHandle, arrStdText, _tcslen(arrStdText), &writeCharNumbers, NULL);

    TCHAR arrNewText[] = TEXT("測試控制台緩沖創建\n");
    WriteConsole(newConsoleHandle, arrNewText, _tcslen(arrNewText), &writeCharNumbers, NULL);

    Sleep(2000);

    // 切換活動屏幕緩沖
    if (!SetConsoleActiveScreenBuffer(newConsoleHandle))
    {
        wcout << TEXT("切換屏幕緩沖失敗,錯誤碼") << GetLastError() << endl;
    }
    wcout << TEXT("正在使用新創建的屏幕緩沖") << endl;

    Sleep(2000);

    // 恢復默認的標准屏幕緩沖
    if (!SetConsoleActiveScreenBuffer(stdOutHandle))
    {
        wcout << TEXT("切換屏幕緩沖失敗,錯誤碼") << GetLastError() << endl;
    }
    wcout << TEXT("正在使用標准屏幕緩沖") << endl;

    // 注意 c/c++標准輸入、輸出、標准錯誤是和STD_OUTPUT_HANDLE關聯的

    CloseHandle(newConsoleHandle);

    return 0;
}
View Code

特別說明下,c/c++中的標准輸入、標准輸出、標准錯誤都是和控制台的默認STD_INPUT_HANDLE、STD_OUTPUT_HANDLE、STD_ERROR_HANDLE相關聯的,如果需要使用c/c++提供的函數做輸入輸出到新的屏幕緩沖,需要做重定向。

實例c 屏幕緩沖滾動

本實例介紹控制台滾動效果的實現,滾動屏幕窗口或者滾動屏幕緩沖。

多數情況下,控制台屏幕緩沖的字符數目遠大於控制體窗口顯示的字符數目。在通常情況是,我們在控制台輸出輸出數據產生的滾動條,或者窗口字符翻頁是使用移動控制台屏幕緩沖的窗口實現。在屏幕緩沖窗口的位置移動到屏幕緩沖下邊緣時才會有屏幕緩沖的實際數據移動(也就是丟掉第一行數據,后續數據向上滾動一行)。如下面兩個圖說是:

屏幕緩沖正常輸入數據時的窗口位置移動

Screen buffer window

屏幕緩沖滿的情況下,數據丟棄處理。

Screen buffer window

關於屏幕緩沖窗口移動的實例代碼可參考,控制台基礎概念實例中的實例a,調用SetConsoleWindowInfo函數。

下面代碼使用ScrollConsoleScreenBuffer函數用於滾動屏幕緩沖,用於移除屏幕緩沖中一些數據,並填充新的數據。

參考代碼如下:

// 屏幕緩沖滾動,ScreenBufferScrollDemo
// 模擬屏幕緩沖滿的情況下,自動丟棄第一行數據,后續上移一行
// 建議使用vs2005以上版本編譯 unicode編碼
#include <windows.h> 
#include <iostream>
using std::wcout;
using std::endl;

int _tmain(int argc, _TCHAR* argv[])
{
    // 輸出中文
    std::wcout.imbue(std::locale("chs"));

    // 設置控制台標題欄
    SetConsoleTitle(_T("ScreenBufferScrollDemo"));

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hStdout == INVALID_HANDLE_VALUE) 
    {
        wcout << TEXT("GetStdHandle failed with ") << GetLastError() << endl; 
        return 1;
    }

    CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 
    // Get the screen buffer size.
    if (!GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) 
    {
        wcout << TEXT("GetConsoleScreenBufferInfo failed ") << GetLastError() << endl; 
        return 1;
    }

    SMALL_RECT srctScrollRect, srctClipRect; 
    COORD coordDest; 
    // The scrolling rectangle is the bottom 15 rows of the screen buffer.
    // 建議驗證下,這個區域大小很大,在我的機器上是300行x80列
    srctScrollRect.Top = csbiInfo.dwSize.Y - 16; 
    srctScrollRect.Bottom = csbiInfo.dwSize.Y - 1; 
    srctScrollRect.Left = 0; 
    srctScrollRect.Right = csbiInfo.dwSize.X - 1; 

    // The destination for the scroll rectangle is one row up.
    coordDest.X = 0; 
    coordDest.Y = csbiInfo.dwSize.Y - 17; 

    // The clipping rectangle is the same as the scrolling rectangle. 
    // The destination row is left unchanged.
    srctClipRect = srctScrollRect; 

    // Fill the bottom row with green blanks. 
    CHAR_INFO chiFill; 
    chiFill.Attributes = BACKGROUND_GREEN | FOREGROUND_RED; 
    chiFill.Char.UnicodeChar = 'a'; 

    // Scroll up one line. 
    if(!ScrollConsoleScreenBuffer(  
        hStdout,         // screen buffer handle 
        &srctScrollRect, // scrolling rectangle 
        &srctClipRect,   // clipping rectangle 
        coordDest,       // top left destination cell 
        &chiFill))       // fill character and color
    {
        printf("ScrollConsoleScreenBuffer failed %d\n", GetLastError()); 
        return 1;
    }

    // 如果你在程序運行完了都沒看到效果,建議嘗試下如下方法
    // 在控制台窗口拉動滾動條,直到最下面,看看有無綠底紅色的一行a字符
    return 0;
}
View Code

上述實例的效果可能位置有點問題,有興趣的可以按照上面代碼中的說明嘗試下。

實例d 按區域讀寫屏幕緩沖

使用ReadConsoleOutput函數從屏幕緩沖中讀取指定區域的字符數據,然后使用WriteConsoleOutput函數輸出到屏幕緩沖的指定區域。

本實例先創建了一個新的屏幕緩沖,然后從默認屏幕緩沖中讀取數據,寫到新創建的屏幕緩沖中。這里重點介紹按區域讀寫屏幕緩沖的調用方式。創建屏幕緩沖可參考實例b的介紹。

代碼如下: 

// 按區域讀寫屏幕緩沖,ScreenBufferBlockDemo
// 從指定區域讀取屏幕緩沖,然后將其輸出到另一個屏幕緩沖上
// 建議使用vs2005以上版本編譯 unicode編碼
#include <windows.h> 
#include <iostream>
using std::wcout;
using std::endl;

int _tmain(int argc, _TCHAR* argv[])
{
    // 輸出中文
    std::wcout.imbue(std::locale("chs"));

    // 設置控制台標題欄
    SetConsoleTitle(TEXT("ScreenBufferBlockDemo"));

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
    HANDLE hNewScreenBuffer = CreateConsoleScreenBuffer( 
        GENERIC_READ |           // read/write access 
        GENERIC_WRITE, 
        FILE_SHARE_READ | 
        FILE_SHARE_WRITE,        // shared 
        NULL,                    // default security attributes 
        CONSOLE_TEXTMODE_BUFFER, // must be TEXTMODE 
        NULL);                   // reserved; must be NULL 
    if (hStdout == INVALID_HANDLE_VALUE || 
        hNewScreenBuffer == INVALID_HANDLE_VALUE) 
    {
        wcout << TEXT("CreateConsoleScreenBuffer failed - ") << GetLastError() << endl; 
        return 1;
    }

    // 設置標准緩沖的輸出屬性,並輸出數據
    SetConsoleTextAttribute(hStdout, FOREGROUND_GREEN);
    for(int i = 1; i < 16; ++i)
    {
        for (int j = 0; j < i; ++j)
        {
            wcout << TEXT("A");
        }
        wcout << endl;
    }

    // Make the new screen buffer the active screen buffer. 
    if (!SetConsoleActiveScreenBuffer(hNewScreenBuffer) ) 
    {
        printf("SetConsoleActiveScreenBuffer failed - (%d)\n", GetLastError()); 
        return 1;
    }

    SMALL_RECT srctReadRect;
    // Set the source rectangle.
    srctReadRect.Top = 2;    // top left: row 2, col 0 
    srctReadRect.Left = 0; 
    srctReadRect.Bottom = 6; // bot. right: row 6, col 79 
    srctReadRect.Right = 79; 

    COORD coordBufSize; 
    COORD coordBufCoord; 
    // The temporary buffer size is 2 rows x 80 columns.
    coordBufSize.Y = 5; 
    coordBufSize.X = 80; 

    // The top left destination cell of the temporary buffer is 
    // row 0, col 0. 
    coordBufCoord.X = 0; 
    coordBufCoord.Y = 0; 

    CHAR_INFO chiBuffer[5*80]; // [5][80]; 
    
    BOOL fSuccess; 
    // Copy the block from the screen buffer to the temp. buffer. 
    fSuccess = ReadConsoleOutput( 
        hStdout,        // screen buffer to read from 
        chiBuffer,      // buffer to copy into 
        coordBufSize,   // col-row size of chiBuffer 
        coordBufCoord,  // top left dest. cell in chiBuffer 
        &srctReadRect); // screen buffer source rectangle 
    if (!fSuccess) 
    {
        wcout << TEXT("ReadConsoleOutput failed - ") << GetLastError() << endl; 
        return 1;
    }

    SMALL_RECT srctWriteRect; 
    // Set the destination rectangle. 
    srctWriteRect.Top = 3;    // top lt: row 3, col 0 
    srctWriteRect.Left = 0; 
    srctWriteRect.Bottom = 7; // bot. rt: row 7, col 79 
    srctWriteRect.Right = 79; 

    // Copy from the temporary buffer to the new screen buffer. 
    fSuccess = WriteConsoleOutput( 
        hNewScreenBuffer, // screen buffer to write to 
        chiBuffer,        // buffer to copy from 
        coordBufSize,     // col-row size of chiBuffer 
        coordBufCoord,    // top left src cell in chiBuffer 
        &srctWriteRect);  // dest. screen buffer rectangle 
    if (!fSuccess) 
    {
        wcout << TEXT("WriteConsoleOutput failed - ") << GetLastError() << endl;
        return 1;
    }
    Sleep(5000); 

    // Restore the original active screen buffer. 

    if (!SetConsoleActiveScreenBuffer(hStdout)) 
    {
        wcout << TEXT("SetConsoleActiveScreenBuffer failed - ") << GetLastError() << endl; 
        return 1;
    }

    CloseHandle(hNewScreenBuffer);
    
    return 0;
}
View Code

上述代碼實際上完成了從標准屏幕緩沖的(2,0)->(6,79)區域復制字符信息,然后將這些信息顯示到新創建的屏幕緩沖的(3,0)->(7,79)區域,最后恢復當前活動屏幕緩沖。

實例e 清空屏幕緩沖

windows下命令行提供了cls指令,可以用於清空控制台緩沖區,簡單的處理方法可以在main函數中調用 system("cls")即可。

下面代碼提供一種編程實現清空控制台屏幕緩沖的方法。

// 清空屏幕緩沖,ScreenBufferClearOperation
// 模擬命令行的cls命令,對控制台屏幕緩沖進行清空處理
// 建議使用vs2005以上版本編譯 unicode編碼
#include <windows.h> 

void cls( HANDLE hConsole )
{
    COORD coordScreen = { 0, 0 };    // home for the cursor 
    DWORD cCharsWritten;
    CONSOLE_SCREEN_BUFFER_INFO csbi; 
    DWORD dwConSize;

    // Get the number of character cells in the current buffer.
    if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
    {
        return;
    }

    dwConSize = csbi.dwSize.X * csbi.dwSize.Y;

    // Fill the entire screen with blanks.
    if( !FillConsoleOutputCharacter( 
        hConsole,        // Handle to console screen buffer 
        (TCHAR) ' ',     // Character to write to the buffer
        dwConSize,       // Number of cells to write 
        coordScreen,     // Coordinates of first cell 
        &cCharsWritten ))// Receive number of characters written
    {
        return;
    }

    // Get the current text attribute.
    if( !GetConsoleScreenBufferInfo( hConsole, &csbi ))
    {
        return;
    }

    // Set the buffer's attributes accordingly.
    if( !FillConsoleOutputAttribute( 
        hConsole,         // Handle to console screen buffer 
        csbi.wAttributes, // Character attributes to use
        dwConSize,        // Number of cells to set attribute 
        coordScreen,      // Coordinates of first cell 
        &cCharsWritten )) // Receive number of characters written
    {
        return;
    }

    // Put the cursor at its home coordinates.
    SetConsoleCursorPosition( hConsole, coordScreen );
}

int main( void )
{
    HANDLE hStdout;

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD dwWriteBytes = 0;
    WriteConsole(hStdout, TEXT("teststring\n"), 11, &dwWriteBytes,NULL);
    Sleep(5000);
    cls(hStdout);
    Sleep(1000);
    return 0;
}
View Code

實例f 底層屏幕緩沖輸出接口 

 本實例主要介紹控制台提供的底層屏幕緩沖輸出函數,主要用於讀取及保存字符串(連續逐行讀取),比如ReadConsoleOutputCharacterWriteConsoleOutputAttributeFillConsoleOutputCharacterFillConsoleOutputAttribute等。

實例中主要給出這幾個函數的使用方法。

代碼如下: 

// 讀寫屏幕緩沖底層接口,LowLevelScreenBufferIODemo
// 向屏幕緩沖讀寫字符串或字符數組
// 建議使用vs2005以上版本編譯 unicode編碼
#include <windows.h> 
#include <iostream>
using std::wcout;
using std::endl;

int _tmain(int argc, _TCHAR* argv[])
{
    // 輸出中文
    std::wcout.imbue(std::locale("chs"));

    // 設置控制台標題欄
    SetConsoleTitle(TEXT("LowLevelScreenBufferIODemo"));

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

    // 設置標准緩沖的輸出屬性,並輸出數據
    SetConsoleTextAttribute(hStdout, FOREGROUND_RED|BACKGROUND_GREEN);
    wcout << TEXT("abcdefg空山不見人,") << endl
        << TEXT("hijklmn但聞人語響。") << endl
        << TEXT("opqrst返景入深林,") << endl
        << TEXT("uvwxyz復照青苔上。") << endl;

    // 去掉背景色,用於區分后續輸出屬性字符是否正確拷貝了
    SetConsoleTextAttribute(hStdout, FOREGROUND_RED|FOREGROUND_GREEN);

    // 從屏幕緩沖(0,6)讀入10個UNICODE字符
    TCHAR readStr[10] = {0};
    DWORD readTextNumber = 10;
    COORD coordRead = {6,0};
    DWORD actualUsedCount = 0;
    if (!ReadConsoleOutputCharacter(hStdout,
        readStr, readTextNumber, coordRead, &actualUsedCount))
    {
        wcout << TEXT("ReadConsoleOutputCharacter failed with ") << GetLastError() << endl;
    }
    else
    {
        // 數據寫到屏幕緩沖的第6行起始位置
        COORD coordWrite = {0,5};
        if (!WriteConsoleOutputCharacter(hStdout,
            readStr, actualUsedCount, coordWrite, &actualUsedCount))
        {
            wcout << TEXT("WriteConsoleOutputCharacter failed with ") << GetLastError() << endl;
        }
    }

    // 這里僅讀取了屏幕緩沖的字符屬性
    WORD chiBuffer[10]; 
    coordRead.X = 5;
    coordRead.Y = 1;
    DWORD readCharInfoNumber = 10;
    if (!ReadConsoleOutputAttribute(hStdout, chiBuffer, readCharInfoNumber,
        coordRead, &actualUsedCount))
    {
        wcout << TEXT("ReadConsoleOutputAttribute failed with ") << GetLastError() << endl;
    }
    else
    {
        // 數據寫到第7行起始位置
        COORD coordWrite = {0,6};
        if (!WriteConsoleOutputAttribute(hStdout, chiBuffer, actualUsedCount,
            coordWrite, &actualUsedCount))
        {
            wcout << TEXT("WriteConsoleOutputAttribute failed with ") << GetLastError() << endl;
        }
    }

    // 在第7行起始位置填充中文"水"八次
    COORD coordFillChar = {0,6};
    if (!FillConsoleOutputCharacter(hStdout, 
        TEXT(''), 8*sizeof(TCHAR), coordFillChar, &actualUsedCount))
    {
        wcout << TEXT("FillConsoleOutputCharacter failed with ") << GetLastError() << endl;
    }
    Sleep(2000);
    FillConsoleOutputAttribute(hStdout, FOREGROUND_BLUE, 20, coordFillChar,
        &actualUsedCount);

    return 0;
}
View Code

由於作為測試代碼,這里在輸入后未移動屏幕緩沖的光標位置,而且不是連續輸出字符的。

實例g 輸入緩沖事件讀取

 函數ReadConsoleInput可直接訪問控制台的輸入緩沖中的事件,為了能夠接收鼠標、窗口改變等事件需要修改控制台的底層默認輸入模式,可使用函數SetConsoleMode修改。下面實例演示了如何處理控制台輸入緩沖中的事件(簡化期間,只處理100個)。

代碼如下: 

// 讀取控制台輸入緩沖事件,ReadProcessInputBufferDemo
// 簡單介紹如何處理輸入緩沖的事件
// 建議使用vs2005以上版本編譯 unicode編碼
#include <windows.h> 
#include <iostream>
using std::wcout;
using std::endl;

HANDLE hStdin; 
DWORD fdwSaveOldMode;

VOID ErrorExit(LPSTR);
VOID KeyEventProc(KEY_EVENT_RECORD); 
VOID MouseEventProc(MOUSE_EVENT_RECORD); 
VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD);

int _tmain(int argc, _TCHAR* argv[])
{
    // 輸出中文
    std::wcout.imbue(std::locale("chs"));

    // 設置控制台標題欄
    SetConsoleTitle(TEXT("ReadProcessInputBufferDemo"));

    DWORD cNumRead, fdwMode, i; 
    INPUT_RECORD irInBuf[128]; 
    int counter=0;

    // Get the standard input handle.
    hStdin = GetStdHandle(STD_INPUT_HANDLE); 
    if (hStdin == INVALID_HANDLE_VALUE) 
        ErrorExit("GetStdHandle"); 

    // Save the current input mode, to be restored on exit. 
    if (! GetConsoleMode(hStdin, &fdwSaveOldMode) ) 
        ErrorExit("GetConsoleMode"); 

    // Enable the window and mouse input events. 
    fdwMode = ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT| ENABLE_EXTENDED_FLAGS; 
    if (! SetConsoleMode(hStdin, fdwMode) ) 
        ErrorExit("SetConsoleMode"); 

    // Loop to read and handle the next 100 input events.
    while (counter++ <= 100) 
    { 
        // Wait for the events. 
        if (! ReadConsoleInput( 
            hStdin,      // input buffer handle 
            irInBuf,     // buffer to read into 
            128,         // size of read buffer 
            &cNumRead) ) // number of records read 
            ErrorExit("ReadConsoleInput"); 

        // Dispatch the events to the appropriate handler. 
        for (i = 0; i < cNumRead; i++) 
        {
            switch(irInBuf[i].EventType) 
            { 
            case KEY_EVENT: // keyboard input 
                KeyEventProc(irInBuf[i].Event.KeyEvent); 
                break; 

            case MOUSE_EVENT: // mouse input 
                MouseEventProc(irInBuf[i].Event.MouseEvent); 
                break; 

            case WINDOW_BUFFER_SIZE_EVENT: // scrn buf. resizing 
                ResizeEventProc( irInBuf[i].Event.WindowBufferSizeEvent ); 
                break; 

            case FOCUS_EVENT:  // disregard focus events 

            case MENU_EVENT:   // disregard menu events 
                break; 

            default: 
                ErrorExit("Unknown event type"); 
                break; 
            } 
        }
    } 

    // Restore input mode on exit.
    SetConsoleMode(hStdin, fdwSaveOldMode);

    return 0; 
}

VOID ErrorExit (LPSTR lpszMessage) 
{ 
    fprintf(stderr, "%s\n", lpszMessage); 

    // Restore input mode on exit.
    SetConsoleMode(hStdin, fdwSaveOldMode);

    ExitProcess(0); 
}

VOID KeyEventProc(KEY_EVENT_RECORD ker)
{
    printf("Key event: ");

    if(ker.bKeyDown)
        printf("key pressed\n");
    else printf("key released\n");
}

VOID MouseEventProc(MOUSE_EVENT_RECORD mer)
{
#ifndef MOUSE_HWHEELED
#define MOUSE_HWHEELED 0x0008
#endif
    printf("Mouse event: ");

    switch(mer.dwEventFlags)
    {
    case 0:

        if(mer.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
        {
            printf("left button press \n");
        }
        else if(mer.dwButtonState == RIGHTMOST_BUTTON_PRESSED)
        {
            printf("right button press \n");
        }
        else
        {
            printf("button press\n");
        }
        break;
    case DOUBLE_CLICK:
        printf("double click\n");
        break;
    case MOUSE_HWHEELED:
        printf("horizontal mouse wheel\n");
        break;
    case MOUSE_MOVED:
        printf("mouse moved\n");
        break;
    case MOUSE_WHEELED:
        printf("vertical mouse wheel\n");
        break;
    default:
        printf("unknown\n");
        break;
    }
}

VOID ResizeEventProc(WINDOW_BUFFER_SIZE_RECORD wbsr)
{
    printf("Resize event\n");
    printf("Console screen buffer is %d columns by %d rows.\n", wbsr.dwSize.X, wbsr.dwSize.Y);
}
View Code

屏幕緩沖大小改變事件通常很難手工觸發,可以編程修改屏幕緩大小,也可以換個屏幕分辨率試試。

實例i 控制台事件處理

使用SetConsoleCtrlHandler函數可以注冊控制台事件回調函數。主要處理事件包括:CTRL_C_EVENT(ctrl+c組合鍵)、CTRL_CLOSE_EVENT(關閉事件)、CTRL_BREAK_EVENT(中斷時間)CTRL_LOGOFF_EVENT(退出登錄)、 CTRL_SHUTDOWN_EVENT(關機事件)。

代碼如下:

#include <windows.h> 
#include <stdio.h> 
 
BOOL CtrlHandler( DWORD fdwCtrlType ) 
{ 
  switch( fdwCtrlType ) 
  { 
    // Handle the CTRL-C signal. 
    case CTRL_C_EVENT: 
      printf( "Ctrl-C event\n\n" );
      Beep( 750, 300 ); 
      return( TRUE );
 
    // CTRL-CLOSE: confirm that the user wants to exit. 
    case CTRL_CLOSE_EVENT: 
      Beep( 600, 200 ); 
      printf( "Ctrl-Close event\n\n" );
      return( TRUE ); 
 
    // Pass other signals to the next handler. 
    case CTRL_BREAK_EVENT: 
      Beep( 900, 200 ); 
      printf( "Ctrl-Break event\n\n" );
      return FALSE; 
 
    case CTRL_LOGOFF_EVENT: 
      Beep( 1000, 200 ); 
      printf( "Ctrl-Logoff event\n\n" );
      return FALSE; 
 
    case CTRL_SHUTDOWN_EVENT: 
      Beep( 750, 500 ); 
      printf( "Ctrl-Shutdown event\n\n" );
      return FALSE; 
 
    default: 
      return FALSE; 
  } 
} 
 
int main( void ) 
{ 
  if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ) ) 
  { 
    printf( "\nThe Control Handler is installed.\n" ); 
    printf( "\n -- Now try pressing Ctrl+C or Ctrl+Break, or" ); 
    printf( "\n    try logging off or closing the console...\n" ); 
    printf( "\n(...waiting in a loop for events...)\n\n" ); 
 
    while( 1 ){ } 
  } 
  else 
  {
    printf( "\nERROR: Could not set control handler"); 
    return 1;
  }
return 0;
}
View Code

本實例來源於msdn上Registering a Control Handler Function。也可參考處理控制台消息

 

注:本文涉及所有代碼可使用Git直接下載:https://git.oschina.net/Tocy/SampleCode.git。實際代碼位於Console目錄下,以2_開頭的cpp文件

 本文作者:Tocy

版權所有,請勿用於商業用途,轉載請注明原文地址。本人保留所有權利。 


免責聲明!

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



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