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

DWORD FormatMessageW(
  [in]           DWORD   dwFlags,
  [in, optional] LPCVOID lpSource,
  [in]           DWORD   dwMessageId,
  [in]           DWORD   dwLanguageId,
  [out]          LPWSTR  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

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

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

函数应在系统消息表资源 () 搜索请求的消息。 如果使用 FORMAT_MESSAGE_FROM_HMODULE 指定此标志,则如果在 lpSource 指定的模块中找不到该消息,则函数将搜索系统消息表。 此标志不能与 FORMAT_MESSAGE_FROM_STRING一起使用。

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

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

[in, optional] lpSource

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

dwFlags 设置
FORMAT_MESSAGE_FROM_HMODULE
0x00000800
包含要搜索的消息表的模块的句柄。

如果未在 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 的消息:

  • 线程 LANGID,基于线程的区域设置值
  • 基于用户的默认区域设置值的用户默认 LANGID
  • 系统默认 LANGID,基于系统默认区域设置值
  • 如果 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。

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

    终止不带尾随新行字符的消息文本行。 此转义序列可用于生成长行或终止消息本身,而无需尾随新行字符。 它对于提示消息很有用。 %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 而不是此前缀可能更容易。

    如果在不 FORMAT_MESSAGE_IGNORE_INSERTS的情况下调用此函数, 则 Arguments 参数必须包含足够的参数来满足消息字符串中的所有插入序列,并且它们必须是正确的类型。 因此,请勿在启用插入的情况下使用不受信任的或未知的消息字符串,因为它们可能包含比 Argument 提供的 更多的插入序列,或者可能包含错误类型的插入序列。 具体而言,采用从 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, 
                           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);
            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, 
                      (LPWSTR)&pBuffer, 
                      &args);
        va_end(args);
        return pBuffer;
    

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