Click to See Complete Forum and Search --> : ATL String: What's wrong with the USES_CONVERSION macros? How to avoid using them?


Siddhartha
April 17th, 2005, 03:56 PM
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!


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 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__alloca.asp)
_alloca (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__alloca.asp) 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:

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:

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':

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:


LPWSTR pwszMyWideCharString = L"Tring, Tring!";
LPSTR pszSimpleCharStringFromLPWSTR = ConvertLPWSTRToLPSTR(pwszMyWideCharString);

// .. use the string

delete [] pszSimpleCharStringFromLPWSTR;
SysFreeString (bstrMyBeaster);andBSTR 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.

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 (http://msdn.microsoft.com/library/en-us/vclib/html/_atl_String_Conversion_Macros.asp?frame=true#atl70stringconversionclassesmacros).