Cautions about ATL String conversion macros and _alloca()
Don't use the macros in a tight loop. For example, you do NOT want to write the following kind of code:
void BadIterateCode(LPCTSTR lpsz)
{
USES_CONVERSION;
for (int ii = 0; ii < 10000; ii++)
pI->SomeMethod(ii, T2COLE(lpsz));
}
The code above could result in allocating megabytes of memory on the stack depending on what the contents of the string lpsz is! It also takes time to convert the string for each iteration of the loop. Instead move such constant conversions out of the loop:
void MuchBetterIterateCode(LPCTSTR lpsz)
{
USES_CONVERSION;
LPCOLESTR lpszT = T2COLE(lpsz);
for (int ii = 0; ii < 10000; ii++)
pI->SomeMethod(ii, lpszT);
}
If the string is not constant, then encapsulate the method call into a function. This will allow the conversion buffer to be freed each time. For example:
void CallSomeMethod(int ii, LPCTSTR lpsz)
{
USES_CONVERSION;
pI->SomeMethod(ii, T2COLE(lpsz));
}
void MuchBetterIterateCode2(LPCTSTR* lpszArray)
{
for (int ii = 0; ii < 10000; ii++)
CallSomeMethod(ii, lpszArray[ii]);
}
Never return the result of one of the macros, unless the return value implies making a copy of the data before the return. For example, this code is bad:
LPTSTR BadConvert(ISomeInterface* pI)
{
USES_CONVERSION;
LPOLESTR lpsz = NULL;
pI->GetFileName(&lpsz);
LPTSTR lpszT = OLE2T(lpsz);
CoMemFree(lpsz);
return lpszT; // bad! returning alloca memory
}
The code above could be fixed by changing the return value to something which copies the value:
CString BetterConvert(ISomeInterface* pI)
{
USES_CONVERSION;
LPOLESTR lpsz = NULL;
pI->GetFileName(&lpsz);
LPTSTR lpszT = OLE2T(lpsz);
CoMemFree(lpsz);
return lpszT; // CString makes copy
}
The macros are easy to use and easy to insert into your code, but as you can tell from the caveats above, you need to be careful when using them.
The implementation of each macro uses the _alloca() function to allocate memory from the stack instead of the heap. Allocating memory from the stack is much faster than allocating memory on the heap, and the memory is automatically freed when the function is exited. In addition, the macros avoid calling MultiByteToWideChar (or WideCharToMultiByte) more than one time. This is done by allocating a little bit more memory than is necessary. We know that an MBC will convert into at most one WCHAR and that for each WCHAR we will have a maximum of two MBC bytes. By allocating a little more than necessary, but always enough to handle the conversion the second call second call to the conversion function is avoided. The call to the helper function AfxA2Whelper reduces the number of argument pushes that must be done in order to perform the conversion (this results in smaller code, than if it called MultiByteToWideChar directly).
In order to for the macros to have space to store the a temporary length, it is necessary to declare a local variable called _convert that does this in each function that uses the conversion macros. This is done by invoking the USES_CONVERSION macro as seen above in the example.
USES_CONVERSION declares a 4 simple stack variables. If the
preprocessor macro _CONVERSION_USES_THREAD_LOCALE is defined it also
results in a call to GetACP() to get the code page. Very minor
overhead.
All of the macro conversions use the _alloca() function to employ
stack-allocated memory. This memory lasts until the function exits.
The dangers of the conversion macros are really the dangers of _alloca
function that they use.
1. You must never use them within catch handlers. You *may* use them
within functions call by a catch handler, but not directly within
catch handlers.
2. Beware of using them in a loop. The weird thing about _alloca is
that the memory that it allocates is not bound by normal C++ scoping
rules.
No matter how nested your scope when you call it, the memory that it
returns remains allocated until the current *function* exits. Not
merely the current scope.
This means that if you do something like this:
void Foo()
{
for ( int x = 1; x < 10000; ++x)
{
void* pBuf = _alloca(10000);
} // pBuf is freed here but not the memory
}
...you can kiss your stack goodbye.
It's easier to do stuff like this than you may think. If you replace
that call to alloca with a call to a conversion macro, you can get the
same effect.
_alloca allocates size bytes from the program stack. The allocated space is automatically freed when the calling function exits. Therefore, do not pass the pointer value returned by _alloca as an argument to free.
There are restrictions to explicitly calling _alloca in an exception handler (EH). EH routines that run on x86-class processors operate in their own memory "frame": They perform their tasks in memory space that is not based on the current location of the stack pointer of the enclosing function. The most common implementations include Windows NT structured exception handling (SEH) and C++ catch clause expressions. Therefore, explicitly calling _alloca in any of the following scenarios results in program failure during the return to the calling EH routine:
Windows NT SEH exception filter expression: __except ( alloca() )
Windows NT SEH final exception handler: __finally { alloca() }
C++ EH catch clause expression
However, _alloca can be called directly from within an EH routine or from an application-supplied callback that gets invoked by one of the EH scenarios listed above.