这是正确的缓冲大小吗?

时间:2014-06-14 03:45:12

标签: c++ winapi

这是在64位机器上加上unicode字符串的整数值的正确大小吗?或者我错过了什么?

    DWORD errorMessageID = GetLastError();
        const wchar_t msgFmt[] = L"foo baaa. Error code = %d";
        wchar_t bufferMsg[sizeof(msgFmt) +        // room for fmt message string itself
            21 +  // enough to hold numbers up to 64-bits
            sizeof(wchar_t)         // byte-terminattor
        ];
        int nBytesWritten = swprintf_s(bufferMsg,
            msgFmt,
            sizeof(msgFmt),
            errorMessageID);

        MessageBox(NULL, 
                    bufferMsg,
                    TEXT("Copy to clipboard failed"), 
                    MB_OK | MB_ICONERROR);

3 个答案:

答案 0 :(得分:2)

除了传递给Kirill Kobelev's answer中提到的swsprintf_s()的参数的问题之外,还有至少一些不太正确的事情,即使并非所有事情都会导致缺陷:

  • Windows SDK中的DWORD为32位,即使在构建64位目标时也是如此。保留21个字符进行格式化是一个小小的过度杀伤,但不是一个bug。但是,这表明存在可能导致其他问题的误解。
  • DWORD是无符号类型,因此使用“%d”格式化它是不对的
  • 如果要格式化64位signed int,“%d”仍然是错误的格式规范,因为它表明该参数是32位int类型。您可能希望使用类似“%lld”或“%I64d”的内容来格式化64位int类型。

答案 1 :(得分:1)

不,这不正确。正确的代码应如下所示:

const wchar_t msgFmt[] = L"foo baaa. Error code = %d";
wchar_t bufferMsg[sizeof(msgFmt)/sizeof(wchar_t) + // room for fmt message string itself
    21 +      // enough to hold numbers up to 64-bits
    1         // symbol-terminator
];
int nBytesWritten = swprintf_s(bufferMsg,
    sizeof(bufferMsg)/sizeof(wchar_t),
    msgFmt,
    errorMessageID);

在这两种情况下,您都使用了字节大小而不是元素数量加上params的顺序是错误的。您调用MessageBox()函数是正确的。

答案 2 :(得分:0)

手动计算sprintf呼叫目标所需的缓冲区大小很难维护。让系统为您执行计算更容易,更安全。 Visual Studio附带的CRT为此提供了_scprintf系列函数。

以下代码说明了它的用法。它实现了一个函数,它接受一个格式字符串和可变数量的参数,并返回一个格式化结果的字符串:

std::wstring FormatString( const wchar_t* a_Format, ... ) {
    std::wstring text;
    va_list argList;
    va_start( argList, a_Format );
    // Calculate required buffer size
    size_t size = _vscwprintf( a_Format, argList ) + 1;
    va_end( argList );
    if ( size > 0 ) {
        // Dynamically construct buffer with the correct size
        std::vector<wchar_t> buffer( size );
        va_start( argList, a_Format );
        int count = _vsnwprintf_s( buffer.data(), size, size - 1,
                                   a_Format, argList );
        va_end( argList );
        if ( count >= 0 ) {
            // Construct return value
            text = std::wstring( buffer.data(), count + 1 );
        }
    }
    return text;
}

此函数返回std::wstring个对象。就像实现中使用的std::vector一样,它会自动为您管理内存,并且不需要明确的清理代码。要将其与需要LPCWSTR参数的Windows API调用一起使用,请调用其c_str()成员。

在原始代码中使用此功能会将其浓缩为以下内容:

DWORD errorMessageID = GetLastError();
MessageBoxW(NULL,
            FormatString( L"foo baaa. Error code = %u", errorMessageID ).c_str(),
            L"Copy to clipboard failed",
            MB_OK | MB_ICONERROR);