VC++ 控制台 开发

开发简要

控制台开发首先是获得控制台的句柄,它有两个常用句柄,一个输入,一个输出,通过GetStdHandle函数获得。

STD_INPUT_HANDLE标准输入设备。 输入缓冲区
STD_OUTPUT_HANDLE标准输出设备。 控制台屏幕缓冲区
STD_ERROR_HANDLE标准错误设备。 控制台屏幕缓冲区

 通过以下代码可以获取句柄

HANDLE g_istd = GetStdHandle(STD_INPUT_HANDLE);// 控制台输入句柄
HANDLE g_ostd = GetStdHandle(STD_OUTPUT_HANDLE); // 控制台输出句柄

 输入设备

一般来说通过输入缓冲区可以用来获取 键盘输入,鼠标输入之类信息。使用函数ReadConsoleInput 从输入缓冲区中读取输入事件,一般提前使用GetNumberOfConsoleInputEvents 函数,来确定输入缓冲区中未读输入事件的数目。

ReadConsoleInput

参数:

hConsoleInput [in]

控制台输入缓冲区的句柄。

lpBuffer [out]

用来接收输入信息的结构体数组。类型为INPUT_RECORD。

nLength [in]

lpBuffer 数组的大小。

lpNumberOfEventsRead [out]

返回读取的输入事件数。

通过该函数,可以获取到键盘的方向按键信息

    DWORD nread;
	INPUT_RECORD* ibuf = NULL;
	// 判读是否有输入事件

	if (!GetNumberOfConsoleInputEvents(g_istd, &nread))
	{
		printf("GetNumberOfConsoleInputEvents error\n");
		return;
	}

	if (!nread)
	{
		return;
	}

	ibuf = new INPUT_RECORD[nread];
	// 读取输入
	if (!ReadConsoleInput(
		g_istd,      // input buffer handle
		ibuf,     // buffer to read into
		nread,         // size of read buffer
		&nread)) // number of records read
	{
		printf("ReadConsoleInput error\n");
		return;
	}

	// 判断事件类型
	for (int i = 0; i < nread; i++)
	{
		switch (ibuf[i].EventType)
		{
		case KEY_EVENT: // keyboard input
			if (ibuf[i].Event.KeyEvent.bKeyDown)
			{
				switch (ibuf[i].Event.KeyEvent.wVirtualKeyCode)
				{
				case VK_LEFT:
				case VK_RIGHT:
				case VK_UP:
				case VK_DOWN:
					// 执行操作
					break;
				default:
					break; // 直接退出。
				}
			}

			break;

		default:
			break;
		}
	}
	delete[] ibuf;


输出设备

一般来说可认为就是我们看到的控制台窗口本尊。通过ReadConsoleOutput 可读取屏幕缓冲区每格单元格的字符和属性,这些决定了控制台显示的内容、颜色等

在对控制台的窗口进行操作的时候,可以把窗口看成是一格一格的类似excel的单元格的存在,只是看不到线。通过GetConsoleScreenBufferInfo等函数 可以获取屏幕缓冲区的信息。

通过WriteConsoleOutput等函数,可以在屏幕上输出内容、颜色等

单元格属性

Attribute含义
FOREGROUND_BLUE文本颜色包含蓝色。
FOREGROUND_GREEN文本颜色包含绿色。
FOREGROUND_RED文本颜色包含红色。
FOREGROUND_INTENSITY文本颜色增强。
BACKGROUND_BLUE背景色包含蓝色。
BACKGROUND_GREEN背景色包含绿色。
BACKGROUND_RED背景色包含红色。
BACKGROUND_INTENSITY背景色增强。
COMMON_LVB_LEADING_BYTE前导字节。
COMMON_LVB_TRAILING_BYTE尾随字节。
COMMON_LVB_GRID_HORIZONTAL顶部水平。
COMMON_LVB_GRID_LVERTICAL左垂直。
COMMON_LVB_GRID_RVERTICAL右垂直。
COMMON_LVB_REVERSE_VIDEO反转前景和背景属性。
COMMON_LVB_UNDERSCORE下划线。

单元格属性可以组合使用,比如FOREGROUND_BLUE | FOREGROUND_GREEN |FOREGROUND_RED 组合表示,输出文本颜色为白色,未指定背景颜色,则默认背景是黑色。

GetConsoleScreenBufferInfo

hConsoleOutput [in]
控制台屏幕缓冲区的句柄

lpConsoleScreenBufferInfo [out]
输出获取到的屏幕缓冲区信息。该结构体成员如下:

  • dwSize  控制台屏幕缓冲区的大小,可以理解成屏幕由x列y行的单元格矩阵组成。
  • dwCursorPosition 当前控制台光标所在的行列坐标。
  • wAttributes 当前屏幕输出文本时所使用的属性
  • srWindow 包含显示窗口左上角和右下角的控制台屏幕缓冲区坐标。
  • dwMaximumWindowSize 控制台窗口的最大大小

WriteConsoleOutput

hConsoleOutput [in]
控制台屏幕缓冲区的句柄。

lpBuffer [in]

要写入到控制台屏幕缓冲区的数据数组,该数组每项对应屏幕缓冲区一个“单元格”,用来设置该单元格的文本和颜色等。

lpBuffer可以想象成是屏幕缓冲区的一块画布。通过dwBufferSize,dwBufferCoord来指定画布与缓冲区的位置关系。最好lpBuffer是与屏幕缓冲区大小一致(GetConsoleScreenBufferInfo 获得的dwSize),方便定位某个“单元格”。

dwBufferSize [in]
lpBuffer 数组的大小

dwBufferCoord [in]

这个参数很变扭,感觉这个点表示,屏幕缓冲区左上角位于画布上的位置。一般设成{0,  0}

lpWriteRegion [in, out]
指定绘制到某个区域。

可以理解成把上面的画布绘制到该区域

通过该函数,可以绘制屏幕

CONSOLE_SCREEN_BUFFER_INFO csbi;
	// 获取屏幕缓冲区信息
	GetConsoleScreenBufferInfo(g_ostd, &csbi);
	int len = 5;
	COORD* pt = new COORD[5];
	// 设定要修改的单元格坐标
	for (int i = 0; i < len; i++)
	{
		pt[i].X = i + 1;
		pt[i].Y = 1;
	}
	// 设定蓝色背景
	WORD attr =  BACKGROUND_BLUE ;
	COORD size = csbi.dwSize;
	CHAR_INFO* buf = NULL; // 要写入缓冲区的数据数组
	COORD bufsize = { size.X, size.Y }; // 缓冲区大小
	COORD coord = { 0, 0 }; // 无偏移
	SMALL_RECT rect = { 0, 0, size.X - 1, size.Y - 1 }; // 绘制区域,控制绘制位置

	buf = new CHAR_INFO[size.X * size.Y];
	// 获取当前缓冲区的数据
	ReadConsoleOutput(g_ostd, buf, bufsize, coord, &rect);
	// 修改pt所指向的单元格的属性
	for (int i = 0; i < len; i++)
	{
		int bufi = size.X * pt[i].Y + pt[i].X;

		if (bufi >= 0 && bufi < size.X * size.Y
			&& pt[i].X >= 0 && pt[i].X < size.X)
		{
			buf[bufi].Char.UnicodeChar = TEXT(' '); // 设置缓冲区单元格文本,这里是空格
			buf[bufi].Attributes = attr; // 设置缓冲区单元格属性
		}
	}
	// 输出到屏幕
	WriteConsoleOutput(g_ostd, buf, bufsize, coord, &rect);
	delete[] buf;

WriteConsole

用于输出一段文字到屏幕

一般先使用SetConsoleCursorPosition 函数设定光标的位置,即设定WriteConsole输出起始位置

此外,使用FillConsoleOutputAttribute 函数可以设定输出文本的颜色。

    WORD attr;
	COORD coord;
	DWORD written;
	DWORD length;
	// 设定文本输出属性,蓝底黄字
	attr = FOREGROUND_GREEN | FOREGROUND_RED | BACKGROUND_BLUE;
    // 设定输出位置
	coord = { SHORT(g_size.X / 2) , SHORT(g_size.Y / 2) };

	TCHAR str[32]=TEXT("Hello World");
	length = lstrlen(str);
    // 设定光标位置
	SetConsoleCursorPosition(g_ostd, coord);
    // 输出文本
	if (!WriteConsole(g_ostd, str, length, &written, NULL))
		return;
    // 设定输出颜色
	if (!FillConsoleOutputAttribute(g_ostd, attr, length, coord, &written))
		return;

示例 显示一个进度条

/// <summary>
/// 在控制台上显示进度条
/// </summary>
/// <param name="hConsole">控制台</param>
/// <param name="percent">需要显示的进度条百分比</param>
/// <param name="back">光标是否回到起始位置,方便下一次刷新进度条,或者指向下一行</param>
/// <param name="show">是否显示光标</param>
void PrintProgress(HANDLE ostd, int percent, BOOL back = TRUE, BOOL show = FALSE)
{
	COORD cur_coord = { 0, 0 };    // 鼠标坐标
	DWORD written;
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	DWORD number;

	if (ostd == NULL)
	{
		ostd = GetStdHandle(STD_OUTPUT_HANDLE);
	}
	if (percent > 100)
		percent = 100;
	else if (percent < 0)
		percent = 0;

	if (!GetConsoleScreenBufferInfo(ostd, &csbi))
	{
		return;
	}

	WORD attr;
	// 输出属性
	attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_BLUE;
	number = csbi.dwSize.X * percent / 100; // 获得百分比对应的输出cells数量
	cur_coord = csbi.dwCursorPosition; // 进度条起始位置

	TCHAR str[10];
	swprintf_s(str, 10, TEXT("%d%%"), percent);
    // 输出进度条
	if (!FillConsoleOutputCharacter(ostd,(TCHAR)' ', number,cur_coord, &written))
	{
		return;
	}
    // 输出百分比
	WriteConsole(ostd, str, lstrlen(str), &written, NULL);
    // 给进度条上色
	if (!FillConsoleOutputAttribute(ostd, attr, number, cur_coord, &written))
	{
		return;
	}

	if (back) // 回到进度条开头
	{
		SetConsoleCursorPosition(ostd, cur_coord);
	}
	else // 指向下一行
	{
		cur_coord.X += 0;
		cur_coord.Y += 1;
		SetConsoleCursorPosition(ostd, cur_coord);
	}
	// 隐藏或显示光标
	CONSOLE_CURSOR_INFO cursorInfo;
	GetConsoleCursorInfo(ostd, &cursorInfo);
	cursorInfo.bVisible = show;
	SetConsoleCursorInfo(ostd, &cursorInfo);
}

 效果测试

	int i=0;
	while (1) {
		PrintProgress(NULL, i++);
		Sleep(100);
	}

示例 贪吃蛇

示例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值