windows_programming_notes.nbk: Home | Index | Next Page: FreeEnvironmentStrings | Previous Page: FindResourceEx


 FormatMessage

Formats a message string. The function requires a message definition as input. The message definition can come from a buffer passed into the function. It can come from a message table resource in an already-loaded module. Or the caller can ask the function to search the system's message table resource(s) for the message definition. The function finds the message definition in a message table resource based on a message identifier and a language identifier. The function copies the formatted message text to an output buffer, processing any embedded insert sequences if requested.

    DWORD WINAPI FormatMessage(
      __in      DWORD dwFlags,
      __in_opt  LPCVOID lpSource,
      __in      DWORD dwMessageId,
      __in      DWORD dwLanguageId,
      __out     LPTSTR lpBuffer,
      __in      DWORD nSize,
      __in_opt  va_list* Arguments
    );

Parameters

Return Value

If the function succeeds, the return value is the number of TCHARs stored in the output buffer, excluding the terminating null character.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

Within the message text, several escape sequences are supported for dynamically formatting the message. These escape sequences and their meanings are shown in the following tables. All escape sequences start with the percent character (%).

Escape sequence Meaning %0 Terminates a message text line without a trailing new line character. This escape sequence can be used to build up long lines or to terminate the message itself without a trailing new line character. It is useful for prompt messages. %n!format string! Identifies an insert. The value of n can be in the range from 1 through 99. The format string (which must be surrounded by exclamation marks) is optional and defaults to !s! if not specified. For more information, see Format Specification Fields. The format string can include a width and precision specifier for strings and a width specifier for integers. Use an asterisk (*) to specify the width and precision. For example, %1!*.*s! or %1!*u!.

If you do not use the width and precision specifiers, the insert numbers correspond directly to the input arguments. For example, if the source string is "%1 %2 %1" and the input arguments are "Bill" and "Bob", the formatted output string is "Bill Bob Bill".

However, if you use a width and precision specifier, the insert numbers do not correspond directly to the input arguments. For example, the insert numbers for the previous example could change to "%1!*.*s! %4 %5!*s!".

The insert numbers depend on whether you use an arguments array (FORMAT_MESSAGE_ARGUMENT_ARRAY) or a va_list. For an arguments array, the next insert number is n+2 if the previous format string contained one asterisk and is n+3 if two asterisks were specified. For a va_list, the next insert number is n+1 if the previous format string contained one asterisk and is n+2 if two asterisks were specified.

If you want to repeat "Bill", as in the previous example, the arguments must include "Bill" twice. For example, if the source string is "%1!*.*s! %4 %5!*s!", the arguments could be, 4, 2, Bill, Bob, 6, Bill (if using the FORMAT_MESSAGE_ARGUMENT_ARRAY flag). The formatted string would then be " Bi Bob Bill".

Repeating insert numbers when the source string contains width and precision specifiers may not yield the intended results. If you replaced %5 with %1, the function would try to print a string at address 6 (likely resulting in an access violation).

Floating-point format specifiers—e, E, f, and g—are not supported. The workaround is to use the StringCchPrintf function to format the floating-point number into a temporary buffer, then use that buffer as the insert string.

Inserts that use the I64 prefix are treated as two 32-bit arguments. They must be used before subsequent arguments are used. Note that it may be easier for you to use StringCchPrintf instead of this prefix.

Any other nondigit character following a percent character is formatted in the output message without the percent character. Following are some examples.

Format string Resulting output %% A single percent sign. %space A single space. This format string can be used to ensure the appropriate number of trailing spaces in a message text line. %. A single period. This format string can be used to include a single period at the beginning of a line without terminating the message text definition. %! A single exclamation point. This format string can be used to include an exclamation point immediately after an insert without its being mistaken for the beginning of a format string. %n A hard line break when the format string occurs at the end of a line. This format string is useful when FormatMessage is supplying regular line breaks so the message fits in a certain width. %r A hard carriage return without a trailing newline character. %t A single tab.

Security Remarks

If this function is called without FORMAT_MESSAGE_IGNORE_INSERTS, the Arguments parameter must contain enough parameters to satisfy all insertion sequences in the message string, and they must be of the correct type. Therefore, do not use untrusted or unknown message strings with inserts enabled because they can contain more insertion sequences than Arguments provides, or those they may be of the wrong type. In particular, it is unsafe to take an arbitrary system error code returned from an API and use FORMAT_MESSAGE_FROM_SYSTEM without FORMAT_MESSAGE_IGNORE_INSERTS.

Example Code [C++]

The FormatMessage function can be used to obtain error message strings for the system error codes returned by GetLastError. For an example, see Retrieving the Last-Error Code. The following examples also demonstrate uses of the FormatMessage function.

The following example shows how to use an argument array and the width and precision specifiers.

#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!
         (DWORD_PTR)L"Bob",                                                // %4
         (DWORD_PTR)6, (DWORD_PTR)L"Bill" };                               // %5!*s!
    const DWORD size = 100+1;
    WCHAR buffer[size];


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

    wprintf(L"Formatted message: %s\n", buffer);
}

Example Code

The following example shows how to implement the previous example using 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!";

    pBuffer = GetFormattedMessage(pMessage, 4, 2, L"Bill", L"Bob", 6, L"Bill");
    if (pBuffer)
    {
        wprintf(L"Formatted message: %s\n", pBuffer);
        LocalFree(pBuffer);
    }
    else
    {
        wprintf(L"Format message failed with 0x%x\n", GetLastError());
    }
}

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,  // ignored
                  0,  // ignored
                  (LPWSTR)&pBuffer, 
                  0, 
                  &args);

    va_end(args);

    return pBuffer;
}

Requirements

Unicode Implemented as FormatMessageW (Unicode) and FormatMessageA (ANSI).


windows_programming_notes.nbk: Home | Index | Next Page: FreeEnvironmentStrings | Previous Page: FindResourceEx


Notebook exported on Monday, 7 July 2008, 18:56:50 PM Eastern Daylight Time