Saturday, August 13, 2011

Perils of using W2A

Ever wondered why W2A leaks a lot of memory when used in a loop?

for(int i=0;i<count;i++)
{
      str= W2A(bstr);
      cout<<str<<endl;
           
}
When we try and step in at W2A we end up with following ASM code.
*********************************************************************************
_alloca_probe_16 proc                   ; 16 byte aligned alloca

        push    ecx
        lea     ecx, [esp] + 8          ; TOS before entering this function
        sub     ecx, eax                ; New TOS
        and     ecx, (16 - 1)           ; Distance from 16 bit align (align down)
        add     eax, ecx                ; Increase allocation size
        sbb     ecx, ecx                ; ecx = 0xFFFFFFFF if size wrapped around
        or      eax, ecx                ; cap allocation size on wraparound
        pop     ecx                     ; Restore ecx
        jmp     _chkstk
***********************************************************************************
What we see here is the stack is resized as in, W2A does an allocation on the stack
      so what would happen when this goes in a loop? obvious answer would be, the stack
      grows, and ends up with Stack Overflow problem.

So we end up with a couple of questions..
1.What is the best way takle this problem.
2.Are there any alternatives.
      3.Why USES_CONVERSION MACRO when we use W2A.

1.One way is to manually convert it CString like this function MyW2A
  does.

CString MyW2A(BSTR bstr)
{
      CString s;
      If(bstr!=NULL)
{
      LPSTR p = s.GetBuffer(SysStringLen(bstr) + 1);
      ::WideCharToMultiByte(CP_ACP,           
                                      0,                
                                      bstr,                 
                                      -1,                // assume NUL-terminated
                                      p,                
                                      SysStringLen(bstr)+1,
                                      NULL,             
                                      NULL);            
      s.ReleaseBuffer();
      }
      return s;
}//MyW2A
     
JNM(Joseph NewComer) has a very good article on String management here


      2. CW2A which has been provided as part of ATL 7.0 can be used instead of W2A,
                another cool thing about this is, USES_CONVERSION is not required for CW2A.


3. Why do we need, USES_CONVERSION MACRO when we use W2A?
   The answer lies in Atlconv.h file and the way these macros have been defined.
  
/*
*where is the _lpw defined?
*where is _convert defined?
*where is _acp defined?
*/
   #define W2A(lpw) (\
      ((_lpw = lpw) == NULL) ? NULL : (\
            (_convert = (lstrlenW(_lpw)+1), \
            (_convert>INT_MAX/2) ? NULL : \
            ATLW2AHELPER((LPSTR) alloca(_convert*sizeof(WCHAR)), _lpw, 
   _convert*sizeof(WCHAR), _acp))))

/*
* U guessed it right? Look at the definition of USES_CONVERSION
* Simple right!!!
*/
#define USES_CONVERSION int _convert = 0; (_convert);\
UINT _acp = ATL::_AtlGetConversionACP() /*CP_THREAD_ACP*/;\
(_acp); LPCWSTR _lpw = NULL; (_lpw); \
LPCSTR _lpa = NULL; (_lpa)

Another clue would be when we compile without the USES_CONVERSION macro, we end up with the following errors.

1>c:\..\vc days\.....\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_lpw' : undeclared identifier
1>c:\..\vc days\......\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_convert' : undeclared identifier
1>c:\..\vc days\......\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_lpw' : undeclared identifier
1>c:\..\vc days\......\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_convert' : undeclared identifier
1>c:\..\vc days\......\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_convert' : undeclared identifier
1>c:\..\vc days\......\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_lpw' : undeclared identifier
1>c:\..\vc days\......\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_convert' : undeclared identifier
1>c:\..\vc days\......\w2atest\w2atest\w2atest.cpp(45) : error C2065: '_acp' : undeclared identifier

No comments:

Post a Comment