Является ли этот правильный размер для хранения целочисленного значения на 64-разрядной машине плюс строка в Юникоде? или я что-то упускаю?
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);
Нет, это неправильно. Правильный код должен выглядеть так:
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);
В обоих случаях вы использовали размер байта вместо количества элементов, а также порядок параметров. Вы правильно вызываете функцию MessageBox()
.
_countof
может быть понятнее, чем sizeof()/sizeof()
. О, и вы выделили 2 символа вместо 1 для терминатора. :-)
В дополнение к проблемам с аргументами, переданными swsprintf_s()
упомянутым в ответе Кирилла Кобелева, есть, по крайней мере, еще несколько вещей, которые не совсем правильны, даже если не все из них приведут к дефекту:
DWORD
в SDK Windows 32-бит, даже при создании для 64-битной цели. Резервирование 21 символа для форматирования - это немного перебор, но не ошибка. Однако это указывает на недоразумение, которое может привести к другим проблемам.DWORD
- это неподписанный тип, поэтому использование "% d" для форматирования не совсем правильно Вручную рассчитывать необходимый размер буфера для назначения вызова sprintf
трудно. Легче и безопаснее, если система выполнит расчет для вас. CRT, поставляемый с Visual Studio, предоставляет для _scprintf
семейство функций _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
используемому в реализации, он автоматически управляет памятью для вас, и явный код очистки не требуется. Чтобы использовать его с вызовами Windows API, для которых требуется аргумент LPCWSTR
вызывается его 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);
auto bufferMsg = L"foo baaa. Error code = %d"s + std::to_wstring(errorMessageID);
Конечно, вы можете позаботиться о любых новых функциях, которые вы не можете использовать, но та же идея.FormatMessage()