控制台输入输出机制实例

本文是针对 控制台输入输出机制 一文的实例说明。相关理论内容建议参考之。

实例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

版权所有,请勿用于商业用途,转载请注明原文地址。本人保留所有权利。 

posted @ 2014-11-29 21:32  Tocy  阅读(3941)  评论(0编辑  收藏  举报