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

如果在此参数中传递特定的 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 指示 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!“,参数可以是,4、2、Bill、Bob、6、Bill ((如果使用 FORMAT_MESSAGE_ARGUMENT_ARRAY 标志) )。 然后,格式化的字符串将为“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_IGNORE_INSERTS使用FORMAT_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;
}

要求

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

另请参阅

错误处理函数

消息编译器

消息表