目录

  • 前言
  • 一、控制台缓冲区大小
    • 一、获取缓冲区大小
    • 二、设置缓冲区大小
    • 三、更详细的结构体
    • 四、特性
  • 二、窗口大小
    • 一、获取窗口大小
    • 二、设置窗口大小
  • 三、光标位置
    • 一、获取光标位置
    • 二、设置光标位置
  • 四、设置光标是否显示
    • 一、旧版API
    • 二、新版控制台的 ANSI 转义序列
    • 三、新版控制台特性
  • 五、通过代码打开 ANSI 转义序列
  • 六、控制台编码
    • 一、设置输出编码
    • 二、设置输入编码
  • 七、修改控制台文本样式
    • 一、使用API
    • 二、使用ANSI转义序列
  • 八、控制台窗口标题
    • 一、获取控制台窗口标题
    • 二、设置窗口标题
    • 一、使用旧版API
      • 二、使用转义序列
      • 三、获取控制台原始标题
  • 九、置顶窗口
  • 十、移动窗口
  • 十一、(旧API)ScrollConsoleScreenBuffer
  • 十二、清空控制台
  • 十三、控制台字体
    • 一、获取控制台字体
    • 二、设置控制台字体
    • 三、字体无法使用
      • 一、参数未正确设置
  • 附录 - 后篇链接

前言

  本文章所使用的一些API为旧版API,建议使用其它方案代替它们。
  本文章多有些纰漏,若发现错误请在评论区指正。
  注: 文章内所有代码均在VS2019下编译通过。

一、控制台缓冲区大小

一、获取缓冲区大小

  GetConsoleScreenBufferInfo 可以获取到控制台窗口的一些信息。

BOOL WINAPI GetConsoleScreenBufferInfo(_In_  HANDLE                      hConsoleOutput,           // 输出句柄_Out_ PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo // 缓冲区信息结构体
);

  PCONSOLE_SCREEN_BUFFER_INFO 实际上是一个指针,可以使用_CONSOLE_SCREEN_BUFFER_INFO 或 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;

  此结构体同样包含缓冲区光标位置的信息,在获取光标位置时可以使用此结构体。
  下方代码将会输出缓冲区大小。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 获取输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_SCREEN_BUFFER_INFO info;GetConsoleScreenBufferInfo(outputHandle, &info);printf("缓冲区大小: x=%hd, y=%hd\n", info.dwSize.X, info.dwSize.Y);return 0;
}


二、设置缓冲区大小

  (旧版API)SetConsoleScreenBufferSize 可以修改控制台的缓冲区大小。

WINBASEAPI
BOOL
WINAPI
SetConsoleScreenBufferSize(_In_ HANDLE hConsoleOutput, // 输出句柄_In_ COORD dwSize           // 新缓冲区大小);

  此函数接受一个 COORD 结构体 作为参数,其原型为:

typedef struct _COORD {SHORT X;SHORT Y;
} COORD, *PCOORD;

  下方代码将会设置缓冲区大小为115×40个字符。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 获取输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);// 定义大小COORD size = { 115, 40 };// 应用SetConsoleScreenBufferSize(outputHandle, size);SetConsoleScreenBufferSize(outputHandle, size);return 0;
}


  不建议使用此API。

三、更详细的结构体

  CONSOLE_SCREEN_BUFFER_INFOEX 结构体能够记录更多的控制台信息,并且也有与之匹配的获取(GetConsoleScreenBufferInfoEx)和设置(SetConsoleScreenBufferInfoEx)函数。多数情况下,笔者更推荐使用 CONSOLE_SCREEN_BUFFER_INFOEX,因为 CONSOLE_SCREEN_BUFFER_INFOEX 拥有与之匹配的函数,而 _CONSOLE_SCREEN_BUFFER_INFO 和 CONSOLE_SCREEN_BUFFER_INFO 仅有一个获取函数(GetConsoleScreenBufferInfo),所以,能用就用吧。
  下面的代码演示了如何使用 CONSOLE_SCREEN_BUFFER_INFOEX。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 获取输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_SCREEN_BUFFER_INFOEX info;info.cbSize = sizeof(info);        // 不要忘记指定结构体大小GetConsoleScreenBufferInfoEx(outputHandle, &info);printf("缓冲区大小: x=%hd, y=%hd\n""窗口大小: x=%hd, y=%hd\n""光标坐标: x=%hd, y=%hd\n", info.dwSize.X, info.dwSize.Y, info.srWindow.Right + 1, info.srWindow.Bottom + 1, info.dwCursorPosition.X, info.dwCursorPosition.Y);return 0;
}

四、特性

  新版控制台又出特性了。在使用 GetConsoleScreenBufferInfoEx 获取控制台缓冲区信息后,如果再次设置,控制台的大小竟会缩减1行!?笔者在 stackoverflow 查找到了解决方法——只需在获取信息后将 srWindow 的 Right 和 Bottom 加 1。请参阅下方代码。

#include <stdio.h>
#include <Windows.h>int main() {// 输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);// 控制台缓冲区信息CONSOLE_SCREEN_BUFFER_INFOEX csbi;csbi.cbSize = sizeof(csbi);// 获取信息GetConsoleScreenBufferInfoEx(outputHandle, &csbi);printf("获取到的 srWindow 的值:{%d %d %d %d}\n", csbi.srWindow.Left, csbi.srWindow.Top, csbi.srWindow.Right, csbi.srWindow.Bottom);csbi.srWindow.Right++;csbi.srWindow.Bottom++;printf("更正后的 srWindow 的值:{%d %d %d %d}\n", csbi.srWindow.Left, csbi.srWindow.Top, csbi.srWindow.Right, csbi.srWindow.Bottom);return 0;
}

  效果:

二、窗口大小

一、获取窗口大小

  对于这种需求,CONSOLE_SCREEN_BUFFER_INFOEX 可以解决一切问题。但现在这里将依然使用 GetConsoleScreenBufferInfo 函数获取窗口大小。下面代码将会演示此功能。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 获取输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_SCREEN_BUFFER_INFO info;GetConsoleScreenBufferInfo(outputHandle, &info);printf("窗口大小: x=%hd, y=%hd\n", info.srWindow.Right + 1, info.srWindow.Bottom + 1);return 0;
}


  注: srWindow会把0也算进窗口大小内,所以需要将结果加1得到正确的数字。
  使用 CONSOLE_SCREEN_BUFFER_INFOEX 的样例在第一节的第三小节。

二、设置窗口大小

  (旧版API)SetConsoleWindowInfo 可以做到这一点。
  查阅 Microsoft Docs 了解此函数的参数。
  下面的代码可以实现此功能。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 获取输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);SMALL_RECT winSize = {0, 0};winSize.Right = 20 - 1;winSize.Bottom = 10 - 1;SetConsoleWindowInfo(outputHandle, 1, &winSize);SetConsoleWindowInfo(outputHandle, 1, &winSize);printf("窗口大小被改变。\n");return 0;
}

三、光标位置

一、获取光标位置

  第一节的第三小节的示例代码演示了如何获取光标位置。而 _CONSOLE_SCREEN_BUFFER_INFO 和 CONSOLE_SCREEN_BUFFER_INFO 也可以获取光标位置。
  _CONSOLE_SCREEN_BUFFER_INFO 和 CONSOLE_SCREEN_BUFFER_INFO 的 dwCursorPosition 成员规定了光标的坐标。 CONSOLE_SCREEN_BUFFER_INFOEX 也有该成员。下面代码演示了如何使用 _CONSOLE_SCREEN_BUFFER_INFO 和 CONSOLE_SCREEN_BUFFER_INFO 的 dwCursorPosition 成员获取光标信息。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 获取输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_SCREEN_BUFFER_INFO info;GetConsoleScreenBufferInfo(outputHandle, &info);printf("光标位置: (%hd,%hd)\n", info.dwCursorPosition.X, info.dwCursorPosition.Y);return 0;
}

二、设置光标位置

  Windows 10 支持特殊 ANSI 序列,可以直接输出 ANSI 转义序列移动光标。

#include <stdio.h>int main(int argc, char* argv[]) {//    光标即将前往的坐标short x = 5, y = 5;printf("\x1b[%hd;%hdH", y + 1, x);printf("Hello world!\n");return 0;
}


  使用 (旧版API)SetConsoleCursorPosition 函数可以在新旧版控制台中工作。如果程序需要跨平台,最好不使用此 API。

四、设置光标是否显示

一、旧版API

  (旧版API)GetConsoleCursorInfo、(旧版API)SetConsoleCursorInfo 和 (旧版结构)CONSOLE_CURSOR_INFO 搭配可以设置光标信息。下方代码演示了这项工作。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 获取句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);// 获取光标信息_CONSOLE_CURSOR_INFO cursorInfomations;GetConsoleCursorInfo(outputHandle, &cursorInfomations);// 将光标设为不可见cursorInfomations.bVisible = 0;// 应用光标信息SetConsoleCursorInfo(outputHandle, cursorInfomations);printf("光标被 API 隐藏了。\n");return 0;
}


  此 API 建议在旧版控制台使用,最好不在新版控制台用。

二、新版控制台的 ANSI 转义序列

  下方代码将通过 ANSI 转义序列显示或隐藏光标。

#include <stdio.h>
#include <Windows.h>void setCursorVisible(bool status) {// \033[?25h  显示光标// \033[?25l  隐藏光标printf("\033[?25%c", (status ? 'h' : 'l'));
}int main(int argc, char* argv[]) {setCursorVisible(1); // 显示setCursorVisible(0); // 隐藏return 0;
}

三、新版控制台特性

  在 Win10 新版控制台中最好不要隐藏光标,因为不管使用 API 隐藏还是 ANSI 转义序列隐藏,用鼠标拖拽窗口改变大小都会导致光标重新显示。下方GIF演示了新版控制台的特性。

五、通过代码打开 ANSI 转义序列

  Windows 10 新版控制台可以手动开启 ANSI 转义序列,下方代码将会演示此功能,重点在于enabledANSI函数。

#include <stdio.h>
#include <Windows.h>//   启用 ANSI
bool enabledANSI(HANDLE outputHandle) {if (outputHandle == INVALID_HANDLE_VALUE) { return 0; }DWORD dwMode_Out = 0;// 获取当前控制台模式if (!GetConsoleMode(outputHandle, &dwMode_Out)) { return 0; }// 启用设置dwMode_Out |= ENABLE_PROCESSED_OUTPUT; dwMode_Out |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;dwMode_Out |= DISABLE_NEWLINE_AUTO_RETURN;// 设置if (!SetConsoleMode(outputHandle, dwMode_Out)) { return 0; }return 1;
}int main(int argc, char* argv[]) {bool status = enabledANSI(GetStdHandle(STD_OUTPUT_HANDLE));printf("ANSI 转义序列打开....。\b\b\b\b\b\b");if (status) {printf("成功");} else {printf("失败");}printf("\n");return 0;
}

  此程序在笔者的系统上成功运行。

六、控制台编码

一、设置输出编码

  SetConsoleOutputCP 函数可以修改当前控制台输出的代码页,其接受一个 UINT(unsigned int) 作为参数。

BOOL WINAPI SetConsoleOutputCP(_In_ UINT wCodePageID       // 新代码页
);

  下面的代码将会演示此函数的功能。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {SetConsoleOutputCP(437);printf("English.\n");printf("中文。\n");printf("日本語。\n");return 0;
}


  代码页437是美国英语的代码页,其只能显示英语字符(准确来说是 ASCII 码内的字符),所以在输出其它语种时就会出现乱码,如上图所示。
  下表是部分代码页。

代码页 描述
437 美国英语
850 多语言
857 土耳其语
866 俄语
869 现代希腊语
936 简体中文

  定义常量以区分这些代码页。

//C
#define US           437
#define MULTILINGUAL 850
#define TURKISH      857
#define RUSSIA       866
#define MODERNGREEK  869
#define CHINESE      936
//C++
unsigned short const US           = 437,MULTILINGUAL = 850,TURKISH      = 857,RUSSIA       = 866,MODERNGREEK  = 869,CHINESE      = 936;

  GetConsoleOutputCP 可以获取当前活动代码页。下方代码将会演示此功能。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {printf("Code: %hd\n", GetConsoleOutputCP());return 0;
}

二、设置输入编码

  SetConsoleCP 函数可以设置输入编码。Microsoft Docs 这么描述它:

“设置与调用进程关联的控制台使用的输入代码页。 控制台使用其输入代码页将键盘输入转换为相应的字符值。”

  Microsoft Docs 明确说明了此 API 用于设置输入的字符的代码页。下方代码将会演示此功能。

// VS2019下需要定义此宏
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 修改输入代码页SetConsoleCP(437);printf("输入一串字符串: ");char getString[20] = "";scanf("%[^\n]", getString);printf("输入的内容是:\n%s\n", getString);return 0;
}


  虽然控制台在接受字符串时显示了中文,但是在输出该字符串时却出现了乱码,而控制台还是可以输出中文,这就是 SetConsoleCP 的作用,更改输入内容的代码页。
  与 GetConsoleOutputCP 一样,GetConsoleCP 也能获取活动的代码页,但是获取的是活动的输入代码页的代码。

七、修改控制台文本样式

一、使用API

  SetConsoleTextAttribute 函数可以更改接下来输出的文字的样式,通过下方代码您就可以了解其作用。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(outputHandle, 0xf0);printf("白底黑字\n");SetConsoleTextAttribute(outputHandle, 0x4e);printf("红底黄字\n");SetConsoleTextAttribute(outputHandle, 0x1a);printf("蓝底绿字\n");SetConsoleTextAttribute(outputHandle, 0x801a);printf("蓝底绿字 + 下划线\n");SetConsoleTextAttribute(outputHandle, 0x4e | COMMON_LVB_UNDERSCORE);printf("红底黄字 + 下划线\n");SetConsoleTextAttribute(outputHandle, 0x07);printf("黑底灰字\n");return 0;
}


  此 API 在新旧版控制台均能工作。
  Windows 还提供了其它的样式。

下方内容摘自 consoleapi2.h

//
// Attributes flags:
//#define FOREGROUND_BLUE      0x0001 // text color contains blue.
#define FOREGROUND_GREEN     0x0002 // text color contains green.
#define FOREGROUND_RED       0x0004 // text color contains red.
#define FOREGROUND_INTENSITY 0x0008 // text color is intensified.
#define BACKGROUND_BLUE      0x0010 // background color contains blue.
#define BACKGROUND_GREEN     0x0020 // background color contains green.
#define BACKGROUND_RED       0x0040 // background color contains red.
#define BACKGROUND_INTENSITY 0x0080 // background color is intensified.
#define COMMON_LVB_LEADING_BYTE    0x0100 // Leading Byte of DBCS
#define COMMON_LVB_TRAILING_BYTE   0x0200 // Trailing Byte of DBCS
#define COMMON_LVB_GRID_HORIZONTAL 0x0400 // DBCS: Grid attribute: top horizontal.
#define COMMON_LVB_GRID_LVERTICAL  0x0800 // DBCS: Grid attribute: left vertical.
#define COMMON_LVB_GRID_RVERTICAL  0x1000 // DBCS: Grid attribute: right vertical.
#define COMMON_LVB_REVERSE_VIDEO   0x4000 // DBCS: Reverse fore/back ground attribute.
#define COMMON_LVB_UNDERSCORE      0x8000 // DBCS: Underscore.#define COMMON_LVB_SBCSDBCS        0x0300 // SBCS or DBCS flag.

  在 cmd 中使用 “color /?” 命令可以查看十六进制表示下颜色的排列,方法可在 【C++】Windows控制台API基本使用(下) 查阅。

二、使用ANSI转义序列

  下方代码将会演示使用 ANSI 转义序列输出多样式文本。

#include <stdio.h>int main(int argc, char* argv[]) {printf("\x1b[31mText\x1b[0m\n");printf("\x1b[31;1mText\x1b[0m\n");printf("\x1b[31;1;47;4mText\x1b[0m\n");printf("\x1b[31;1;7mText(反色)\x1b[0m\n");return 0;
}


  这种写法在旧版控制台无效。

八、控制台窗口标题

一、获取控制台窗口标题

  GetConsoleTitle 函数可以获取窗口的标题。其原型为:

WINBASEAPI DWORD WINAPI GetConsoleTitleA(_Out_writes_(nSize) LPSTR lpConsoleTitle,_In_ DWORD nSize);WINBASEAPI DWORD WINAPI GetConsoleTitleW(_Out_writes_(nSize) LPWSTR lpConsoleTitle,_In_ DWORD nSize);
#ifdef  UNICODE
#define GetConsoleTitle  GetConsoleTitleW
#else
#define GetConsoleTitle  GetConsoleTitleA
#endif // !UNICODE

  上方截取了 consoleapi2.h 中有关 (旧API)GetConsoleTitle 的部分。在宏 UNICODE 存在时,GetConsoleTitle 为 GetConsoleTitlteW,否则为 GetConsoleTitleA。GetConsoleTitleW 是 GetConsoleTitle 的宽字符版,而 GetConsoleTitleA 是摘字符版。在源文件中取消宏 UNICODE 就能使用 ANSI 版。下面的代码演示了如何正确使用 GetConsoleTitleA 和 GetConsoleTitleW。

#include <stdio.h>
#include <wchar.h>
#include <Windows.h>int main(int argc, char* argv[]) {char    titleA[30]    =  "";wchar_t titleW[30]    = L"";GetConsoleTitle (titleW, 30);GetConsoleTitleA(titleA, 30);printf("titleA = %s\n""titleW = %ls\n",titleA, titleW);return 0;
}


  Windows API 定义了不同的字符串类型,下方截取了 GetConsoleTitle 及其宽字符和窄字符版所用到的类型的定义。

// 截取自 Winnt.h
typedef _Null_terminated_ CHAR *NPSTR, *LPSTR, *PSTR;
typedef _Null_terminated_ WCHAR *NWPSTR, *LPWSTR, *PWSTR;

二、设置窗口标题

一、使用旧版API

  (旧API)SetConsoleTitle 可以设置窗口标题,与 (旧API)GetConsoleTitle 一样,它也分宽字符和窄字符两种版本。下方代码演示了如何使用这些 API。

#include <stdio.h>
#include <wchar.h>
#include <Windows.h>int main(int argc, char* argv[]) {char    titleA[] =  "titleA";wchar_t titleW[] = L"titleW";SetConsoleTitleA(titleA);printf("TitleA.\n");getchar();// 或在宏 UNICODE 存在时使用 SetConsoleTitle(titleW);SetConsoleTitleW(titleW);printf("TitleW.\n");return 0;
}

二、使用转义序列

  Windows 新版控制台支持使用转义序列控制控制台窗口标题,其格式为:

\033]0;标题字符串\007

  转义序列的标题使用响铃字符(ASCII: 7)为结尾,下方代码演示了此转义序列。

#include <stdio.h>int main() {printf("\033]0;新标题\007");return 0;
}

三、获取控制台原始标题

  (旧API)GetConsoleOriginalTitle 函数可以获取控制台的原始标题。此函数也分宽字符和窄字符两种版本,其参数与 (旧API)GetConsoleTitle 的参数一样。下面的程序将会演示其功能。

#include <iostream>
#include <Windows.h>int main(int argc, char* argv[]) {using std::cout;HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);// 设置新标题SetConsoleTitleA("新标题");char originalTitle[50] = "";GetConsoleOriginalTitleA(originalTitle, 50);cout << "原始标题: " << originalTitle << std::endl;return 0;
}


  (旧API)GetConsoleOriginalTitle 在 (旧API)SetConsoleTitle 后调用,但 (旧API)GetConsoleOriginalTitle 获取的标题不是现在的新标题,而是开始的标题。

九、置顶窗口

  SetWindowPos 函数可以实现此功能。其原型为:

// 摘自 WinUser.h
WINUSERAPI BOOL WINAPI SetWindowPos(_In_ HWND hWnd,                // 要操作的窗口_In_opt_ HWND hWndInsertAfter,_In_ int X,                    // 窗口新的位置的X轴_In_ int Y,                    // 窗口新的位置的Y轴_In_ int cx,                   // 窗口的新宽度_In_ int cy,                   // 窗口的新高度_In_ UINT uFlags               // 附加属性;

  uFlags将决定 X、Y、cx 和 cy 四个参数是否生效。下方代码演示了如何置顶和不置顶窗口。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {//   获取控制台窗口的句柄HWND window = GetConsoleWindow();// 置顶窗口SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);// 不置顶窗口SetWindowPos(window, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);/*SetWindowPos 函数的 uFlags 参数如果包括 SWP_NOMOVE,则 X 和 Y 参数不生效,窗口就不会移动。如果包含 SWP_NOSIZE,则窗口大小不会被重新定义。*/return 0;
}

  上述代码中 window 是一个控制台窗口对象(或窗口句柄),GetConsoleWindow 将会返回控制台窗口的句柄。调用 SetWindowPos 时传递了控制台窗口的句柄,则接下来要操作的就是控制台窗口。
  与 GetConsoleWindow 的 API 是 GetForegroundWindow 函数,它也能返回窗口句柄,但其返回的是当前焦点所在的窗口的句柄。

  上图中正在被操作的窗口就是当前焦点所在窗口。
  下方程序演示了置顶当前焦点所在窗口。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {//   获取当前焦点所在窗口的句柄HWND window = GetForegroundWindow();// 置顶窗口SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);return 0;
}

十、移动窗口

  SetWindowPos 依然能够实现此功能。刚才讲过,若 uFlags 包含 SWP_NOMOVE 则指定窗口不会移动,忽略参数 X 和 Y,如果去掉就可以实现移动窗口。下方代码演示了此功能。

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>int main(int argc, char* argv[]) {//   获取控制台窗口HWND window = GetConsoleWindow();// 获取到的坐标unsigned int getPos[2] = {0};while (1) {// 获取 X 轴printf("X:");scanf("%ud", &getPos[0]);// 获取 Y 轴printf("Y:");scanf("%ud", &getPos[1]);getchar();// 移动窗口SetWindowPos(window, NULL, getPos[0], getPos[1], 0, 0, SWP_NOSIZE);printf("\n");}return 0;
}

十一、(旧API)ScrollConsoleScreenBuffer

  (旧API)ScrollConsoleScreenBuffer 函数可以移动控制台指定区域的文本到制定区域,其原型为:

// 摘自 Microsoft Docs
BOOL WINAPI ScrollConsoleScreenBuffer(_In_           HANDLE     hConsoleOutput,_In_     const SMALL_RECT *lpScrollRectangle,_In_opt_ const SMALL_RECT *lpClipRectangle,_In_           COORD      dwDestinationOrigin,_In_     const CHAR_INFO  *lpFill
);

Microsoft Docs 这么描述:
参数
hConsoleOutput[in]
控制台屏幕缓冲区的句柄。 句柄必须具有 GENERIC_READ 访问权限。 有关详细信息,请参阅控制台缓冲区安全性和访问权限。

lpScrollRectangle [in]
指向 SMALL_RECT 结构的指针,该结构的成员指定要移动的控制台屏幕缓冲区矩形的左上角和右下角坐标。

lpClipRectangle [in,可选]
指向 SMALL_RECT 结构的指针,该结构的成员指定受滚动影响的控制台屏幕缓冲区矩形的左上角和右下角坐标。 此指针可以为 NULL。

dwDestinationOrigin [in]
指定lpScrollRectangle内容的新位置的左上角的oozie.coord.application.path结构,以字符为字符。

lpFill [in]
一个指向 CHAR_INFO 结构的指针,该结构指定用于填充 lpScrollRectangle 和 lpClipRectangle 的交集中的单元格的字符和颜色属性,这些单元格在移动后留空。

  此 API 的更多信息请访问 【C++】Windows控制台API基本使用(下) 有关移动控制台文本的章节。

十二、清空控制台

  Microsoft Docs 提供了 Cmd 中 cls 指令清除控制台的代码。

摘自 Microsoft Docs

#include <windows.h>void cls(HANDLE hConsole)
{CONSOLE_SCREEN_BUFFER_INFO csbi;SMALL_RECT scrollRect;COORD scrollTarget;CHAR_INFO fill;// Get the number of character cells in the current buffer.if (!GetConsoleScreenBufferInfo(hConsole, &csbi)){return;}// Scroll the rectangle of the entire buffer.scrollRect.Left = 0;scrollRect.Top = 0;scrollRect.Right = csbi.dwSize.X;scrollRect.Bottom = csbi.dwSize.Y;// Scroll it upwards off the top of the buffer with a magnitude of the entire height.scrollTarget.X = 0;scrollTarget.Y = (SHORT)(0 - csbi.dwSize.Y);// Fill with empty spaces with the buffer's default text attribute.fill.Char.UnicodeChar = TEXT(' ');fill.Attributes = csbi.wAttributes;// Do the scrollScrollConsoleScreenBuffer(hConsole, &scrollRect, NULL, scrollTarget, &fill);// Move the cursor to the top left corner too.csbi.dwCursorPosition.X = 0;csbi.dwCursorPosition.Y = 0;SetConsoleCursorPosition(hConsole, csbi.dwCursorPosition);
}int main(void)
{HANDLE hStdout;hStdout = GetStdHandle(STD_OUTPUT_HANDLE);cls(hStdout);return 0;
}

十三、控制台字体

一、获取控制台字体

  若要更改或获取控制台字体,应在代码中添加下方代码块:

struct CONSOLE_FONT
{DWORD index;COORD dim;
};
typedef BOOL(WINAPI* PROCSETCONSOLEFONT)(HANDLE, DWORD);
typedef BOOL(WINAPI* PROCGETCONSOLEFONTINFO)(HANDLE, BOOL, DWORD, CONSOLE_FONT*);
typedef COORD(WINAPI* PROCGETCONSOLEFONTSIZE)(HANDLE, DWORD);
typedef DWORD(WINAPI* PROCGETNUMBEROFCONSOLEFONTS)();
typedef BOOL(WINAPI* PROCGETCURRENTCONSOLEFONT)(HANDLE, BOOL, CONSOLE_FONT*);
PROCSETCONSOLEFONT SetConsoleFont;
PROCGETCONSOLEFONTINFO GetConsoleFontInfo;
PROCGETNUMBEROFCONSOLEFONTS GetNumberOfConsoleFonts;typedef BOOL(WINAPI* PROCGETCONSOLEDISPLAYMODE)(LPDWORD);
typedef BOOL(WINAPI* PROCSETCONSOLEDISPLAYMODE)(HANDLE, DWORD, LPDWORD);

  例如:

#include <Windows.h>
#include <stdio.h>
struct CONSOLE_FONT
{DWORD index;COORD dim;
};
typedef BOOL(WINAPI* PROCSETCONSOLEFONT)(HANDLE, DWORD);
typedef BOOL(WINAPI* PROCGETCONSOLEFONTINFO)(HANDLE, BOOL, DWORD, CONSOLE_FONT*);
typedef COORD(WINAPI* PROCGETCONSOLEFONTSIZE)(HANDLE, DWORD);
typedef DWORD(WINAPI* PROCGETNUMBEROFCONSOLEFONTS)();
typedef BOOL(WINAPI* PROCGETCURRENTCONSOLEFONT)(HANDLE, BOOL, CONSOLE_FONT*);
PROCSETCONSOLEFONT SetConsoleFont;
PROCGETCONSOLEFONTINFO GetConsoleFontInfo;
PROCGETNUMBEROFCONSOLEFONTS GetNumberOfConsoleFonts;typedef BOOL(WINAPI* PROCGETCONSOLEDISPLAYMODE)(LPDWORD);
typedef BOOL(WINAPI* PROCSETCONSOLEDISPLAYMODE)(HANDLE, DWORD, LPDWORD);int main(int argc, char* argv[]) {printf("Hello world!\n");return 0;
}

  GetCurrentConsoleFontEx 函数可以获取字体信息,其原型为:

WINBASEAPI BOOL WINAPI GetCurrentConsoleFontEx(_In_ HANDLE hConsoleOutput,                        // 输出句柄。_In_ BOOL bMaximumWindow,                          // 如果此参数为 TRUE,则为最大窗口大小检索字体信息。 如果此参数为 FALSE,则将检索当前窗口大小的字体信息。_Out_ PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx  // 字体结构体);

  参数 lpConsoleCurrentFontEx 的类型 PCONSOLE_FONT_INFOEX 是一个指向 CONSOLE_FONT_INFOEX 的指针,CONSOLE_FONT_INFOEX 的定义为:

typedef struct _CONSOLE_FONT_INFOEX {ULONG cbSize;                // 结构体的大小(以字节为单位)。DWORD nFont;                 // 系统控制台字体表中字体的索引。COORD dwFontSize;            // 字体大小,规定了字体的长与宽。UINT FontFamily;             // 字体间距和系列。UINT FontWeight;             // 字体粗细。权重的范围可以是 100 到 1000,以 100 的倍数表示。WCHAR FaceName[LF_FACESIZE]; // 字体名称。
} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;

  在调用 GetCurrentConsoleFontEx 前必须设置 cbSize 的值,否则函数调用失败。其值可以设为 sizeof(CONSOLE_FONT_INFOEX),下方代码将会演示如何使用此函数和结构。

#include <iostream>
#include <Windows.h>int main(int argc, char* argv[]) {using std::cout;using std::wcout;// 控制台字体对象CONSOLE_FONT_INFOEX cfi;cfi.cbSize = sizeof(cfi);// 输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);// 获取字体信息GetCurrentConsoleFontEx(outputHandle, 0, &cfi);// 输出字体信息wcout << "字体: " << cfi.FaceName;cout << std::endl;cout << "大小: " << cfi.dwFontSize.X << "*" << cfi.dwFontSize.Y << std::endl<< "权重: " << cfi.FontWeight << std::endl;return 0;
}


  不知道为什么,无法获取到控制台字体。

二、设置控制台字体

  (旧版API)SetCurrentConsoleFontEx 函数可以修改字体,需要搭配 (旧版结构)CONSOLE_FONT_INFOEX 结构体使用。下方的程序将会演示此功能。

/*此程序将会演示如何使用 SetCurrentConsoleFontEx 函数更改控制台字体为“楷体”。有一点需要注意,更改的字体必须是等宽字体,不是等宽字体的修改后都会变成默认字体。
*/
#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 控制台字体对象CONSOLE_FONT_INFOEX cfi;cfi.cbSize = sizeof(cfi);       // 不要忘记设置结构体的大小// 输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);printf("字体会在按下回车后更改。\n");getchar();// 更改字体cfi.nFont        = 0;cfi.dwFontSize.X = 8;           // 更改字体宽度cfi.dwFontSize.Y = 18;          // 更改字体高度cfi.FontFamily   = FF_DONTCARE;cfi.FontWeight   = FW_NORMAL;wcscpy_s(cfi.FaceName, L"黑体"); // 更改字体名SetCurrentConsoleFontEx(outputHandle, FALSE, &cfi); // 应用更改printf("更改完毕。\n");return 0;
}

三、字体无法使用

  某些字体可能无法被应用,这可能是由以下原因引发的:
  1. 参数未正确设置。
  2. 字体不支持当前代码页。

一、参数未正确设置

  请检查代码中是否正确设置控制台字体对象的 cbSize 成员,如果有,那就是因为其它项没有正确设置。
  程序需要设置所使用字体的各种信息,如权重、名称、大小等,如果您不知道如何设置它们,可以考虑从当前设置中设置。也就是在使用 (旧版API)SetCurrentConsoleFontEx 前先调用 GetCurrentConsoleFontEx,下面是上方代码的改进。

#include <stdio.h>
#include <Windows.h>int main(int argc, char* argv[]) {// 控制台字体对象CONSOLE_FONT_INFOEX cfi;cfi.cbSize = sizeof(cfi);       // 不要忘记设置结构体的大小// 输出句柄HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);// 获取字体GetCurrentConsoleFontEx(outputHandle, 0, &cfi);printf("字体会在按下回车后更改。\n");getchar();// 更改字体cfi.dwFontSize.X = 8;           // 更改字体宽度cfi.dwFontSize.Y = 18;          // 更改字体高度cfi.FontFamily   = FF_DONTCARE;cfi.FontWeight   = FW_NORMAL;wcscpy_s(cfi.FaceName, L"Sarasa Fixed Slab SC Semibold"); // 更改字体名SetCurrentConsoleFontEx(outputHandle, FALSE, &cfi); // 应用更改printf("更改完毕。\n");return 0;
}

附录 - 后篇链接

  【C++】Windows控制台API基本使用(下)。

【C++】Windows控制台API基本使用(上)相关推荐

  1. 计算机控制台win10,Win10系统打开Windows控制台的方法

    Windows控制台的功能是帮助基于Windows的计算机在未正确启动或根本无法启动时进行恢复操作,当系统遇到问题的时候可以通过打开Windows控制台来进行解决,然而很多用户在升级到win10系统之 ...

  2. C#使用Windows API实现桌面上的遮罩层(鼠标穿透)

    C#实现实现桌面上的遮罩层(鼠标穿透)主要通过一下几个API函数来实现:GetWindowLong,SetWindowLong,SetLayeredWindowAttributes.其中有一个Wind ...

  3. windows常用 API函数

    系统API查询 http://www.vbgood.com/api.html http://hi.baidu.com/3582077/item/9cc3483b581f53c5392ffae3 第一个 ...

  4. 89个windows常用API调用

    VC API常用函数简单例子大全 第一个:FindWindow根据窗口类名或窗口标题名来获得窗口的句柄,该函数返回窗口的句柄 函数的定义:HWND WINAPI FindWindow(LPCSTR l ...

  5. Dll注入过滤任意Windows控制台命令行输入

    命令提示符也就是命令行控制台,新版本也叫做Windows 终端.如何做到当命令被输入控制台窗口后能够做到过滤呢? 其中,有一种就是键盘钩子判断键盘输入,但实用性可能不高. 另外一种方法就是获取控制台缓 ...

  6. windows常用API函数

    windows常用API函数 http://www.vbgood.com/api.html http://hi.baidu.com/3582077/item/9cc3483b581f53c5392ff ...

  7. Windows恶意软件API调用特征分析

    本文讲的是Windows恶意软件API调用特征分析, 1.背景 目标: 1)找到病毒调用概率高的API 2)找到病毒调用概率不高,但是当调用频次高的时候,是病毒概率高的API. 通常对病毒使用API的 ...

  8. 如何在C#Windows控制台应用程序中更新当前行?

    使用C#构建Windows控制台应用程序时,是否可以在不扩展当前行或转到新行的情况下写入控制台? 例如,如果我想显示一个百分比,该百分比代表一个过程到完成为止的距离,我只想在与光标相同的行上更新值,而 ...

  9. 安装 Windows 自动化 API 3.0 后,Visual Studio 2010 的运行速度更快

    安装 Windows 自动化 API 3.0 后,Visual Studio 2010 的运行速度更快 本文适用于以下产品: Microsoft Visual Studio 2010 如果未安装 Wi ...

最新文章

  1. 如何编辑PDF文件,怎么修改PDF中的文字
  2. 【Kotlin】函数类型 ( 函数类型 | 带参数名称的参数列表 | 可空函数类型 | 复杂函数类型 | 带接收者函数类型 | 函数类型别名 | 函数类型实例化 | 函数调用 )
  3. 端计算(4)-kotlin(2)
  4. 3 SAP STMS跨服务器传请求号
  5. abb机器人伺服电机报闸是什么_ABB机器人电池更换时回零程序Reference
  6. 【论文解读】AVOD-Net 用于自动驾驶的聚合视图3D对象检测网络
  7. 移动端微信、QQ、浏览器调用qq临时会话功能
  8. 【前端】vue阶段案例:组件化-房源展示
  9. 线性函数、非线性函数与线性回归的区别
  10. java培训课程有哪些
  11. 浙江大学计算机保研条件_2020年计算机系保研推免记录(浙江大学软件学院)...
  12. 使用FCEUX调试器寻找并修改游戏初始物品
  13. 如何查看进程所打开的端口
  14. 什么是垂直搜索引擎(之二)
  15. P2320 [HNOI2006] 鬼谷子的钱袋
  16. oracle 数据转换不报错,oracle数据库与hibernate方言转型异常
  17. 【bzoj4146】 [AMPPZ2014]Divisors
  18. Scanner调用next方法时报错NoSuchElementException原因,及对Scanner的一些总结
  19. 【问题】Ubuntu系统使用搜狗输入法时,只能打出英文,无法输中文。
  20. spark Java oracle,spark2.x由浅入深深到底系列六之RDD java api用JdbcRDD读取关系型数据库...

热门文章

  1. 日历算法 [ 解读算法的乐趣 ]
  2. 芝麻叶泡水的营养价值 芝麻叶泡水的功效与作用
  3. 小提琴1234567位置图解_小提琴指法图233(免费)
  4. 计算机组装与维护提问问题,计算机组装与维护浅析论文
  5. 计算机计算出负数,cfa计算器 负数
  6. 看见幽灵:Vitalik 终于规范化了以太坊 Casper 协议升级
  7. PHP生成曲线统计图表示例,直方图等数据
  8. C/C++ 排序专题
  9. 关于RC电路耦合、相移、滤波、微分、积分的那些事儿~
  10. HTML 行内元素 与 块级元素