Windows控制台API基本使用(下)

文章目录
前言
十三、控制台字体
二、字体不支持当前代码页
十四、填充指定行
一、直接输出
二、使用API
三、使用转义序列
十五、DEC线条绘制功能
十六、备用和主缓冲区
十七、控制台模式(Console mode)
一、获取控制台模式
二、设置控制台模式
十八、移动控制台上的文本
一、直通链接
二、使用 API 的概述
三、此 API 的参数
四、SMALL_RECT 结构
五、示例
一、示例描述
二、代码
六、特性++?
十九、为文本盖上阴影
一、重绘大法(简介)
二、使用 API
一、文档链接
二、API 介绍
一、参数
二、孪生兄弟
三、示例
前言
  建议在观看此文章前先观看【C++】Windows控制台API基本使用(上)。

十三、控制台字体
二、字体不支持当前代码页
  这应该是新版控制台的一项限制,在旧版控制台,笔者并未见过某些字体因不支持中文而不能切换,所以程序可以通过更改代码页以使用字体。
  笔者电脑中安装有 Cascadia Code 字体,这里使用这款字体做演示。

#include
#include

int main() {
    // 切换编码
    SetConsoleOutputCP(437);
    SetConsoleCP(437);

    // 设置字体
    CONSOLE_FONT_INFOEX fontInfo;
    fontInfo.cbSize = sizeof(fontInfo);

    // 获取字体信息
    GetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &fontInfo);

    // 自定义设置
    fontInfo.dwFontSize.X = 8;
    fontInfo.dwFontSize.Y = 20;
    wcscpy_s(fontInfo.FaceName, L"Cascadia Code");

    // 让更改生效
    SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &fontInfo);

    printf("Test....\n");
    getchar();

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  字体生效了。


十四、填充指定行
  Windows 控制台特性,若程序频繁朝控制台输出文本,则当用户标记了指定区域并复制会导致输出错位。

  填充指定行可以补救这种问题。要填充指定行有两种方法,一种直接输出,另一种用API。

一、直接输出
  直接使用cout或printf输出,这种方法简单,易用,并且可以跨平台,请看例子。

#include
#include

int main() {
    // 获取控制台缓冲区大小
    // 获取输出句柄
    HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO info;

    GetConsoleScreenBufferInfo(outputHandle, &info);

    // 输出要被删除的文本
    printf("这行文本会在按下回车后删除。");
    getchar();
    // 移动光标到上一行
    printf("\033[1A");

    // 删除文本
    printf("\r");
    int rangeI = 0;
    for (; rangeI < info.dwSize.X; rangeI++) {
        printf(" ");
    }

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

  这种方法的优缺点:

优点:
  1. 跨平台。
  2. 易写,易用。
缺点:
  1. 在填充最后一行时光标会换行,让第一行离开显示范围。
  2. Windows 旧版控制台(新版不知道有没有这个特性)特性会导致填充后,光标后的第一个字符出现残缺。
1
2
3
4
5
6
二、使用API
  使用 (旧版API)FillConsoleOutputCharacter 可以使用指定字符填充行,下面是此 API 的原型:

BOOL WINAPI FillConsoleOutputCharacter(
  _In_  HANDLE  hConsoleOutput,        // 输出句柄
  _In_  TCHAR   cCharacter,            // 要填充的字符
  _In_  DWORD   nLength,               // 填充长度
  _In_  COORD   dwWriteCoord,          // 要写入的第一个位置的坐标
  _Out_ LPDWORD lpNumberOfCharsWritten // 返回实际填充的字符的数量
);
1
2
3
4
5
6
7
  此函数执行成功将会返回非0值。利用这个函数和上次介绍的 GetConsoleScreenBufferInfo 可以填充一行。

#include
#include

int main() {
    // 获取控制台缓冲区大小
    // 获取输出句柄
    HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_SCREEN_BUFFER_INFO info {0}; // 缓冲区信息

    // 输出要被删除的文本
    printf("#include \n");
    printf("int main() {\n");
    printf("\tprintf(\"Hello world!\\n\");\n");
    printf("\treturn 0;\n");
    printf("}\n");

    GetConsoleScreenBufferInfo(outputHandle, &info); // 获取缓冲区信息
    getchar();

    // 填充行(核心部分)
    DWORD getWrite = 0;
    FillConsoleOutputCharacterA(outputHandle, ' ', info.dwSize.X, COORD {0, 0}, &getWrite);

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

三、使用转义序列
  没错,ANSI转义序列也能解决这个问题。以下内容从控制台虚拟终端序列截取。

对于以下行,参数 有 3 个有效值:

0 的擦除范围是从当前光标位置(含)到行/显示的末尾
1 的擦除范围是从行/显示开始到当前光标位置(含)
2 的擦除范围是整行/显示
序列    代码    说明    行为
ESC [ < n > J    ED    显示中的擦除    将 指定的当前视区/屏幕中的所有文本替换为空格字符
ESC [ < n > K    EL    行中的擦除    将行上的所有文本替换为由 指定的光标与空格字符
下方代码演示了如何使用此转义序列擦除行。

#include

int main() {
    // 输出要被删除的文本
    printf("这行文本会在按下回车后被擦除。");
    getchar();
    // 移动光标到上一行
    printf("\033[1A");

    // 删除文本
    printf("\r\033[2K\n");

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

十五、DEC线条绘制功能
  若您之前看过一些别的控制台 API 文章,您或许会见到某些在窗口中间绘制小窗口的实例,但是这种方法有一个缺点,只能在 437 代码页绘制,不然无法显示窗口边框。但 DEC 特殊图形字符集提供了别的选择,您可以查看微软的控制台虚拟终端序列的文档了解 DEC 特殊图形字符集。以下是从 Microsoft Docs 截取的内容。

下表显示了 ASCII 字符与线条绘制字符之间的对应关系。

Hex    ASCII    DEC 线条绘制
0x6a    j    ┘
0x6b    k    ┐
0x6c    l    ┌
0x6d    m    └
0x6e    n    ┼
0x71    q    ─
0x74    T    ├
0x75    u    ┤
0x76    v    ┴
0x77    w    ┬
0x78    x    │
  转义序列\033(0允许控制台使用 DEC 特殊图形字符集,而\033(B允许控制台返回ASCII字符集。下方代码演示了使用 DEC 特殊图形字符集绘制一个小区域并显示“Hello world!”。

#include
#include

int main() {
    printf("DEC 特殊图形字符集测试程序。\n");
    printf("\033[45;37;1m"); // 使用 DEC 特殊图形字符集

    // 绘制区域
    // 为了对齐,第十一行必须打空格,但是在 437 代码页下是不用的
    printf("\033(0lqqqqqqqqqqqqqk \n");
    printf("x\033(B Hello world!\033(0x \n");
    printf("\033(0mqqqqqqqqqqqqqj ");

    printf("\033(B\033[0m\n"); // 返回 ASCII 字符集

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

  虽然图形字符有一个普通字符那么高和宽,但总算能在非 437 代码页下绘制小区域了。您可以移动光标或填补空格,让绘制的小区域居中。

十六、备用和主缓冲区
  若您使用过 vim 之类的控制台软件,您可能会好奇这些软件是怎么做到在全屏后退出依然能够保持原缓冲区的。\033[?1049h转义序列能够让程序使用备用缓冲区,而\033[?1049l可以回到主缓冲区。

十七、控制台模式(Console mode)
  【C++】Windows控制台API基本使用(上) 中有关 打开 ANSI 转义序列 的内容里出现了 GetConsoleMode 和 SetConsoleMode,这两个 API 可以设置控制台的模式,启用或关闭指定功能。

一、获取控制台模式
  GetConsoleMode 可以获取控制台模式,其原型如下。

转自 Microsoft Docs。

BOOL WINAPI GetConsoleMode(
 _In_  HANDLE  hConsoleHandle,
 _Out_ LPDWORD lpMode
);
1
2
3
4
  与其它控制台 API 一样,它也需要一个控制台句柄,第二个参数是一个指向接收控制台模式的变量的指针。下方是一个有关 GetConsoleMode 的示例。

#include
#include

int main()
{
    // 控制台输入输出句柄
    HANDLE inputHandle  , outputHandle;
    // 控制台模式
    DWORD  inConsoleMode, outConsoleMode;

    inputHandle  = GetStdHandle(STD_INPUT_HANDLE);
    outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    // 获取控制台模式
    // 获取输入的控制台模式
    GetConsoleMode(inputHandle , &inConsoleMode);
    // 获取输出的控制台模式
    GetConsoleMode(outputHandle, &outConsoleMode);

    printf("控制台模式(输入):\n"
           "值: %u\n", inConsoleMode);
    printf("控制台模式(输出):\n"
           "值: %u\n", outConsoleMode);

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


  调用 GetConsoleMode 时传入的句柄将会决定获取的是输入还是输出的控制台模式。
  若要判断控制台模式启用和禁用的内容,可以使用 & 运算符。下面是有关控制台模式的宏。

摘自 Microsoft Docs。
表 1 (输入)

值    含义
ENABLE_ECHO_INPUT 0x0004    由 ReadFile 或 ReadConsole 函数读取的字符在键入到控制台时,将被写入到活动屏幕缓冲区 。 只有同时启用了 ENABLE_LINE_INPUT 模式时,才能使用此模式。
ENABLE_INSERT_MODE 0x0020    如果启用,在控制台窗口中输入的文本将插入到当前光标位置,并且不会覆盖该位置后面的所有文本。 如果禁用,则将覆盖后面的所有文本。
ENABLE_LINE_INPUT 0x0002    仅当读取回车符时,才返回 ReadFile 或 ReadConsole 函数 。 如果禁用此模式,则将在有一个或多个字符可用时返回函数。
ENABLE_MOUSE_INPUT 0x0010    如果鼠标指针位于控制台窗口的边框内并且窗口具有键盘焦点,则通过移动鼠标和按下按钮生成的鼠标事件会放置在输入缓冲区中。 即使启用此模式,ReadFile 或 ReadConsole 也会丢弃这些事件 。
ENABLE_PROCESSED_INPUT 0x0001    Ctrl+C 由系统处理,且不会放入输入缓冲区中。 如果 ReadFile 或 ReadConsole 正在读取输入缓冲区,则其他控制键将由系统处理,且不会返回到 ReadFile 或 ReadConsole 缓冲区中 。 如果还启用了 ENABLE_LINE_INPUT 模式,则 Backspace、回车符和换行符将由系统处理。
ENABLE_QUICK_EDIT_MODE 0x0040    用户可通过此标志使用鼠标选择和编辑文本。 要启用此模式,请使用 ENABLE_QUICK_EDIT_MODE
ENABLE_WINDOW_INPUT 0x0008    更改控制台屏幕缓冲区大小的用户交互将记录到控制台的输入缓冲区中。 使用 ReadConsoleInput 函数的应用程序可从输入缓冲区中读取有关这些事件的信息,但使用 ReadFile 或 ReadConsole 的应用程序无法读取这些事件 。
ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200    如果设置此标志,则会指导虚拟终端处理引擎将控制台窗口收到的用户输入转换为可由支持的应用程序通过 WriteFile 或 WriteConsole 函数检索的控制台虚拟终端序列 。

此标志通常与输出句柄上的 ENABLE_VIRTUAL_TERMINAL_PROCESSING 一起使用,以连接到只通过虚拟终端序列进行通信的应用程序。

如果 hConsoleHandle 参数是屏幕缓冲区句柄,则模式可以是下列的一个或多个值。 创建屏幕缓冲区后,默认情况下将启用这两种输出模式。
表 2 (输出)

值    含义
ENABLE_PROCESSED_OUTPUT 0x0001    针对 ASCII 控制序列对由 WriteFile 或 WriteConsole 函数写入的字符,或者由 ReadFile 或 ReadConsole 函数回显的字符进行分析,并执行正确的操作 。 处理 Backspace、Tab、报警符、回车符和换行符。
ENABLE_WRAP_AT_EOL_OUTPUT 0x0002    使用 WriteFile 或 WriteConsole 写入时,或者使用 ReadFile 或 ReadConsole 回显时,当光标到达当前行的末尾时,它将移到下一行的开头 。 这会导致控制台窗口中显示的行在光标前进到窗口的最后一行时自动向上滚动。 还会导致控制台屏幕缓冲区的内容在光标前进到控制台屏幕缓冲区的最后一行时向上滚动(…/丢弃控制台屏幕缓冲区的顶行)。 如果禁用此模式,则将覆盖该行中的最后一个字符以及后面的任何字符。
ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004    使用 WriteFile 或 WriteConsole 写入时,将为 VT100 和类似控制字符序列分析字符,这些字符序列可控制光标移动、颜色/字体模式以及其他也可通过现有控制台 API 执行的操作 。 有关详细信息,请参阅控制台虚拟终端序列。
DISABLE_NEWLINE_AUTO_RETURN 0x0008    使用 WriteFile 或 WriteConsole 写入时,这会将附加状态添加到行尾换行,从而可能导致延迟光标移动和缓冲区滚动操作 。

正常情况下,当设置 ENABLE_WRAP_AT_EOL_OUTPUT 并且文本到达行尾时,光标将立即移动到下一行,缓冲区的内容将向上滚动一行。 与设置此标志相比,滚动操作和光标移动会延迟到下一个字符到达为止。 写入的字符将在该行的最后位置输出,并且光标将保持在该字符的上方,就好像 ENABLE_WRAP_AT_EOL_OUTPUT 已禁用一样,但将输出下一个可输出字符,就像 ENABLE_WRAP_AT_EOL_OUTPUT 已启用一样 。 不会覆盖字符。 具体而言,光标会快速向下移动到下一行,必要时执行滚动,输出字符,然后光标向前移动一个位置。

此标志通常与 ENABLE_VIRTUAL_TERMINAL_PROCESSING 设置结合使用,以便更好地模拟终端模拟器,即在屏幕上(右下角位置)写入最后一个字符,但不触发立即滚动操作。
ENABLE_LVB_GRID_WORLDWIDE 0x0010    用于写入字符属性(包括 WriteConsoleOutput 和 WriteConsoleOutputAttribute)的 API 允许使用来自字符属性的标志调整文本的前景色和背景色 。 此外,使用 COMMON_LVB 前缀指定了 DBCS 标志范围。 过去,这些标志仅在中文、日语和韩语的 DBCS 代码页中起作用。

除前导字节和尾随字节标志以外,其余描述线条绘制和反向显示(…/前景色和背景色转换)的标志对于其他语言很有用,可用于强调输出的各个部分。

如果设置此控制台模式标志,则将允许在每种语言的每个代码页中使用这些属性。

默认情况下,此标志处于禁用状态,以保持与过去利用控制台的已知应用程序的兼容性,控制台会忽略非 CJK 计算机上的这些标志,以存储这些位字段,供自己使用或发生意外时使用。

请注意,使用 ENABLE_VIRTUAL_TERMINAL_PROCESSING 模式可能会导致设置 LVB 的网格和反> 向显示标志,而如果附加应用程序通过控制台虚拟终端序列请求下划线或反向显示,则此标志仍会处于禁用状态。
  了解控制台模式的宏后,就能够判断了,下面是基于 GetConsoleMode 的示例的修改版。

二、设置控制台模式
  SetConsoleMode 可以设置控制台模式。要在当前控制台模式的基础上启用或关闭功能需要先获取控制台模式,再使用 “|” 、 “&” 和 “~” 运算符调整模式,最后使用 SetConsoleMode 应用模式。其原型如下。

摘自 Microsoft Docs。

BOOL WINAPI SetConsoleMode(
  _In_ HANDLE hConsoleHandle,
  _In_ DWORD  dwMode
);
1
2
3
4
  与 GetConsoleMode 一样,传入什么句柄就设置控制台的什么模式。下面是 SetConsoleMode 与 GetConsoleMode 的示例。

#include
#include

int main()
{
    // 控制台输入输出句柄
    HANDLE inputHandle  , outputHandle;
    // 控制台模式
    DWORD  inConsoleMode, outConsoleMode;

    inputHandle  = GetStdHandle(STD_INPUT_HANDLE);
    outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    // 输出一些文本
    printf("#include \n"
           "int main()\n"
           "{\n"
           "    printf(\"Hello world!\\n\");\n"
           "    return 0;\n"
           "}\n");

    // 获取控制台模式
    // 获取输入的控制台模式
    GetConsoleMode(inputHandle , &inConsoleMode);
    // 获取输出的控制台模式
    GetConsoleMode(outputHandle, &outConsoleMode);

    // 启用快速编辑模式
    inConsoleMode |= ENABLE_QUICK_EDIT_MODE;
    // 应用
    SetConsoleMode(inputHandle, inConsoleMode);
    
    getchar();

    // 禁用快速编辑模式
    inConsoleMode &= ~ENABLE_QUICK_EDIT_MODE;
    // 应用
    SetConsoleMode(inputHandle, inConsoleMode);

    getchar();

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

  程序运行后,您可以使用鼠标来选中文本,如上图所示。

  但在按下回车后,快速编辑模式被关闭,您无法直接用鼠标选中文本了。

十八、移动控制台上的文本
一、直通链接
  此节直通链接 -->
  Microsoft Docs:
  ScrollConsoleScreenBuffer 函数;
  SMALL_RECT 结构。

二、使用 API 的概述
  Cmd 的 “cls” 命令的清屏原理就是将控制台中的一切文本移动到缓冲区之外,【C++】 Windows 控制台 API 基本使用(上) 的其中一节有 cmd 清屏命令实现的代码。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
);
1
2
3
4
5
6
7
  注: 此函数也分为 ASCII 和 Unicode 版本,区别于 lpFill 参数。使用 ASCII 版本时 API 将会在被移动区域填充 lpFill->Chars

三、此 API 的参数
名称    描述    允许的其它值
hConsoleOutput    老熟人了, 控制台输出句柄。    
lpScrollRectangle    被移动区域的四角坐标,左上角坐标为 (0, 0)。    
lpClipRectangle    受滚动影响的控制台屏幕缓冲区矩形的左上角和右下角坐标。(引用的原文链接)    NULL 或 nullptr
dwDestinationOrigin    被移动区域的新位置(左上角的 x、y 坐标)。    
lpFill    指定被移动区域位置的新字符的属性    
四、SMALL_RECT 结构
  SMALL_RECT 结构由四个 short 组成,用于标记一个四边形区域的左上角、左下角、右上角、右下角坐标,Microsoft Docs 直通链接。
  举个例子:

0    1    2    3    4    5    6
0    A    B    C    D    E    F    G
1    S    T    D    I    O    .    H
2    H    e    l    l    o        w
3    o    r    l    d    !        
  上表格用于模拟控制台。
  若要标记 第一行第一列 到 第三行第四列 的所有字符(左上角字符坐标为 (0, 0)),使用 SMALL_RECT 的表示应该为:

SMALL_RECT { 1, 1, 4, 3 };
1
  由于 C++ 会根据各成员声明先后来赋值,而 SMALL_RECT 的原型为

摘自 Microsoft Docs。

typedef struct _SMALL_RECT {
  SHORT Left;
  SHORT Top;
  SHORT Right;
  SHORT Bottom;
} SMALL_RECT;
1
2
3
4
5
6
  因此在初始化一个 SMALL_RECT 时,不要写成 “上左右下” 的形式,而要写成 “左上右下” 的形式。

五、示例
一、示例描述
  程序将会移动 (0, 0) 到 (5, 0) 的文本到 (1, 1)。

二、代码
#include
#include

int main(const int argc, char* argv[])
{
    // 输出句柄
    HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    // 获取屏幕缓冲区信息
    CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo;
    GetConsoleScreenBufferInfo(outputHandle, &consoleScreenBufferInfo);

    // 输出被移动的文本
    printf("Hello");
    getchar();

    // 移动文本
    // 被移动的区域
    SMALL_RECT movePos { 0, 0, 5, 0 };
    // 移动到的新位置的左上角坐标
    //             X  Y
    COORD newPos { 1, 1 };
    // 被移动区域填充的字符的信息
    CHAR_INFO charInfo;
    // 被移动区域留空
    charInfo.Char.AsciiChar = ' ';
    // 设置被移动区域新填充的字符的属性(也可以说成颜色,但是不太准确)
    // 属性的值为当前输出的文本的属性
    charInfo.Attributes     = consoleScreenBufferInfo.wAttributes;

    // 调用 API
    ScrollConsoleScreenBufferA(outputHandle, &movePos, NULL, newPos, &charInfo);
    
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  执行效果:


  Visual Studio 2022 编译通过。

六、特性++?
  又一个特性出来了?如下代码段更改上方示例代码,然后运行。

// 原来内容
COORD newPos { 1, 1 };

// 修改为
COORD newPos { 0, 1 };
1
2
3
4
5
  运行后你就会发现…

  Visual Studio 的调试控制台把 “H” 给吞了!?
  但是如果您在 cmd 下手动执行生成的文件,就不会出现文本被吞的情况。


十九、为文本盖上阴影
  如果您使用过一些 DOS 下的 “图形化” 程序,您可能看到过用字符堆起来的 “窗口”。

  本章主要讨论在 Windows 下 “窗口” 旁边的阴影部分是怎么做到的,如果想要做出窗口的效果,不如试试 这个 Github 项目: TVision。

一、重绘大法(简介)
  遇事不决,重绘解决。 重新使用特定属性绘制文本是一个不错的方法,但是您必须知道您重绘的位置上的文本,有关 Windows 下获取指定区域文本的 API 将会在后面的章节提到(下次修改会添加关于这个 API 的信息,但您现在可以在 Microsoft Docs 预先浏览它)。
  通过 SetConsoleTextAttribute 或 虚拟终端序列 设置被重绘的文本的颜色,使用标准库输出,或者使用 Windows 的另一个 API,这个 API 也会在之后的章节出现(以后更新该 API 的内容),就像这样。

// 注: 此代码没有被生成和测试过
#include

int main()
{
    printf("123456789123456789\r");
    printf("123");
    printf("\033[30;1m456789\033[0m\n");

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
二、使用 API
一、文档链接
  MS 文档: FillConsoleOutputAttribute、WriteConsoleOutputAttribute。

二、API 介绍
  FillConsoleOutputAttribute 可以重新着色指定位置,其原型如下。

BOOL WINAPI FillConsoleOutputAttribute(
  _In_  HANDLE  hConsoleOutput,
  _In_  WORD    wAttribute,
  _In_  DWORD   nLength,
  _In_  COORD   dwWriteCoord,
  _Out_ LPDWORD lpNumberOfAttrsWritten
);
1
2
3
4
5
6
7
一、参数
名称    描述    允许的其它值
hConsoleOutput    输出句柄    
wAttribute    重新绘制上的属性    
nLength    被绘制的字符的数量    
dwWriteCoord    开始绘制的坐标    
lpNumberOfAttrsWritten    一个指向类型为 DWORD,接收实际着色字符数量的指针    不能为 NULL 或 nullptr!
  lpNumberOfAttrsWritten 这个参数挺迷的,必须传一个 DWORD*,不然运行就会崩溃,调试一看发现程序在写入一个不可写入的地址(例如 NULL 和 nullptr)(0xC000005)……

二、孪生兄弟
  WriteConsoleOutputAttribute 是一个与此 API 很像的 API,它们的区别在于当要写入的属性数超出了控制台屏幕缓冲区中指定行的末尾的处理方式,参数与此 API 相同。

三、示例
  下方的示例程序将会重新着色从 (5, 0) 开始的 9 个字符。

// VS2022 编译通过
#include
#include

int main(const int argc, char* argv[])
{
    // 输出示例文本
    printf("Hello world! Hello world! Hello world!");
    // 输出句柄
    HANDLE outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
    // 接收实际着色数量的变量
    DWORD getWrittenCount = 0;
    // 着色起点坐标
    COORD writtenPos      = { 5, 0 };
    // 着色
    FillConsoleOutputAttribute(outputHandle, 0xF0, 9, writtenPos, &getWrittenCount);
    
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

  注: 传递给 wAttribute 的是一个数字,数字的每一位将会决定文本显示的样式,十位代表背景色,个位为前景色(此说法仅适用 16 进制表示,但是一般笔者都会使用 16 进制表示,这样比十进制方便),每一位的允许数值可以在 cmd 中键入 “color /?” 查询。

————————————————
版权声明:本文为CSDN博主「Takanawa-door」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_48178135/article/details/121715810

你可能感兴趣的:(Windows,windows,DOS,API,C++)