获取标准输出句柄
要开始操作控制台,首先我们得获取到stdout的句柄。
HANDLE GetStdHandle(DWORD nStdHandle);
返回标准的输入、输出或错误的设备的句柄,也就是获得输入、输出/错误的屏幕缓冲区的句柄。
其中nStdHandle值为下面几种类型的一种:
STD_INPUT_HANDLE
、STD_OUTPUT_HANDLE
、STD_ERROR_HANDLE
分别代表标准输入,标准输出,错误输出。
程序结束之后会自动关闭句柄,但是一些大型应用可能要考虑一下释放句柄了
BOOL CloseHandle(HANDLE hObject);
获取当前控制台属性
在搞事情之前,保存一下控制台属性总是好的。
BOOL GetConsoleScreenBufferInfo (
HANDLE hConsoleOutput,
CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
CONSOLE_SCREEN_BUFFER_INFO是一个描述控制台属性的结构体,注意使用函数时需要传址来接收信息,其原型如下:
typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
COORD dwSize; // 缓冲区大小
COORD dwCursorPosition; // 当前光标位置
WORD wAttributes; // 字符属性
SMALL_RECT srWindow; // 当前窗口显示的大小和位置
COORD dwMaximumWindowSize; // 最大的窗口缓冲区大小
} CONSOLE_SCREEN_BUFFER_INFO ;
基本上想要的都在这了。
控制台标题
DWORD GetConsoleTitle(LPTSTR lpConsoleTitle, DWORD nSize);
LPTSTR可以用普通char数组代替,nsize是接收的最大长度。
BOOL SetConsoleTitle(LPCTSTR lpConsoleTitle);
控制台操作例子
#include <windows.h>
#include <stdio.h>
#include <conio.h>
int main(void)
{
char strTitle[255];
CONSOLE_SCREEN_BUFFER_INFO bInfo; // 窗口缓冲区信息
COORD size = {80, 25};
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备句柄
GetConsoleScreenBufferInfo(hOut, &bInfo ); // 获取窗口缓冲区信息
GetConsoleTitle(strTitle, 255); // 获取窗口标题
printf("当前窗口标题是:\n%s\n", strTitle);
_getch();
SetConsoleTitle("控制台窗口操作"); // 设置窗口标题
GetConsoleTitle(strTitle, 255);
printf("当前窗口标题是:\n%s\n", strTitle);
_getch();
SetConsoleScreenBufferSize(hOut,size); // 重新设置缓冲区大小
_getch();
SMALL_RECT rc = {0,0, 80-1, 25-1}; // 重置窗口位置和大小
SetConsoleWindowInfo(hOut,true ,&rc);
CloseHandle(hOut); // 关闭标准输出设备句柄
return 0;
}
文字颜色
既然是控制台,离不开的就是文字颜色了。windows提供的API叫做SetConsoleTextAttribute
。
一个小例子
#include <stdio.h>
#include <windows.h>
int main()
{
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
WORD wOldColorAttrs;
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
// Save the current color
GetConsoleScreenBufferInfo(h, &csbiInfo);
wOldColorAttrs = csbiInfo.wAttributes;
// Set the new color
SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_GREEN);
printf("This is a test\n");
// Restore the original color
SetConsoleTextAttribute(h, wOldColorAttrs);
printf("This is a test\n");
}
BOOL SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes);
其中hConsoleOutput为我们上面获取到的句柄,wAttributes为颜色代码:
FOREGROUND_BLUE 字体颜色:蓝 FOREGROUND_GREEN 字体颜色:绿 FOREGROUND_RED 字体颜色:红 FOREGROUND_INTENSITY 前景色高亮显示 BACKGROUND_BLUE 背景颜色:蓝 BACKGROUND_GREEN 背景颜色:绿 BACKGROUND_RED 背景颜色:红 BACKGROUND_INTENSITY 背景色高亮显示
其颜色遵循三原色混合规律:
使用时将想要的参数用 | (“按位或”)符号连接即可
想要恢复怎么办?CONSOLE_SCREEN_BUFFER_INFO里面有一个变量wAttributes(见上)储存的就是原来的颜色属性,需要时再用SetConsoleTextAttribute设置回来即可。
光标位置
储存光标位置的类型是COORD,对应CONSOLE_SCREEN_BUFFER_INFO.dwCursorPosition。
如果要设置光标位置使用SetConsoleCursorPosition
BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput, COORD dwCursorPosition);
COORD 原型如下:
typedef _COORD { short X, short Y} COORD;
一个小例子:
//C语言实现控制台中光标随意移动
#include <stdio.h>
#include <windows.h>
#include <conio.h>
HANDLE hout;
//获得输入
char getInput()
{
int ch; //输入字符串
COORD coord; //屏幕上的坐标
CONSOLE_SCREEN_BUFFER_INFO csbi; //控制台屏幕缓冲区信息
coord.X=10;
coord.Y=10;
ch=getch();
//0x0d表示回车,0XE0表示上下左右等键的键码
while(ch==0xE0||ch==0x0d)
{
GetConsoleScreenBufferInfo(hout,&csbi);
coord.X=csbi.dwCursorPosition.X; //得到坐标X的值
coord.Y=csbi.dwCursorPosition.Y; //得到坐标Y的值
//如果是回车
if(ch==0x0d)
{
coord.X=0;
coord.Y++;
SetConsoleCursorPosition(hout,coord);
break;
}
ch=getch();
//上
if(ch==0x48)
{
if(coord.Y!=0)coord.Y--;
}
//下
else if(ch==0x50)
{
coord.Y++;
}
//左
else if(ch==0x4b)
{
if(coord.X!=0){coord.X--;}
}
//右
else if(ch==0x4d)
{
if(coord.X!=79)coord.X++;
else
{
coord.X=0;
coord.Y++;
}
}
SetConsoleCursorPosition(hout,coord);
ch=getch();
}
return ch;
}
int main()
{
char ch;
hout=GetStdHandle(STD_OUTPUT_HANDLE);
//从键盘获取输入,如果是方向键则执行方向功能,如果是回车键则换行,如果是字符则输出
while(1)
{
ch=getInput();
printf("%c",ch);
}
getchar();
}
光标
BOOL SetConsoleCursorInfo( // 设置光标信息
HANDLE hConsoleOutput, // 句柄
CONST CONSOLE_CURSOR_INFO *lpConsoleCursorInfo // 光标信息
);
BOOL GetConsoleCursorInfo( // 获取光标信息
HANDLE hConsoleOutput, // 句柄
PCONSOLE_CURSOR_INFO lpConsoleCursorInfo // 返回光标信息
);
这两个函数都与CONSOLE_CURSOR_INFO结构体类型有关,其定义如下:
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize; // 光标百分比大小
BOOL bVisible; // 是否可见
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
dwSize值反映了光标的大小,它的值范围为1-100;当为1时,光标最小,仅是一条最靠下的水平细线,当为100,光标最大,为一个字符大小的方块。
文本输出
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); // (接收,需传址)已写个数
文本操作例子
// 在具有阴影效果的窗口中显示一行字符
#include <windows.h>
HANDLE hOut;
void ShadowWindowLine(char *str);
void DrawBox(bool bSingle, SMALL_RECT rc); // 绘制边框
int main(void)
{
hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备句柄
SetConsoleOutputCP(437); // 设置代码页,这里如果设置成936(简体中文),那么程序会怎样?那样的话,将画不出边框。
ShadowWindowLine("Display a line of words, and center the window with shadow.");
CloseHandle(hOut); // 关闭标准输出设备句柄
return 0;
}
void ShadowWindowLine(char *str)
{
SMALL_RECT rc;
CONSOLE_SCREEN_BUFFER_INFO bInfo; // 窗口缓冲区信息
WORD att0,att1,attText;
int i, chNum = strlen(str);
GetConsoleScreenBufferInfo( hOut, &bInfo ); // 获取窗口缓冲区信息
// 计算显示窗口大小和位置
rc.Left = (bInfo.dwSize.X - chNum)/2 - 2;
rc.Top = 8; // 原代码段中此处为bInfo.dwSize.Y/2 - 2,但是如果您的DOS屏幕有垂直滚动条的话,还需要把滚动条下拉才能看到,为了方便就把它改为10
rc.Right = rc.Left + chNum + 4;
rc.Bottom = rc.Top + 4;
att0 = BACKGROUND_INTENSITY; // 阴影属性
att1 = FOREGROUND_RED |FOREGROUND_GREEN |FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE;// 文本属性
attText = FOREGROUND_RED |FOREGROUND_INTENSITY; // 文本属性
// 设置阴影然后填充
COORD posShadow = {rc.Left+1, rc.Top+1}, posText = {rc.Left, rc.Top};
for (i=0; i<5; i++)
{
FillConsoleOutputAttribute(hOut, att0, chNum + 4, posShadow, NULL);
posShadow.Y++;
}
for (i=0;i<5;i++)
{
FillConsoleOutputAttribute(hOut, att1,chNum + 4, posText, NULL);
posText.Y++;
}
// 写文本和边框
posText.X = rc.Left + 2;
posText.Y = rc.Top + 2;
WriteConsoleOutputCharacter(hOut, str, strlen(str), posText, NULL);
DrawBox(true, rc);
SetConsoleTextAttribute(hOut, bInfo.wAttributes); // 恢复原来的属性
}
void DrawBox(bool bSingle, SMALL_RECT rc) // 函数功能:画边框
{
char chBox[6];
COORD pos;
if (bSingle)
{
chBox[0] = (char)0xda; // 左上角点
chBox[1] = (char)0xbf; // 右上角点
chBox[2] = (char)0xc0; // 左下角点
chBox[3] = (char)0xd9; // 右下角点
chBox[4] = (char)0xc4; // 水平
chBox[5] = (char)0xb3; // 坚直
} else {
chBox[0] = (char)0xc9; // 左上角点
chBox[1] = (char)0xbb; // 右上角点
chBox[2] = (char)0xc8; // 左下角点
chBox[3] = (char)0xbc; // 右下角点
chBox[4] = (char)0xcd; // 水平
chBox[5] = (char)0xba; // 坚直
}
// 画边框的上 下边界
for(pos.X = rc.Left+1;pos.X<rc.Right-1;pos.X++)
{
pos.Y = rc.Top;
// 画上边界
WriteConsoleOutputCharacter(hOut, &chBox[4], 1, pos, NULL);
// 画左上角
if(pos.X == rc.Left+1)
{
pos.X--;
WriteConsoleOutputCharacter(hOut, &chBox[0],1, pos, NULL);
pos.X++;
}
// 画右上角
if(pos.X == rc.Right-2)
{
pos.X++;
WriteConsoleOutputCharacter(hOut, &chBox[1], 1, pos, NULL);
pos.X--;
}
pos.Y = rc.Bottom;
// 画下边界
WriteConsoleOutputCharacter(hOut, &chBox[4], 1, pos, NULL);
// 画左下角
if(pos.X == rc.Left+1)
{
pos.X--;
WriteConsoleOutputCharacter(hOut, &chBox[2], 1, pos, NULL);
pos.X++;
}
// 画右下角
if(pos.X==rc.Right-2)
{
pos.X++;
WriteConsoleOutputCharacter(hOut, &chBox[3], 1, pos, NULL);
pos.X--;
}
}
// 画边框的左右边界
for (pos.Y = rc.Top+1; pos.Y<=rc.Bottom-1; pos.Y++)
{
pos.X = rc.Left;
// 画左边界
WriteConsoleOutputCharacter(hOut, &chBox[5], 1, pos, NULL);
pos.X = rc.Right-1;
// 画右边界
WriteConsoleOutputCharacter(hOut, &chBox[5], 1, pos, NULL);
}
}
效果图