CString::Format with %s specifier = garbage text   3 comments

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

About these ads

3 responses to “CString::Format with %s specifier = garbage text

Subscribe to comments with RSS.

  1. Does anybody know why in this codes we *may* get garbage text :

    CString First( “John” );
    CString Last( “Doe” );
    CString Name;
    Name.Format( “%s %s”, First, Last ); // Name may be garbage text

    Given a specific compiler and a specific implementation of MFC we should either get garbage *all* the time or a correct result *all* the time.

    Am I missing something here ?

  2. Hi Michael:

    When I mentioned “may sometimes generate garbage text” I did not mean the issue is intermittent. I meant there are multiple scenarios that could cause garbage text. So depending on the scenario, use of CString::Format may or may not cause garbage text.

    The same code should either always generate garbage text or always format properly, depending on unknown factors. Perhaps Unicode or ANSI character-set setting, Visual Studio version, desktop or CE project, or compiler optimization setting, etc.

    Thanks.

  3. Hi Ed,

    thanks a lot for the clarifications..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: