FormatMessage 函数 (winbase.h)

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

语法

DWORD FormatMessage(
  [in]           DWORD   dwFlags,
  [in, optional] LPCVOID lpSource,
  [in]           DWORD   dwMessageId,
  [in]           DWORD   dwLanguageId,
  [out]          LPTSTR  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
Arguments 参数不是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),并将其传递到未更改的输出缓冲区。 此标志可用于提取消息以便以后进行格式设置。 如果设置了此标志,则忽略 Arguments 参数。
 

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,则忽略此参数。

如果在此参数中传递特定的 LANGID,FormatMessage 将仅返回该 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 指示 Arguments 数组中的第一个值;%2 指示第二个参数;等等。

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

默认情况下, Arguments 参数的类型为 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+3 。 对于 va_list,如果上一个格式字符串包含一个星号,则下一个插入号为 n+1 ;如果指定了两个星号,则为 n+2

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

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

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

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

 

百分比字符后的任何其他非数字字符在输出消息中设置格式,不带百分比字符。 下面是一些示例。

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

安全备注

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

示例

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;
}

要求

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

另请参阅

错误处理函数

消息编译器

消息表