FIX: CString::Format Gives Assertion Failed, Access Violation (127038)
The information in this article applies to:
- The Microsoft Foundation Classes (MFC), when used with:
- Microsoft Visual C++, 32-bit Editions 2.0
- Microsoft Visual C++, 32-bit Editions 2.1
This article was previously published under Q127038 SYMPTOMS
Using CString::Format causes one of the following: CAUSE
There are three known bugs in the CString::Format() member function in MFC
version 3.0 that can cause this behavior: - Bug One: Using a format width specifier that is greater than the length
of the data passed to CString::Format().
- Bug Two: Using an asterisk width specifier.
- Bug Three: Using an asterisk precision specifier.
RESOLUTION
Create an updated function by using the code listed below. To use the code,
define a new function named Format. The new format function will take a
CString as the first parameter. Then use the new Format function instead of
CString::Format.
For example, instead of using this:
CString str;
str.Format(...)
use this:
CString str;
Format(str,...)
Updated CString::Format Code with Necessary Constant Definitions
#define FORCE_ANSI 0x10000
#define FORCE_UNICODE 0x20000
// formatting (using wsprintf style formatting)
void Format(CString& str, LPCTSTR lpszFormat, ...)
{
ASSERT(AfxIsValidString(lpszFormat, FALSE));
va_list argList;
va_start(argList, lpszFormat);
// make a guess at the maximum length of the resulting string
int nMaxLen = 0;
for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0';
lpsz = _tcsinc(lpsz))
{
// handle '%' character, but watch out for '%%'
if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%')
{
nMaxLen += _tclen(lpsz);
continue;
}
int nItemLen = 0;
// handle '%' character with format
int nWidth = 0;
for (; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
{
// check for valid flags
if (*lpsz == '#')
nMaxLen += 2; // for '0x'
else if (*lpsz == '*')
nWidth = va_arg(argList, int);
else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0'
|| *lpsz == ' ')
;
else // hit non-flag character
break;
}
// get width and skip it
if (nWidth == 0)
{
// width indicated by
nWidth = _ttoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz);
lpsz = _tcsinc(lpsz))
;
}
ASSERT(nWidth >= 0);
int nPrecision = 0;
if (*lpsz == '.')
{
// skip past '.' separator (width.precision)
lpsz = _tcsinc(lpsz);
// get precision and skip it
if (*lpsz == '*')
{
nPrecision = va_arg(argList, int);
lpsz = _tcsinc(lpsz);
}
else
{
nPrecision = _ttoi(lpsz);
for (; *lpsz != '\0' && _istdigit(*lpsz);
lpsz = _tcsinc(lpsz))
;
}
ASSERT(nPrecision >= 0);
}
// should be on type modifier or specifier
int nModifier = 0;
switch (*lpsz)
{
// modifiers that affect size
case 'h':
nModifier = FORCE_ANSI;
lpsz = _tcsinc(lpsz);
break;
case 'l':
nModifier = FORCE_UNICODE;
lpsz = _tcsinc(lpsz);
break;
// modifiers that do not affect size
case 'F':
case 'N':
case 'L':
lpsz = _tcsinc(lpsz);
break;
}
// now should be on specifier
switch (*lpsz | nModifier)
{
// single characters
case 'c':
case 'C':
nItemLen = 2;
va_arg(argList, TCHAR);
break;
case 'c'|FORCE_ANSI:
case 'C'|FORCE_ANSI:
nItemLen = 2;
va_arg(argList, char);
break;
case 'c'|FORCE_UNICODE:
case 'C'|FORCE_UNICODE:
nItemLen = 2;
va_arg(argList, WCHAR);
break;
// strings
case 's':
case 'S':
nItemLen = lstrlen(va_arg(argList, LPCTSTR));
nItemLen = max(1, nItemLen);
break;
case 's'|FORCE_ANSI:
case 'S'|FORCE_ANSI:
nItemLen = lstrlenA(va_arg(argList, LPCSTR));
nItemLen = max(1, nItemLen);
break;
#ifndef _MAC
case 's'|FORCE_UNICODE:
case 'S'|FORCE_UNICODE:
nItemLen = wcslen(va_arg(argList, LPWSTR));
nItemLen = max(1, nItemLen);
break;
#endif
}
// adjust nItemLen for strings
if (nItemLen != 0)
{
nItemLen = max(nItemLen, nWidth);
if (nPrecision != 0)
nItemLen = min(nItemLen, nPrecision);
}
else
{
switch (*lpsz)
{
// integers
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
va_arg(argList, int);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
case 'e':
case 'f':
case 'g':
case 'G':
va_arg(argList, _AFX_DOUBLE);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
case 'p':
va_arg(argList, void*);
nItemLen = 32;
nItemLen = max(nItemLen, nWidth+nPrecision);
break;
// no output
case 'n':
va_arg(argList, int*);
break;
default:
ASSERT(FALSE); // unknown formatting option
}
}
// adjust nMaxLen for output nItemLen
nMaxLen += nItemLen;
}
va_end(argList);
// finally, set the buffer length and format the string
va_start(argList, lpszFormat); // restart the arg list
str.GetBuffer(nMaxLen);
VERIFY(_vstprintf(str.GetBuffer(1), lpszFormat, argList) <= nMaxLen);
str.ReleaseBuffer();
va_end(argList);
}
STATUS
Microsoft has confirmed these to be bugs in the Microsoft products listed
at the beginning of this article. The first two bugs were fixed in MFC
version 3.1 that shipped with Microsoft Visual C++, 32-bit Edition, version
2.1, and the third was fixed in Visual C++, 32-bit Edition, version 2.2.
Modification Type: | Major | Last Reviewed: | 10/24/2003 |
---|
Keywords: | kbBug kbcode kbfix kbNoUpdate kbString kbVC220fix KB127038 |
---|
|