DWORD FormatMessageA()

in source/shared/FormattedPrint.cpp [1404:1493]


DWORD FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPTSTR lpBuffer, DWORD nSize, va_list *Arguments)
{
    DWORD chars_printed = 0;

    // XPLAT_ODBC_TODO VSTS 718708 Localization by handling FORMAT_MESSAGE_FROM_HMODULE and dwLanguageId param
    if ( dwFlags & FORMAT_MESSAGE_FROM_STRING )
    {
        // Format specification allows for reordering of insertions relative to var arg position
        // This means we need to walk thru the format specification to find the types of the var args in var arg order
        // We extract the var args in order based on the identified types
        // Finally, we re-walk the format specfication and perform the insertions

        // First pass thru the format string to determine all args and their types
        // This first pass also validates the format string and will return an error
        // if it is invalid.  This allows FormatMessageToBuffer to have less error
        // checking.
        std::vector< vararg_t > args;
        // Based on quick scan of RC files, the largest arg count was 7 so reserve 8 slots to reduce allocations
        args.reserve(8);
        if ( GetFormatMessageArgsA( reinterpret_cast<const char *>(lpSource), &args, Arguments ) )
        {
            if ( dwFlags == (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING) )
            {
                *((char**)lpBuffer) = NULL;

                const DWORD max_size = 64000;
                char local_buf[max_size] = {'\0'};
                chars_printed = FormatMessageToBufferA( reinterpret_cast<const char *>(lpSource), local_buf, max_size, args );
                if ( 0 < chars_printed )
                {
                    size_t buf_size = std::min( max_size, std::max(nSize, (chars_printed+1)) );
                    char * return_buf = (char *)LocalAlloc(0, buf_size * sizeof(char));
                    if ( NULL == return_buf )
                    {
                        errno = ENOMEM;
                    }
                    else
                    {
                        mplat_cscpy(return_buf, local_buf);
                        *((char**)lpBuffer) = return_buf;
                    }
                }
            }
            else if ( dwFlags == FORMAT_MESSAGE_FROM_STRING )
            {
                chars_printed = FormatMessageToBufferA( reinterpret_cast<const char *>(lpSource), lpBuffer, std::min(nSize, (DWORD)64000), args );
            }
        }
    }
    else if ( dwFlags & FORMAT_MESSAGE_FROM_SYSTEM )
    {
        // Since we don't have the Windows system error messages available use a fixed message
        // Can not use a message ID for this since this same code is used by driver and tools,
        // each having their own RLL file.  Don't think we should be reserving an ID across all RLLs.
        const char systemMsg[] = "Error code 0x%X";
        if ( dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER )
        {
            *((char**)lpBuffer) = NULL;

            // Add 9 for up to 8 hex digits plus null term (ignore removal of format specs)
            const size_t msgsize = (9 + sizeof(systemMsg)/sizeof(systemMsg[0]));
            char * return_buf = (char *)LocalAlloc(0, msgsize * sizeof(char));
            if ( NULL == return_buf )
            {
                errno = ENOMEM;
            }
            else
            {
                chars_printed = mplat_snprintf_s( return_buf, msgsize, msgsize, systemMsg, dwMessageId );
                // Assert that we did our buffer size math right
                assert( chars_printed < msgsize );
                if ( 0 < chars_printed )
                {
                    *((char**)lpBuffer) = return_buf;
                }
                else
                {
                    LocalFree( return_buf );
                    errno = EINVAL;
                }
            }
        }
        else
        {
            chars_printed = mplat_snprintf_s( lpBuffer, nSize, nSize, systemMsg, dwMessageId );
        }
    }

    return chars_printed;
}