FormatMessageA 函数 (winbase.h)

设置消息字符串的格式。 该函数需要消息定义作为输入。 消息定义可能来自传入函数的缓冲区。 它可以来自已加载模块中的消息表资源。 或者调用方可以请求函数在系统的消息表资源(s)中搜索消息定义。 该函数基于消息标识符和语言标识符在消息表资源中查找消息定义。 该函数将格式化的消息文本复制到输出缓冲区,并处理任何嵌入的插入序列(如果请求)。

语法

DWORD FormatMessageA(
  [in]           DWORD   dwFlags,
  [in, optional] LPCVOID lpSource,
  [in]           DWORD   dwMessageId,
  [in]           DWORD   dwLanguageId,
  [out]          LPSTR   lpBuffer,
  [in]           DWORD   nSize,
  [in, optional] va_list *Arguments
);

参数

[in] dwFlags

格式设置选项以及如何解释 lpSource 参数。 dwFlags 的低序字节指定函数如何处理输出缓冲区中的换行符。 低序字节还可以指定格式化输出行的最大宽度。

此参数可以是以下一个或多个值。

价值 意义
FORMAT_MESSAGE_ALLOCATE_BUFFER
0x00000100
该函数分配足够大的缓冲区来保存格式化的消息,并将指针置于由 lpBuffer指定的地址处的分配缓冲区。 lpBuffer 参数是指向 LPTSTR;必须将指针强制转换为 LPTSTR(例如,(LPTSTR)&lpBuffer)。 nSize 参数指定要为输出消息缓冲区分配的最小 TCHAR 数。 调用方应使用 LocalFree 函数在不再需要缓冲区时释放缓冲区。

如果格式化消息的长度超过 128K 字节,则 FormatMessage 将失败,后续调用 getLastError 将返回 ERROR_MORE_DATA

在早期版本的 Windows 中,在编译 Windows 应用商店应用时,此值不可用。 从 Windows 10 开始,可以使用此值。

Windows Server 2003 和 Windows XP:

如果格式化消息的长度超过 128K 字节,则 FormatMessage 不会自动失败,并出现 ERROR_MORE_DATA错误。

FORMAT_MESSAGE_ARGUMENT_ARRAY
0x00002000
参数 参数不是 va_list 结构,而是指向表示参数的值数组的指针。

此标志不能与 64 位整数值一起使用。 如果使用 64 位整数,则必须使用 va_list 结构。

FORMAT_MESSAGE_FROM_HMODULE
0x00000800
lpSource 参数是包含要搜索的消息表资源的模块句柄。 如果此 lpSource 句柄 NULL,则将搜索当前进程的应用程序映像文件。 此标志不能与 FORMAT_MESSAGE_FROM_STRING一起使用。

如果模块没有消息表资源,则函数将失败并 ERROR_RESOURCE_TYPE_NOT_FOUND

FORMAT_MESSAGE_FROM_STRING
0x00000400
lpSource 参数是指向包含消息定义的 null 终止字符串的指针。 消息定义可能包含插入序列,就像消息表资源中的消息文本一样。 此标志不能与 FORMAT_MESSAGE_FROM_HMODULEFORMAT_MESSAGE_FROM_SYSTEM一起使用。
FORMAT_MESSAGE_FROM_SYSTEM
0x00001000
该函数应搜索系统消息表资源以获取请求的消息。 如果使用 FORMAT_MESSAGE_FROM_HMODULE指定此标志,则函数将在 lpSource指定的模块中找不到消息时搜索系统消息表。 此标志不能与 FORMAT_MESSAGE_FROM_STRING一起使用。

如果指定了此标志,则应用程序可以传递 GetLastError 函数的结果,以检索系统定义错误的消息文本。

FORMAT_MESSAGE_IGNORE_INSERTS
0x00000200
在消息定义(如 %1)中插入序列将被忽略,并传递到未更改的输出缓冲区。 此标志可用于提取消息以供以后的格式设置。 如果设置了此标志,则忽略 参数 参数。
 

dwFlags 的低序字节可以指定格式化输出行的最大宽度。 以下是低序字节的可能值。

价值 意义
0
没有输出行宽度限制。 该函数将消息定义文本中的换行符存储在输出缓冲区中。
FORMAT_MESSAGE_MAX_WIDTH_MASK
0x000000FF
该函数忽略消息定义文本中的常规换行符。 该函数将消息定义文本中的硬编码换行符存储在输出缓冲区中。 该函数不生成新的换行符。
 

如果低序字节不是 FORMAT_MESSAGE_MAX_WIDTH_MASK非零值,则它指定输出行中的最大字符数。 该函数忽略消息定义文本中的常规换行符。 该函数永远不会在换行符之间拆分由空格分隔的字符串。 该函数将消息定义文本中的硬编码换行符存储在输出缓冲区中。 硬编码换行符使用 %n 转义序列进行编码。

[in, optional] lpSource

消息定义的位置。 此参数的类型取决于 dwFlags 参数中的设置。

dwFlags 设置 意义
FORMAT_MESSAGE_FROM_HMODULE
0x00000800
包含要搜索的消息表的模块句柄。
FORMAT_MESSAGE_FROM_STRING
0x00000400
指向由无格式消息文本组成的字符串的指针。 将相应地扫描插入和格式化。
 

如果 dwFlags中未设置这两个标志,则忽略 lpSource

[in] dwMessageId

请求的消息的消息标识符。 如果 dwFlags 包含 FORMAT_MESSAGE_FROM_STRING,则忽略此参数。

[in] dwLanguageId

请求的消息 语言标识符。 如果 dwFlags 包含 FORMAT_MESSAGE_FROM_STRING,则忽略此参数。

如果在此参数中传递特定的 LANGIDFormatMessage 将仅返回该 LANGID 的消息。 如果函数找不到该 LANGID的消息,则会将 Last-Error 设置为 ERROR_RESOURCE_LANG_NOT_FOUND。 如果传入零,FormatMessage 按以下顺序查找 LANGID 的消息:

  1. 中性语言
  2. 线程 LANGID,基于线程的区域设置值
  3. 用户默认 LANGID,具体取决于用户的默认区域设置值
  4. 系统默认 LANGID,基于系统默认区域设置值
  5. 美国英语
如果 FormatMessage 找不到上述任何 LANGID的消息,则返回存在的任何语言消息字符串。 如果失败,它将返回 ERROR_RESOURCE_LANG_NOT_FOUND

[out] lpBuffer

指向接收以 null 结尾的字符串的缓冲区的指针,该字符串指定格式化的消息。 如果 dwFlags 包括 FORMAT_MESSAGE_ALLOCATE_BUFFER,该函数将使用 localAlloc 函数分配缓冲区,并将指针置于 lpBuffer中指定的地址处。

此缓冲区不能大于 64K 字节。

[in] nSize

如果未设置 FORMAT_MESSAGE_ALLOCATE_BUFFER 标志,则此参数指定输出缓冲区的大小,TCHAR。 如果设置了 FORMAT_MESSAGE_ALLOCATE_BUFFER,此参数指定要为输出缓冲区分配的最小 TCHAR 数

输出缓冲区不能大于 64K 字节。

[in, optional] Arguments

在格式化消息中用作插入值的值数组。 格式字符串中的 %1 指示 参数 数组中的第一个值;%2 指示第二个参数;等等。

每个值的解释取决于与消息定义中插入相关联的格式设置信息。 默认值是将每个值视为指向以 null 结尾的字符串的指针。

默认情况下,参数 参数的类型为 va_list*,它是用于描述可变数量的参数的语言和实现特定数据类型。 从函数返回时未定义 va_list 参数的状态。 若要再次使用 va_list,请使用 va_end 销毁变量参数列表指针,并使用 va_start重新初始化它。

如果没有类型为 va_list*的指针,则指定 FORMAT_MESSAGE_ARGUMENT_ARRAY 标志并将指针传递给 DWORD_PTR 值的数组;这些值是格式化为插入值的消息的输入。 每个插入都必须在数组中具有相应的元素。

返回值

如果函数成功,则返回值是输出缓冲区中存储的 TCHAR 数,不包括终止 null 字符。

如果函数失败,则返回值为零。 若要获取扩展的错误信息,请调用 GetLastError

言论

在消息文本中,支持多个转义序列来动态设置消息的格式。 下表显示了这些转义序列及其含义。 所有转义序列以百分比字符(%)开头。

转义序列 意义
%0 终止消息文本行而不带尾随新行字符。 此转义序列可用于生成长行或终止消息本身,而无需尾随新行字符。 这对于提示消息非常有用。
% n格式字符串 标识插入序列。 n 的值可以介于 1 到 99 的范围内。 格式字符串(必须用感叹号括起来)是可选的,默认为 !s! 如果未指定, 有关详细信息,请参阅 格式规范字段

格式字符串可以包括字符串的宽度和精度说明符,以及整数的宽度说明符。 使用星号()指定宽度和精度。例如,%1!.*s! 或 %1!*u!。

如果不使用宽度和精度说明符,则插入数字直接对应于输入参数。 例如,如果源字符串为“%1 %2 %1”,并且输入参数为“Bill”和“Bob”,则格式化的输出字符串为“Bill Bob Bill”。

但是,如果使用宽度和精度说明符,则插入数字不直接对应于输入参数。 例如,上一示例中的插入数字可能更改为“%1!*.*s! %4 %5!*s!“。

插入数字取决于是使用参数数组(FORMAT_MESSAGE_ARGUMENT_ARRAY)还是 va_list。 对于参数数组,如果上一个格式字符串包含一个星号,并且如果指定了两个星 号,则下一个插入号为 n+2 n+2。 对于 va_list,如果上一个格式字符串包含一个星号,则下一个插入号 为 n+1,如果指定了两个星号,则 n+2

如果要重复“Bill”,如上一示例中所示,参数必须包含“Bill”两次。 例如,如果源字符串为“%1!*.*s! %4 %5!*s!“,参数可以是 4、2、Bill、Bob、6、Bill(如果使用 FORMAT_MESSAGE_ARGUMENT_ARRAY 标志)。 然后,格式化的字符串将为“Bi Bob Bill”。

当源字符串包含宽度和精度说明符时重复插入数字可能不会产生预期的结果。 如果将 %5 替换为 %1,该函数将尝试在地址 6 处打印字符串(可能导致访问冲突)。

不支持浮点格式说明符(e、E、f 和 g)。 解决方法是使用 StringCchPrintf 函数将浮点数格式化为临时缓冲区,然后将该缓冲区用作插入字符串。

使用 I64 前缀的插入被视为两个 32 位参数。 在使用后续参数之前,必须使用它们。 请注意,使用 StringCchPrintf 而不是此前缀可能更容易。

 

输出消息中设置了除百分比字符之后的任何其他非digit 字符的格式,没有百分比字符。 下面是一些示例。

格式字符串 生成的输出
%% 单一百分比符号。
% 空间 单个空间。 此格式字符串可用于确保消息文本行中适当的尾随空格数。
%。 单个句点。 此格式字符串可用于在行开头包含单个句点,而无需终止消息文本定义。
%! 单个感叹号。 此格式字符串可用于在插入后立即包含感叹号,而不会误认为格式字符串的开头。
%n 当格式字符串出现在行尾时,硬换行符。 FormatMessage 提供常规换行符以便消息适合特定宽度时,此格式字符串非常有用。
%r 不带尾随换行符的硬回车符。
%t 单个选项卡。
 

安全备注

如果未 FORMAT_MESSAGE_IGNORE_INSERTS调用此函数,则 Arguments 参数必须包含足够的参数才能满足消息字符串中的所有插入序列,并且它们必须是正确的类型。 因此,不要对启用插入使用不受信任的或未知消息字符串,因为它们可以包含比 参数 提供的插入序列更多的插入序列,或者那些可能属于错误类型的字符串。 具体而言,采用从 API 返回的任意系统错误代码并使用不带 FORMAT_MESSAGE_IGNORE_INSERTSFORMAT_MESSAGE_FROM_SYSTEM 是不安全的。

例子

FormatMessage 函数可用于获取由 GetLastError返回的系统错误代码的错误消息字符串。 有关示例,请参阅 检索 Last-Error 代码

下面的示例演示如何使用参数数组和宽度和精度说明符。
#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h>
#include <stdio.h>

void main(void)
{
    LPWSTR pMessage = L"%1!*.*s! %4 %5!*s!";
    DWORD_PTR pArgs[] = { (DWORD_PTR)4, (DWORD_PTR)2, (DWORD_PTR)L"Bill",  // %1!*.*s! refers back to the first insertion string in pMessage
         (DWORD_PTR)L"Bob",                                                // %4 refers back to the second insertion string in pMessage
         (DWORD_PTR)6, (DWORD_PTR)L"Bill" };                               // %5!*s! refers back to the third insertion string in pMessage
    const DWORD size = 100+1;
    WCHAR buffer[size];


    if (!FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                       pMessage, 
                       0,
                       0,
                       buffer, 
                       size, 
                       (va_list*)pArgs))
    {
        wprintf(L"Format message failed with 0x%x\n", GetLastError());
        return;
    }

    // Buffer contains "  Bi Bob   Bill".
    wprintf(L"Formatted message: %s\n", buffer);
}


以下示例演示如何使用 va_list实现上一个示例。

#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h>
#include <stdio.h>

LPWSTR GetFormattedMessage(LPWSTR pMessage, ...);

void main(void)
{
    LPWSTR pBuffer = NULL;
    LPWSTR pMessage = L"%1!*.*s! %3 %4!*s!";

    // The variable length arguments correspond directly to the format
    // strings in pMessage.
    pBuffer = GetFormattedMessage(pMessage, 4, 2, L"Bill", L"Bob", 6, L"Bill");
    if (pBuffer)
    {
        // Buffer contains "  Bi Bob   Bill".
        wprintf(L"Formatted message: %s\n", pBuffer);
        LocalFree(pBuffer);
    }
    else
    {
        wprintf(L"Format message failed with 0x%x\n", GetLastError());
    }
}

// Formats a message string using the specified message and variable
// list of arguments.
LPWSTR GetFormattedMessage(LPWSTR pMessage, ...)
{
    LPWSTR pBuffer = NULL;

    va_list args = NULL;
    va_start(args, pMessage);

    FormatMessage(FORMAT_MESSAGE_FROM_STRING |
                  FORMAT_MESSAGE_ALLOCATE_BUFFER,
                  pMessage, 
                  0,
                  0,
                  (LPWSTR)&pBuffer, 
                  0, 
                  &args);

    va_end(args);

    return pBuffer;
}

注意

winbase.h 标头将 FormatMessage 定义为别名,该别名根据 UNICODE 预处理器常量的定义自动选择此函数的 ANSI 或 Unicode 版本。 将中性编码别名与不中性编码的代码混合使用可能会导致编译或运行时错误不匹配。 有关详细信息,请参阅函数原型的 约定。

要求

要求 价值
最低支持的客户端 Windows XP [桌面应用 |UWP 应用]
支持的最低服务器 Windows Server 2003 [桌面应用 |UWP 应用]
目标平台 窗户
标头 winbase.h (包括 Windows.h)
Kernel32.lib
DLL Kernel32.dll

另请参阅

错误处理函数

消息编译器

消息表