ATL String: What's wrong with the USES_CONVERSION macros? How to avoid using them?
Q: ATL String: What's wrong with the USES_CONVERSION macros? How to avoid using them?
A: The simplest way (in ATL 3.0) to convert a Wide Character String to an ANSI String is by using OLE2A or W2A, and their equivalent Macros. Simplest, but not the safest!
Code:
BSTR bstrMyBeaster = SysAllocString (L"Tring, Tring!");
WCHAR* pwszMyWCharString = L"Tring, Tring!";
USES_CONVERSION;
LPSTR pszCharStringFromBSTR = OLE2A (bstrMyBeaster);
LPSTR pszCharStringFromLPWSTR = W2A (pwszMyWCharString);
// ...
SysFreeString (bstrMyBeaster);
Q: If it is simple and if it works, then, what's wrong in using it?
A: Macros such as OLE2A, W2A and the likes cause Stack Overflows when used in loops.The reason:
They allocate memory using
_alloca _alloca allocates memory on the Function Stack. This memory is released ("popped") only on function exit.
So, a loop that loops too often and converts strings can result in a situation where the stack has no space left to offer.
This situation causes a Stack Overflow Exception.
Sample of a Prospective Stack Overflow Exception causing Function:
Code:
int StackGuzzler (void)
{
WCHAR* pwszTest = SOME_WCHAR_STRING;
for (int nCounter = 0; nCounter < SOME_MAX_COUNT; nCounter++)
{
USES_CONVERSION;
LPSTR pszCharVersion = W2A (pwszTest); // Allocated on stack
}
return 1;
} // Stack Memory is cleared i.e. "popped" here - this is sometimes too late!
Q: How do we overcome this Stack Overflow problem?
A: By not using the macros.
By simply delegating the string conversion to another function - one that returns the ANSI String (i.e. 'char*' or 'LPSTR' allocated on the heap/free store).
If you are using ATL 7.0, you have the option to use a set of Conversion Classes that are better suited. Take a look at the next question for further information.
Function that safely converts a BSTR to LPSTR:
Code:
char* ConvertBSTRToLPSTR (BSTR bstrIn)
{
LPSTR pszOut = NULL;
if (bstrIn != NULL)
{
int nInputStrLen = SysStringLen (bstrIn);
// Double NULL Termination
int nOutputStrLen = WideCharToMultiByte(CP_ACP, 0, bstrIn, nInputStrLen, NULL, 0, 0, 0) + 2;
pszOut = new char [nOutputStrLen];
if (pszOut)
{
memset (pszOut, 0x00, sizeof (char)*nOutputStrLen);
WideCharToMultiByte (CP_ACP, 0, bstrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0);
}
}
return pszOut;
}
Function that safely converts a 'WCHAR' String to 'LPSTR':
Code:
char* ConvertLPWSTRToLPSTR (LPWSTR lpwszStrIn)
{
LPSTR pszOut = NULL;
if (lpwszStrIn != NULL)
{
int nInputStrLen = wcslen (lpwszStrIn);
// Double NULL Termination
int nOutputStrLen = WideCharToMultiByte (CP_ACP, 0, lpwszStrIn, nInputStrLen, NULL, 0, 0, 0) + 2;
pszOut = new char [nOutputStrLen];
if (pszOut)
{
memset (pszOut, 0x00, nOutputStrLen);
WideCharToMultiByte(CP_ACP, 0, lpwszStrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0);
}
}
return pszOut;
}
Using them:
Code:
LPWSTR pwszMyWideCharString = L"Tring, Tring!";
LPSTR pszSimpleCharStringFromLPWSTR = ConvertLPWSTRToLPSTR(pwszMyWideCharString);
// .. use the string
delete [] pszSimpleCharStringFromLPWSTR;
SysFreeString (bstrMyBeaster);
and
Code:
BSTR bstrMyBeaster = SysAllocString (L"Tring, Tring!");
LPSTR pszSimpleCharStringFromBSTR = ConvertBSTRToLPSTR(bstrMyBeaster);
// ... use the string
delete [] pszSimpleCharStringFromBSTR;
SysFreeString (bstrMyBeaster);
The two methods above totally erase the possibility of causing a Stack Overflow whilst converting Wide-Character Strings.
Q: Does this issue persist with ATL 7.0?
A: Fortunately, no - as you now have the option to not use these macros. Changes with ATL 7.0:
- ATL 7.0 claims to resolve the issue of accumulating memory allocation per loop-cycle.
- ATL 7.0 does not require USES_CONVERSION macros.
- ATL 7.0 provides conversion (template) classes, and not macros.
Code:
CW2T pszString (L"Tring Tring");
// Use it as a LPCTSTR
std::cout << pszString;
For more information on how to convert strings using ATL 7.0, visit: ATL 7.0 String Conversion Classes and Macros.