CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 1 of 1
  1. #1
    Join Date
    Oct 2002
    Location
    Germany
    Posts
    6,205

    Lightbulb 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.
    Last edited by Siddhartha; November 10th, 2005 at 06:11 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured