文档章节

控制台输入输出机制实例

Tocy
 Tocy
发布于 2015/09/09 22:14
字数 4242
阅读 33
收藏 0

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

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

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

© 著作权归作者所有

Tocy
粉丝 29
博文 50
码字总数 59635
作品 0
海淀
程序员
私信 提问
Apache Karaf用户指导

Apache <mark>Karaf</mark>用户指导 Apache Karaf用户指导 一 安装karaf 本章讲述如何在windows和unix平台安装Apache Karaf,这里你将知道预先要安装什么软件,如何下载并且安装Karaf。 预安...

LeBlancs
2016/12/22
81
0
基于winserver部署Apollo初次体验(附.net客户端demo)

前言 配置中心伴随着这几年分布式系统演变和微服务架构的兴起,已经成为必不可少的需求之一。试下一下如果哪天公司的所有应用服务,从公司服务器迁移到云服务,成千上万的配置,修改起来是多...

陈珙
2018/07/12
0
0
学习C++在windows下窗口应用程序开发(一)

在网上有幸找到孙鑫老师的C++视频,先让我膜拜一下,看到视频是免费的,所以分享一下URL应该没什么事情吧,偷摸的。。。。emmmmmm 点击打开链接 好了,还是进入正题吧,毕竟这是我第一次写博...

qq_32823595
2018/03/05
0
0
【umi插件开发】控制台二维码

前言 在进行移动端webapp开发时,你是否会想要在真机上调试项目。 下面分析一下本地运行项目时,真机调试需要的步骤和麻烦的点。 你需要将手机和运行项目的电脑连接到同一局域网(连接同一个...

广州芦苇科技web前端
07/26
0
0
5. Java 中的变量 【连载 5】

1. 理解 Java 中的变量 程序中需要处理数据,Java 中的变量是用来装载数据。变量类似一个盒子,我们通过这个盒子,就可以操作盒子里面的数据。 Java 中每个变量都有名称和数值。名称不会变化...

密叔
2018/01/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

TVS瞬态抑制二极管的三大特点

  TVS管瞬态抑制二极管有以下三大特点。   1、将 TVS 瞬态抑制二极管加在信号及电源线上,能防止微处理器或单片机因瞬间的脉冲,如静电放电效应、交流电源之浪涌及开关电源的噪音所导致的...

仙溪
12分钟前
2
0
6 个 K8s 日志系统建设中的典型问题,你遇到过几个?

导读:随着 K8s 不断更新迭代,使用 K8s 日志系统建设的开发者,逐渐遇到了各种复杂的问题和挑战。本篇文章中,作者结合自己多年经验,分析 K8s 日志系统建设难点,期待为读者提供有益参考。...

Mr_zebra
12分钟前
2
0
准则2.1-性能、运行Wi-Fi在iPad上一个或多个错误问题

很多开发者上架遇到这个问题,苹果那边打不开APP,加载不出来内容! 很多人以为是没有兼容ipad,其实是苹果审核都用ipad,跟有没有支持兼容没有关系。 如果自己在国内测试加载正常,要看APP...

qtb999
12分钟前
3
0
华为云学院带你7天入门Redis(2)

华为云学院带你7天入门Redis(2) 1、深度剖析memory Info是Redis提供的一个非常有用的查看状态信息的命令。使用 redis-cli 连上 Redis,输入 info all 命令,redis-server 就 会返回 Redis ...

华为云学院
19分钟前
4
0
Android 自定义View中,四个参数的构造函数的含义

MyView(Context context) Used when instanciating Views programmatically. MyView(Context context, AttributeSet attrs) Used by the LayoutInflater to apply xml attributes. If one of......

SuShine
22分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部