Known causes for garbage text:

  • Using %s with CString argument, without casting to LPCTSTR, may sometime generate garbage text, depending on multiple scenarios. See the explanations below.
  • Using incorrect %s or %S specifier.

Use (LPCTSTR) cast with CString argument:

  • CString::Format with %s specifier does *NOT* expect the matching argument to be a CString.
  • CString::Format internally uses vsprintf, so the %s specifier expects the argument to be a pointer to a null-terminated string of TCHAR characters, an LPCTSTR to be exact.
  • CString objects in Visual Studio 2003 (MFC 7.1) and later "automagically" dereference themselves to the internal string data.
  • CString objects in Visual Studio and eMbedded VC++ versions prior to 2003 do *NOT* dereference themselves to the internal string data.
  • Sub-classed CString objects containing virtual functions, regardless of which version of Visual Studio is used, do *NOT* dereference themselves to the internal string data, they dereference to their v-table. Thanks Michael Walz.
  • This means that if you sub-class CString and your custom class contains any virtual functions (including a virtual destructor), or you are using Visual Studio or eMbedded VC++ versions prior to 2003, you must use (LPCTSTR) cast with CString arguments.
  • The only 100% reliable way to get a pointer to the internal string data of a CString object is to either cast to LPCTSTR, use GetBuffer( 0 ), or use GetString().
  • The official MSDN documentation on CString::Format has something completely wrong to say about this:
When you pass a character string as an optional argument, you must cast it explicitly as LPCTSTR.
  • This is *NOT* correct. It should read "When you pass a CString object as an optional argument, you must cast it explicitly as LPCTSTR."
  • A CString is not the same thing as a "character string".
  • If you pass a character string (char or wchar) as an optional argument, and it matches the project's character-set setting, then the character string is already an LPCTSTR as far as printf related functions are concerned. Casting a character string to LPCTSTR is completely unnecessary.
  • It is the CString argument which needs to be cast to LPCTSTR.

Correct Usage of LPCTSTR cast for CString argument:

CString First( "John" ); // CString or sub-class  
CString Last( "Doe" ); // CString or sub-class  
CString Name;  
Name.Format("%s %s", (LPCTSTR)First, (LPCTSTR)Last );  

Incorrect Usage: don't do this

CString First( "John" ); // CString or sub-class  
CString Last( "Doe" ); // CString or sub-class  
CString Name;  
Name.Format( "%s %s", First, Last ); // Name may be garbage text  

Use correct %s or %S specifier:

  • You must use the correct %s or %S specifier, based on the argument type (CHAR, WCHAR, or TCHAR) and project character-set type (Unicode or ANSI).
String Type ANSI Projects Unicode Projects
CString or TCHAR* %s (lowercase s) %s (lowercase s)
CStringA or char* %s (lowercase s) %S (uppercase s)
CStringW or WCHAR* %S (uppercase s) %s (lowercase s)
  • What if you plan to change the project's character set setting? My suggestion would be to use %s with an intermediate CString variable cast to LPCTSTR. Read more about this here.

Update 1/16/2013: I've always been aware of a problem with using %s with CString::Format with CString arguments, but I never bothered to track down the cause of the problem, because casting to an LPCTSTR seemed to be an acceptable solution. The answer came as part of a discussion about this article on stackoverflow.com. Michael Walz properly identified that the problem was only with sub-classed objects derived from classes containing virtual functions, because the object no longer "magically" dereferences itself to its internal string data, it dereferences itself to its v-table. I have since rewritten this article to include this updated information.

REF:

CString::Format


%s · arg · C/C++ · cast · corrupt · CString · Format · garbage · junk · LPCTSTR · MFC · MSDN Documentation Errors · printf · specifier · sprintf