previous page
next page

Strings, Character Sets, and Conversions

Several important changes have been made in the way that ATL handles strings. Everything you know from ATL 3 still works, but you should consider upgrading your code to use the new features.

Character-Conversions Macros

ATL 3 provided a set of macros that make it easier to convert among various character sets: ANSI, Multibyte, Wide, and the mutable TCHAR type. To use the macros, you first had to include the USES_CONVERSION macro in the code above the first use:

void PrintMessage( LPCTSTR lpszMessage ) {
  USES_CONVERSION;
  std::cout << "Your message is " << T2A(lpszMessage) << endl;
}

The conversion macros are convenient, but the implementation had a couple of problems. The storage for the conversion buffer was allocated on the program stack (via the _alloca function), which made cleanup automatic. Unfortunately, if you called the conversion macros in a loop, none of the memory was cleaned up until the function containing that loop returned. As a result, you could overrun your stack if you weren't careful. The use of _alloca also changed the stack layout enough that if you used a conversion macro inside a C++ catch { } block, your program would probably crash. Finally, if you forgot the USES_CONVERSION macro, you got some strange error messages.

To address these problems, ATL 7 introduced a new set of conversion classes. Each one has the same name as the old conversion macro, with a C on the front. The previous example becomes this with the class:

void PrintMessage( LPCTSTR lpszMessage ) {
  std::cout << "Your message is " << CT2A(lpszMessage) << endl;
}

The conversion classes do not require the USES_CONVERSION macro and are actually C++ classes instead of macro trickery. Each object has a small internal buffer that is used to store the converted string if the string is short; for longer conversions, a heap allocation is done. The conversion class's destructor makes sure to clean up any allocation.

Using the conversion classes instead of the macros avoids doing weird things to the stack, and as a result, you get more stable code and fewer weird corner cases (such as the crash in catch blocks). See Chapter 2, "Strings and Text," for more information on the new conversion classes.

Security Enhanced C Standard Library

This one isn't really an ATL change, but it's a new feature of Visual C++ 8. It's also something that all C and C++ developers should pay attention to, especially web developers, because this feature is now well supported in ATL via ATL Server. The Internet is a hostile place and is only getting more so as time goes on. One of the most common security holes in unmanaged code is the buffer overrun; data is copied into a fixed size array that's too small, and the data runs over the end of the array and puts carefully crafted bytes on the stack that do whatever the attacker wants them to do.

If you use the standard C library, particularly the string functions (the old standbys such as strcpy and strcat), it turns out to be remarkably difficult to avoid buffer overflows. These factors contribute to buffer overflows:

  • No buffer sizes. strcpy provides no way to indicate how large the buffer is that you're copying into.

  • Inconsistent string termination. strncpy does let you specify how long the destination buffer is, but if your buffer is too long, it doesn't actually put the \0 byte at the end to terminate the string. In general, the C string functions are either underspecified about string termination or just unpredictable.

  • Inadequate parameter checking. Most of these functions handle NULL pointers unpredictably.

  • Interface inconsistency. Some functions put the destination in the first parameter; others put it in the second. It's easy to mix up source and destination buffers because they're both variables of type char *.

As part of the recent security push at Microsoft, a new set of secure string functions was written and included in the C runtime library.[1] They're named with an "_s" suffix, so they are functions such as strcat_s, and strcpy_s.[2]

[1] These functions have also been submitted to the C and C++ standardization committees, so expect to see them in other compilers as the language standards get revised.

[2] For details about the motivations and design of these functions, take a look at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncode/html/secure03102004.asp.

Why does this concern ATL developers? There are two reasons. The first, less interesting one is that the majority of changes from ATL 7 to ATL 8 were replacements of string function calls with these new secure string functions. The second, and more important, one is that when you compile your old code with ATL 8, you now get compiler warnings for calls to the old insecure functions saying that they're deprecated and what function to replace them with. For example, this code

void BuildUrl( char *dest, size_t dest_len, char *page ) {
  strncpy( dest, "http://localhost/Sample", dest_len );
  strncat( dest, page, dest_len );
  strncat( dest, "/default.srf", dest_len );
}

now results in these compile-time warnings:

c:\atlinternals2e\src\appendixc\unsafestringhandling\unsafestringhandling.cpp
  (72) : warning C4996: 'strncpy' was declared deprecated
c:\program files\microsoft visual studio 8\vc\include\string.h(156) :
  see declaration of 'strncpy'
Message: 'This function or variable may be unsafe. Consider using
  strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE.
  See online help for details.'
c:\atlinternals2e\src\appendixc\unsafestringhandling\unsafestringhandling.cpp
  (73) : warning C4996: 'strncat' was declared deprecated
c:\program files\microsoft visual studio 8\vc\include\string.h(143) :
  see declaration of 'strncat'
Message: 'This function or variable may be unsafe. Consider using
  strncat_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE.
  See online help for details.'
c:\atlinternals2e\src\appendixc\unsafestringhandling\unsafestringhandling.cpp
  (74) : warning C4996: 'strncat' was declared deprecated
c:\program files\microsoft visual studio 8\vc\include\string.h(143) :
  see declaration of 'strncat'
Message: 'This function or variable may be unsafe. Consider using
  strncat_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE.
  See online help for details.'

You can turn off the deprecation warnings (using the _CRT_SECURE_NO_DEPRECATE preprocessor flag, as shown in the warning message), but it's generally a better idea to fix the code. For example, the previous code using the secure string functions looks like this:

void SafeBuildUrl( char * dest, size_t dest_len, char *page ) {
  // _TRUNCATE is a special value that tells the function to
  // copy as much of source as will fit in dest and still have a
  // \0 terminator.
  strncpy_s( dest, dest_len, "http://localhost/Sample",
    _TRUNCATE );
  strncat_s( dest, dest_len, page, _TRUNCATE );
  strncat_s( dest, dest_len, "/default.srf", _TRUNCATE );
}

The strncat_s function also has the nice feature of automatically figuring out how much room is left in the destination buffer, and it avoids many off-by-one (or off-by-many, in the case of the broken code shown previously) errors.

Chapter 2 discusses use of these functions more.


previous page
next page
Converted from CHM to HTML with chm2web Pro 2.75 (unicode)