Sometimes, I am willing to disclose :) some secrets of real performance. Even in the .NET world, we can't avoid BSTR allocation, and in the unmanaged coding world, automation and COM will be at our path once more.

In my honest opinion, many, many programmers make or made the mistake, in the unmanaged world, of not reallocating resources, instead, they just destroy the resource, and allocate it again. MS tried to fix this 'bug' by caching allocations, as much as possible. I find this decision, to start caching allocations, I mean for BSTR allocation, not a good decision. This must be one of the reasons, that COM in a multitasked, MPS environment sometimes, simply cannot scale!

I'll explain why. In a single user environment, caching data for a thread is a good idea, since say MS Word, and scripts like in VBA and VBS, might reuse data/allocations. But as soon as our ASP/COM server environment starts to do this, and the code is reentrant, caching is useless, since threads that allocated data, might not be allowed to reuse zombie-data (if a caching-pattern is used) from another thread.

It could have been solved so easily! (Now, I might sound presumptuous to say that, I agree) How? Just don't cache but reallocate!

VB6 and automation clients for instance, uses the BSTR datatype all over the place, and ATL when used in a COM environement, does as well. If you look at the compiled code that programmers deliver, they never reallocate (only the runtime does sometimes). So for instance myString = "Hello" and myString = "bye" could have been compiled internally by:
myString = SysAllocString(L"Hello");
SysReAllocString(myString, L"bye");

And that's really all!
SysReAllocString(Len) internally uses CoTaskMemRealloc and that function tries to enlarge the memory allocation in-place, and this at its turn minimizes RAM-synchronization in MPS systems on the CPU.

So far, my theory. Am I just filling up your internet-html disk-cache and chit-chatting because I'm just idle for an hour? No.

Let's just try this out!

I've rewritten CComBSTR (from the ATL namespace) and you can find this in the platform SDK at \PlatSDK\Include\atl\atlbase.h (in case you have Visual Studio 2005, don't use this location, but use the most recent header files).

This silly little program does nothing but appending random (sort of) wide strings to a BSTR allocation. Let's rewrite CComBSTR and measure it!

I assume that you can get the headers right to get the program below compile and run.

int _tmain()
{
HRESULT hr = S_OK;
CoInitialize(NULL);
{

_tmain()
{
HRESULT hr = S_OK;
CoInitialize(NULL);
{

CComBSTR appendPlay;
DWORD timer = GetTickCount();
// Ethan Winer, an Assembly coding specialist, once thought me that loops counting down to zero are faster, this still is the case!, just a silly fact.

for(int xy = 10000; xy > 0; xy--)
{
   PWSTR zy = xy % 2 == 0 ? L"hiya" : L"bye";

      appendPlay.Append(zy);
}
wprintf(L"speed %d\n", GetTickCount() - timer);
}
CoUninitialize();
}

Now run the code and on my AMD 3200+ system, this takes 578 time ticks. This is even with the OLE BSTR cache enabled! (When caching is disabled, this takes 520 time ticks).

Let's improve the Append part of CComBSTR (make sure you keep the original atlbase.h intact). In my case, I just redefined CComBSTR to CComBSTR2 and copy-pasted all of it and rewrote the slow parts.

The slow original code is using the 'delete' 'allocate' sequence.
// very slow original
HRESULT __stdcall Append(LPCOLESTR lpsz, int nLen) throw()

{

if(lpsz == NULL)

      {

if(nLen != 0)

return E_INVALIDARG;

else

return S_OK;

}

                      

int n1 = Length();

if (n1+nLen < n1)

return E_OUTOFMEMORY;

BSTR b;

b = ::SysAllocStringLen(NULL, n1+nLen);

if (b == NULL)

return E_OUTOFMEMORY;

if(m_str != NULL)

memcpy(b, m_str, n1*sizeof(OLECHAR));

                      

memcpy(b+n1, lpsz, nLen*sizeof(OLECHAR));

b[n1+nLen] = NULL;

SysFreeString(m_str);

m_str = b;

return S_OK;

}

And here goes the improved code. It has the Automation runtime resize the BSTR while the string in most cases remains at the same memory address. This is how the original BSTR programmers have designed for performance, while nobody is utilizing it! But we instead, do use it, as you understand.

HRESULT __stdcall Append(LPCOLESTR lpsz, int nLen) throw()

{

      if (lpsz == NULL || (m_str != NULL && nLen == 0))

            return S_OK;

      int n1 = Length();

      HRESULT hr = SetLength(n1 + nLen);

      if ( SUCCEEDED(hr) )       

         memcpy(m_str+n1, lpsz, nLen*sizeof(OLECHAR));

      return hr;

}


We need to append the _SetLength function, which is a static wrapper for SysReAllocStringLen(..)

// Cuts the length to specified but does not clear contents

HRESULT __stdcall SetLength(unsigned int length) throw()

{

      return _SetLength(&m_str, length);

}

static HRESULT __stdcall  _SetLength(BSTR * str, unsigned int length) throw()

{

      return ::SysReAllocStringLen(str, NULL, length) == FALSE ? E_OUTOFMEMORY : S_OK;

}

I've included the full 99% compatible CComBSTR2 replacement for you, as a handy dowload so bother about that later. :)

Now, get me to the results please, how much faster would this code run now?

Yes, it takes a whopping 15 milliseconds! And figure that, against 578 milliseconds, which makes the improvement 3800%

// Cuts the length to specified but does not clear contents

HRESULT __stdcall SetLength(unsigned int length) throw()

{

      return _SetLength(&m_str, length);

}

static HRESULT __stdcall  _SetLength(BSTR * str, unsigned int length) throw()

{

      return ::SysReAllocStringLen(str, NULL, length) == FALSE ? E_OUTOFMEMORY : S_OK;

}

I've included the full 99% compatible CComBSTR2 replacement for you, as a handy dowload so bother about that later. :)

Now, get me to the results please, how much faster would this code run now?

Yes, it takes a whopping 15 milliseconds! And figure that, against 578 milliseconds, which makes the improvement 3800%

Now you might understand why .NET had to be invented by MS :) they figured that the maximum scalability limit was hit on real MPS systems, and that COM never could perform the task of being scalable just because the BSTR sucks in performance! And now, you know that, I'm just now on the conspiracy path, and I'm lying :-).

Anyway, the conclusion is, that current COM clients, and applications and servers, could, if they would like to, improve a lot for free, by just removing the BSTR reallocation barrier and take advantage of the maximum 'unmanaged code'  speed possible on a Windows (r) System.

The non-conspiracy theory is that caching was made in times, when Microsoft did not play a big role in server environments, and that computers were relativily slow. A conclusion would be that if MS would like to improve an old car (COM) for free, they'd just remove the caching and implement the idea that I've proven to be very good. This would be good for classic ASP pages as well that are still very popular on the internet.

Just a final question. Why bother? If you are an Automation developer, creating services that depend heavily on BSTRs?
The answer is, I bothered once a while ago, just because of some artistic feeling (good programmers are artists, not scientists :) ) that I could improve the enormous BSTR stress on my product here
Isp Session Manager. Of course, the biggest part of such managed COM server (as COM+ was called in the past!) is doing talking to a DBMS. But after implementing the CComBSTR replacement, the performance on a MPS server, suddenly got very easy and the pages per second throughput went up and showed a flat line (that's the wet dream of each webmaster). Before, without using the CComBSTR replacement the throughtput was erratic, so this again proved my point, that not-reallocating BSTRs causes a huge demand on RAM synchronization and makes scalability limited because of wrong usage of the COM runtime.

Here, you got the CComBSTR whopper for your own downloads. Please do not forget to deploy it after download...

[edit, nov 2008] It seems that attached links on this BLOG are not supported anymore.

Therefore, the sourcecode inline.

#pragma once

#ifndef CComBSTR

#define CComBSTR CComBSTR2

#endif

#define CComVariant CComVariant2

namespace ATL

{

//typedef  HRESULT  (__stdcall*HASHDATA2) (LPBYTE, DWORD, LPBYTE, DWORD);

/////////////////////////////////////////////////////////////////////////////

// CComBSTR2

class CComBSTR2

{

public:

      BSTR m_str;

      CComBSTR2() throw()

      {

            m_str = NULL;

      }

      CComBSTR2(_In_ int nSize)

      {

            //if (nSize == 0) //BUG it should be possible to assign a L"" string

            m_str = NULL;

            HRESULT hr = SetLength(nSize);

            if (FAILED(hr))

                  AtlThrow(hr);

            ZeroMemory(m_str, nSize * sizeof(wchar_t));

           

      }

      CComBSTR2(_In_ int nSize, _In_opt_count_(nSize) LPCOLESTR sz)

      {

            if (nSize == 0)

                  m_str = NULL;

            else

            {

                  m_str = ::SysAllocStringLen(sz, nSize);

                  if (m_str == NULL)

                        AtlThrow(E_OUTOFMEMORY);

            }

      }

      CComBSTR2(_In_opt_ LPCOLESTR pSrc)

      {

            if (pSrc == NULL)

                  m_str = NULL;

            else

            {

                  m_str = ::SysAllocString(pSrc);

                  if (m_str == NULL)

                        AtlThrow(E_OUTOFMEMORY);

            }

      }

      CComBSTR2(_In_ const CComBSTR& src)

      {

            m_str = src.Copy();

            if (!!src && m_str == NULL)

                  AtlThrow(E_OUTOFMEMORY);

      }

      CComBSTR2(_In_ REFGUID guid)

      {

            wchar_t szGUID[64];

           

            m_str = ::SysAllocStringLen(szGUID,

                  ::StringFromGUID2(guid, szGUID, 64)

                  );

            if (m_str == NULL)

                  AtlThrow(E_OUTOFMEMORY);

      }

      CComBSTR2& operator=(_In_ const CComBSTR& src)

      {

            if (m_str != src.m_str)

            {

                  if (::SysReAllocStringLen(&m_str, src, src.Length()) == FALSE)              

                        AtlThrow(E_OUTOFMEMORY);

            }

            return *this;

      }

      CComBSTR2& operator=(_In_opt_ LPCOLESTR pSrc)

      {

            if (pSrc != m_str)

            {

                  if (pSrc != NULL)

                  {

                        if (::SysReAllocString(&m_str, pSrc) == FALSE)                  

                             AtlThrow(E_OUTOFMEMORY);

                  }

                  else             

                        Empty();

                 

            }

            return *this;

      }

//???

      inline ~CComBSTR2() throw()

      {

            ::SysFreeString(m_str);

      }

      static ULONG GetStreamSize(BSTR bstr)

      {

            ULONG ulSize=sizeof(ULONG);

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "The semantics of this function are about allocation, not content")

            if (bstr != NULL)

#pragma prefast(pop)

#pragma warning(pop)

            {

                  ulSize += SysStringByteLen(bstr) + sizeof(wchar_t);

            }

            return ulSize;

      }

      unsigned int __stdcall Length() const throw()

      {

            return ::SysStringLen(m_str);

      }

      unsigned int __stdcall ByteLength() const throw()

      {

            return ::SysStringByteLen(m_str);

      }

      operator BSTR() const throw()

      {

            return m_str;

      }

#ifndef ATL_CCOMBSTR_ADDRESS_OF_ASSERT

// Temp disable CComBSTR::operator& Assert

#define ATL_NO_CCOMBSTR_ADDRESS_OF_ASSERT

#endif

      BSTR* operator&() throw()

      {

#ifndef ATL_NO_CCOMBSTR_ADDRESS_OF_ASSERT

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "We are deliberately checking if this has already been allocated")

            ATLASSERT(!*this);

#pragma prefast(pop)

#pragma warning(pop)

#endif

            return &m_str;

      }

      BSTR __stdcall Copy() const throw()

      {

            if (*this == NULL)

                  return NULL;

            else

            {

                  unsigned int copyLen = ByteLength();

                  BSTR retVal = ::SysAllocStringByteLen(NULL, copyLen);

                  if (copyLen > 0 && retVal != NULL)

                  {

                        memcpy_s(retVal, copyLen, m_str, copyLen);

                  }

                  return retVal;

            }

     

      }

      //modified by E.N. pbstr is now in/out!

      // you must care for it that it properly gets initialized

      _Check_return_ STDMETHODIMP CopyTo(__inout_opt BSTR* pbstr) const throw()

      {

            ATLASSERT(pbstr != NULL);

            HRESULT hr;

            if (pbstr == NULL)

                  hr = E_POINTER;        

            else

            {

#ifdef SysReAllocStringByteLen

                  unsigned int copyLen = ByteLength();

                  hr = SysReAllocStringByteLen(pbstr, NULL, copyLen) == FALSE ? E_OUTOFMEMORY : S_OK;

                  if (hr == S_OK && copyLen > 0)

                  {

                        memcpy_s(*pbstr, copyLen, m_str, copyLen);

                  }

#else

            *pbstr = Copy();

            #pragma warning(push)

            #pragma warning(disable:4068)

            #pragma prefast(push)

            #pragma prefast(disable:325, "We are checking allocation semantics here")

            if ((*pbstr == NULL) && (m_str != NULL))

        {

                  return E_OUTOFMEMORY;

        }

            #pragma prefast(pop)

            #pragma warning(pop)

#endif

            }

            return hr;

      }

      // *** returns true if length equals zero characters or when unallocated(null pointer)

      bool __stdcall IsEmpty (void) throw()

      {

            return m_str == NULL || Length() == 0;

      }

      /* added by may 2005 e.n. needs #include 'wchar.h'*/

      HRESULT __stdcall Format(__in PCWSTR pszFormat, __in va_list args) throw()

      {

            size_t len = _vscwprintf( pszFormat, args );

           

            HRESULT hr = SetLength((UINT)len) ;

            if(SUCCEEDED(hr))

                  if (vswprintf( m_str, len + 1, pszFormat, args ) < 0)

                        hr = E_INVALIDARG;

           

            return hr;

      }

      /* added by may 2005 e.n. needs #include 'wchar.h'*/

      HRESULT __cdecl Format(__in PCWSTR pszFormat, ...) throw()

      {

            va_list args;               

            va_start( args, pszFormat );      

            HRESULT hr = Format(pszFormat, args);

            va_end(args);

           

            return hr;

      }

private:

      HRESULT __stdcall Insert(__in unsigned int atPosition, __in_opt LPCOLESTR lpsz, __in unsigned int nLen) throw()

      {

            unsigned int curLen = Length();

            HRESULT hr = S_OK;

            if (atPosition > curLen || lpsz == NULL)

                  hr = E_INVALIDARG;

            else

                  hr = SetLength(curLen + nLen);

            if (SUCCEEDED(hr) && curLen != 0 && nLen != 0)

            {

                  wmemmove(&m_str[atPosition + nLen], &m_str[atPosition],

                        curLen - atPosition);

                  wmemcpy_s(&m_str[atPosition], curLen + nLen - atPosition, lpsz, nLen );

            }

            return hr;

      }

public:

      HRESULT __stdcall  Insert(__in unsigned int atPosition, __in_opt PCWSTR value) throw()

      {

            return Insert(atPosition, value, (unsigned int)wcslen(value));

      }

      HRESULT __stdcall Insert(__in unsigned int atPosition, __in const CComBSTR& value) throw()

      {

            return Insert(atPosition, value.m_str, value.Length());

      }

      HRESULT TrimStart(__in_opt PCWSTR trimwhat = NULL) throw()

      {

            PCWSTR trim = trimwhat == NULL? L" ": trimwhat;

            if (IsEmpty()) return S_OK;

            unsigned int trimLen = (unsigned int)wcslen(trim);

            while(StartsWith(trim))

                  Remove(0, trimLen);

            return S_OK;

      }

      HRESULT TrimEnd(__in_opt PCWSTR trimwhat = NULL) throw()

      {

            PCWSTR trim = trimwhat == NULL? L" ": trimwhat;

            if (IsEmpty()) return S_OK;

            unsigned int trimLen = (unsigned int)wcslen(trim);

            while(EndsWith(trim))

                  SetLength(Length() - trimLen);

            return S_OK;

      }

      //** removes in-place characters from this BSTR

      HRESULT __stdcall Remove(

            //** zero based starting position where you start to remove characters

            //** if this number is outside valid bounds, E_INVALIDARG is returned

            __in unsigned int startIndex,

            //** the number of characters, you want to remove

            //** if this number is outside valid bounds, it is corrected

            __in unsigned int count) throw()

      {

            unsigned int maxIdx = Length();

            // avoid buffer overflow

            if (count + startIndex > maxIdx) count = maxIdx - startIndex;

            HRESULT hr = S_OK;

            if (startIndex < maxIdx)                      

            {

                  if (maxIdx - startIndex - count != 0)               

                        //copy back, overlapped memory

                        wmemmove_s(&m_str[startIndex], maxIdx - startIndex, &m_str[startIndex + count] ,

                             maxIdx - startIndex - count);

                  // shrink to new length

                 

                  hr = SetLength(maxIdx - count);

            }

            else

                  hr = E_INVALIDARG;

           

            return hr;

      }

      // original string john smith

      // merge with west at position 6

      // result john westh

      // won't extend string length!

      void __stdcall MergeString(__in unsigned int startIndex, __in const BSTR value) throw()

      {

            unsigned int maxIdx = Length();

            if (startIndex > maxIdx || value == NULL) return; // illegal operation

            unsigned int mergeLen = SysStringLen(value);

            if (mergeLen + startIndex > maxIdx)

                  mergeLen = maxIdx - startIndex;

            wmemcpy_s(&m_str[startIndex], maxIdx - startIndex, value, mergeLen);

      }

      BSTR __stdcall Substring(__in unsigned int startIndex) throw()

      {

            unsigned int maxIdx = Length();

            if (m_str != NULL && startIndex >= 0 && startIndex <= maxIdx)

            {

                  return ::SysAllocStringLen(m_str + startIndex, maxIdx - startIndex);

            }

            else

                  return NULL;

      }

     

      HRESULT __stdcall SetByteLength(__in unsigned int length) throw()

      {

            return _SetByteLength(&m_str, length);

      }

     

      // Cuts the length to specified but does not clear contents

      HRESULT __stdcall SetLength(__in unsigned int length) throw()

      {

            return _SetLength(&m_str, length);

      }

private:

      static HRESULT __stdcall _SetByteLength(_Inout_ BSTR *str, __in unsigned int length) throw()

      {

            #ifdef SysReAllocStringByteLen

                  return SysReAllocStringByteLen2(str, NULL, length) == FALSE ? E_OUTOFMEMORY : S_OK;

            #else

                  BSTR Copy = NULL;

                  UINT origLen = SysStringByteLen(*str);

                  if (origLen != 0)

                  {

                        Copy = SysAllocStringByteLen((LPCSTR)*str, length);

                        SysFreeString(*str);

                        *str = Copy;

                  }

                  return *str == NULL  ? E_OUTOFMEMORY : S_OK;

            #endif

      }

      static HRESULT __stdcall  _SetLength(_Inout_ BSTR * str, __in unsigned int length) throw()

      {

            return ::SysReAllocStringLen(str, NULL, length) == FALSE ? E_OUTOFMEMORY : S_OK;

      }

public:

      int __stdcall TokenCount(__in PCWSTR find, __in bool caseInsensitive) throw()

      {

            int strLen = (int)Length();

            int tokenCount = 0;

            int findLen = (int)wcslen(find);

            for(int x = 0;

                  (x = IndexOf((const PWSTR)find, x, caseInsensitive)) >= 0;)

            {

                  x += findLen;

                  tokenCount++;

            }

            return tokenCount;

      }

      /// <summary>

      /// Replaces a find token with a specified token

      /// By E.N.

      /// </summary>   

      /// <param name="find">token to be replace</param>

      /// <param name="replace">token that will replace</param>

      /// <param name="caseInsensitive">if true, will do a case insensitive replacement</param>

      /// <example>

      /// CComBSTR2 myReplace(L"the dog jumps over the");

      /// myReplace(CComBSTR2(L"the"), CComBSTR2(L"big"));

      /// </example>

      /// <returns>The example would modify the string to "big dog jumps over big" </returns>     

      HRESULT __stdcall Replace(__in_opt BSTR find, __in_opt BSTR replace, __in bool caseInsensitive) throw()

      {

            HRESULT hr = S_OK;

            if (m_str == NULL || find == NULL || replace == NULL)

                  hr = E_INVALIDARG;

            else

            {

                  int countTokens = TokenCount(find, caseInsensitive);

                  if (countTokens == 0) return hr;

                  int findLen = SysStringLen(find);

                  int replaceLen = SysStringLen(replace);

                  int lengthDiff = replaceLen - findLen;

                  int oldLen = (int)Length() ;

                  int newLen = oldLen + (countTokens * lengthDiff);

                  if (lengthDiff > 0)

                  {

                        hr = SetLength(newLen);

                        if (FAILED(hr)) return hr;

                  }

                 

                  for(int x = 0;

                        (x = IndexOf(find, x, caseInsensitive)) >= 0                    

                        ; )

                  {

                        int offset = x + findLen;

                        if (lengthDiff != 0)

                        {

                             wmemmove_s(&m_str[offset + lengthDiff],

                                   (lengthDiff < 0 ? oldLen : newLen) - offset,

                                   &m_str[offset],

                                   oldLen - offset);

                             oldLen += lengthDiff;

                        }                                                         

                        if (replaceLen > 0)

                        {

                             MergeString(x, replace);

                             x += replaceLen;            

                        }

                  }

                  if (lengthDiff < 0)

                  {

                        hr = SetLength(newLen);                 

                  }

            }

            return hr;

      }

      /// <summary>

      /// Replaces a find token with a specified token

      /// <param name="find">token to be replace</param>

      /// <param name="replace">token that will replace</param>

      /// </summary>   

      /// <example>

      /// CComBSTR2 myReplace(L"the dog jumps over the");

      /// myReplace(CComBSTR2(L"the"), CComBSTR2(L"big"));

      /// </example>

      /// <returns>The example would modify the string to "big dog jumps over big" </returns>     

      HRESULT __stdcall Replace(__in_opt const BSTR find, __in_opt const BSTR replace) throw()

      {

            return Replace(find, replace, false);

      }

      SAFEARRAY* __stdcall Split(__in_opt PCWSTR expression, __in const bool caseInsenstive) throw()

      {

            return Split(CComBSTR(expression), caseInsenstive);

      }

      /// <summary>

      /// Split and copies this instance of CComBSTR2 into a SAFEARRAY* of VTYPE = VT_BSTR

      /// </summary>   

      /// <example>

      /// CComSafeArray<BSTR> myArray;

      /// CComBSTR2 joined(L"John|Smith");

      /// myArray.Attach(joined.Split(CComBSTR2(L"|")))

      /// </example>

      /// <returns>The example would return a safearray with 2 VT_BSTR elements containing "John" and "Smith"</returns>

      SAFEARRAY* __stdcall Split(__in const CComBSTR& expression, __in const bool caseInsensitive)

      {

            SAFEARRAY* retval = NULL;

            HRESULT hr = S_OK;

            if (m_str == NULL)

                  AtlThrow(E_POINTER);

            else

            {

                  unsigned int exprLen = expression.Length();

                  unsigned int mLen = Length();

                 

                  int x = 0;

                  //contains the number of found expression tokens

                  unsigned int found = mLen == 0 ? -1 : 0;                        

                  //find until no more...

                  if (expression != NULL && found >= 0)

                  {

                        for (;;)

                        {

                             x = IndexOf(expression, x, caseInsensitive);

                             if (x == -1) break;                     

                             found++;

                             x += exprLen;                     

                        }                      

                  }                

                  SAFEARRAYBOUND rgsa = {found + 1, 0};               

                  retval = ::SafeArrayCreate(VT_BSTR, 1, &rgsa);

                  if (retval == NULL)

                        return retval;

                 

                  else if (mLen > 0)

                  {                      

                        BSTR* paBSTR  ;//(BSTR*)retval->pvData;

                        hr = SafeArrayAccessData(retval, (void**)&paBSTR);                    

                        int prevPos = 0;

                        x = 0;

                        for (unsigned int curEl = 0; curEl <= found; curEl++)

                        {

                             x = IndexOf(expression, x, caseInsensitive);                          

                             paBSTR[curEl] = x < 0 ? Substring(prevPos) : Substring(prevPos, x - prevPos);

                             if (paBSTR[curEl] == NULL)

                             {

                                   hr = E_OUTOFMEMORY;

                                   break;

                             }

                             x += exprLen;    

                             prevPos = x;                            

                        }                      

                        SafeArrayUnaccessData(retval);                            

                  }

                  if (FAILED(hr))

                  {

                        AtlTrace(L"general split fail %x", hr);

                        if (retval != NULL)

                             ::SafeArrayDestroy(retval);

                        retval = NULL;

                        AtlThrow(hr);

                  }                      

            }

            return retval;

      }

      SAFEARRAY* __stdcall Split(__in_opt PCWSTR expression) throw()

      {

            return Split(CComBSTR(expression), false);

      }

      /// <summary>

      /// Added by E.N.

      /// Joins a SAFEARRAY to a single BSTR

      /// </summary>   

      /// <example>

      /// CComSafeArray<BSTR> myArray;  

      /// myArray.Add(CComBSTR2(L"John"));

      /// myArray.Add(CComBSTR2(L"Smith"));

      /// CComBSTR2 joined;

      /// joined.Attach(CComBSTR2::Join(myArray.m_psa, CComBSTR2(L"|") ) );  ///

      /// </example>

      /// <returns>The example would return "John|Smith"</returns>    

      static BSTR __stdcall Join(__in_opt SAFEARRAY *psa, __in const BSTR delimiter) //throw()

      {

            BSTR retval = NULL;

            HRESULT hr = S_OK;

            if (psa != NULL && psa != NULL)

            {

                  VARTYPE vt = VT_EMPTY;

                  unsigned int delLen = ::SysStringLen(delimiter);

                  hr = ::SafeArrayGetVartype(psa, &vt);

                  if (vt != VT_BSTR || psa->cDims != 1)

                        AtlThrow(E_INVALIDARG);

                  SAFEARRAYBOUND *rgsa = psa->rgsabound;

                  ULONG elements = rgsa->cElements - rgsa->lLbound;

                  unsigned int totalLen = 0;

                  BSTR* paBSTR = (BSTR*) psa->pvData;           

                 

                  ULONG els = elements;

                  while(els != 0)

                  {

                        if (paBSTR[--els] != NULL)

                             totalLen += ::SysStringLen(paBSTR[els]);

                        if (els != 0)

                             totalLen += delLen;

                  }

                  hr = _SetLength(&retval, totalLen);

                  if (FAILED(hr))

                        AtlThrow(hr);

                  else

                  {

                        totalLen = 0;

                        els = elements;

                        ULONG curel = 0;

                        while (els != 0)

                        {

                             els--;

                             if (paBSTR[curel] != NULL)

                             {

                                   unsigned int curLen = ::SysStringLen(paBSTR[curel]);

                                   wmemcpy_s(retval + totalLen, curLen, paBSTR[curel], curLen);

                                   totalLen += curLen;

                             }

                             curel++;

                             if (els != 0 && delLen != 0)

                             {

                                   wmemcpy_s(retval + totalLen, delLen, delimiter, delLen);

                                   totalLen += delLen;

                             }                           

                        }

                  } //success allocate string                   

                 

            }

            return retval;

      }

      /// <summary>

      /// Added by E.N.

      /// Returns a new instance of a BSTR which is a Substring starting at the 'startindex' character

      /// </summary>   

      /// <example>

      /// CComBSTR2 john(L"John Smith"), subbed;

      /// subbed.Attach(john.Substring(5));

      /// </example>

      /// <returns>The example would return "Smith"</returns>   

      BSTR __stdcall Substring(__in const unsigned int startIndex, __in unsigned int length) throw()

      {

            unsigned int maxIdx = Length();

            //if (length < 0) length = 0;

            if (startIndex + length > maxIdx)

                  length = maxIdx - startIndex;

            if (m_str != NULL && startIndex >= 0 && startIndex <= maxIdx)

            {

                  return ::SysAllocStringLen(m_str + startIndex, length);

            }

            else

                  return NULL;

      }

private:

      bool __stdcall local_Test(__in_opt PCWSTR src, __in unsigned int startIndex) throw()

      {

            bool retval = false;

            if (src != NULL)

            {

                  unsigned int compLen = lstrlenW(src);

                  unsigned int thisLen = Length();

                  if (compLen <= thisLen)

                  {

                        DWORD dwCompFlags = 0;

                        retval = ::CompareStringW(::GetThreadLocale(), dwCompFlags,

                             &m_str[thisLen - startIndex], compLen, src, compLen) == CSTR_EQUAL;

                  }

            }

            return retval;

      }

public:

      bool __stdcall EndsWith(__in_opt PCWSTR src) throw()

      {

            return local_Test(src, lstrlenW(src));

      }

      bool __stdcall StartsWith(__in_opt PCWSTR src) throw()

      {

            return local_Test(src, Length());

           

      }

      int __stdcall LastIndexOf(__in const wchar_t src, __in_opt const unsigned int startIndex = 0, __in const bool caseInsensitive = false) throw()

      {

            wchar_t  src2[] = {src, NULL}; //create zero terminated PWSTR

            return LastIndexOf(src2, startIndex, caseInsensitive);

      }

      int __stdcall LastIndexOf(__in_opt const wchar_t *src, __in const unsigned int startIndex = 0, __in const bool caseInsensitive = false, unsigned int count = 0) throw()

      {

            int result = -1;

            if (m_str != NULL && src != NULL)

            {

                  LCID lcid = ::GetThreadLocale();

                  DWORD dwCmpFlags = caseInsensitive ? NORM_IGNORECASE : 0;

           

                  unsigned int compLen = (unsigned int)wcslen(src);

                  unsigned int maxLen = Length();

                  unsigned int examinedChars = 0;

                  if (compLen <= maxLen)

                  {

                        for(unsigned int x = maxLen - compLen; x >= startIndex; )

                        {

                             bool doCompare = caseInsensitive ? true : m_str[x] == src[0];

                             if (doCompare)

                                   doCompare=  ::CompareStringW(lcid, dwCmpFlags, &m_str[x],

                                               compLen, src, compLen) == CSTR_EQUAL     ;

                             if (doCompare)

                             {

                                   result = x;

                                   break;

                             }

                             if (x-- == 0 || (examinedChars++ == count && count != 0)) break;                            

                        }

                  }

            }

            return result;

      }

      int __stdcall IndexOf(__in const wchar_t src, __in const unsigned int startIndex = 0, __in const bool caseInsensitive = false, unsigned int count = 0) throw()

      {

            wchar_t  src2[] = {src, NULL}; //create zero terminated PWSTR

            return IndexOf(src2, startIndex, caseInsensitive, count);

      }

      //

      // Addded by E.N.

      //

      int __stdcall IndexOf(__in_opt const PWSTR src, __in const unsigned int startIndex = 0, __in const bool caseInsensitive = false, unsigned int count = 0) throw()

      {

            int result = -1;

           

            if (m_str != NULL && src != NULL)

            {                

                  LCID lcid = ::GetThreadLocale();

                  DWORD dwCmpFlags = caseInsensitive ? NORM_IGNORECASE : 0;

                  unsigned int maxLen = Length();

                  unsigned int compLen = (unsigned int)wcslen(src);

                  unsigned int examinedChars = 0;

                  if (compLen <= maxLen && compLen > 0)

                  {

                        for(int x = startIndex; (x + compLen <= maxLen) || (examinedChars++ == count && count != 0); x++)

                        {

                             bool doCompare = caseInsensitive ? true :

                                   m_str[x] == src[0];

                              if (doCompare)

                                   doCompare=  ::CompareStringW(lcid, dwCmpFlags, &m_str[x],

                                               compLen, src, compLen) == CSTR_EQUAL     ;

                             if (doCompare)                    

                             {

                                   result = x;

                                   break;

                             }                                 

                        }

                  }                           

            }

            return result;

      }

      // copy BSTR to VARIANT

      _Check_return_ STDMETHODIMP __stdcall CopyTo(__out VARIANT *pvarDest) throw()

      {

            ATLASSERT(pvarDest != NULL);

            HRESULT hRes = E_POINTER;

            if (pvarDest != NULL)

            {

                  pvarDest->vt = VT_BSTR;

                  pvarDest->bstrVal = Copy();

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "We are checking allocation semantics here")

                  if (pvarDest->bstrVal == NULL && m_str != NULL)

            {

                        hRes = E_OUTOFMEMORY;

            }

#pragma prefast(pop)

#pragma warning(pop)

                  else

            {

                        hRes = S_OK;

            }

            }

            return hRes;

      }

      void __stdcall Attach(__in_opt BSTR src) throw()

      {

            if (m_str != src)

            {

                  SysFreeString(m_str);

                  m_str = src;

            }

      }

      BSTR Detach() throw()

      {

            BSTR s = m_str;

            m_str = NULL;

            return s;

      }

      void Empty() throw()

      {

            ::SysFreeString(m_str);

            m_str = NULL;

      }

      bool __stdcall operator!() const throw()

      {

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "The semantics of this function are about allocation, not content")

            return (m_str == NULL);

#pragma prefast(pop)

#pragma warning(pop)

      }

     

      HRESULT __stdcall Append(__in const CComBSTR2& bstrSrc) throw()

      {

            return AppendBSTR(bstrSrc.m_str);

      }

      /*HRESULT Append(const CComVariant2& pVarSrc) throw()

      {

            return Append(pVarSrc.pvarVal);

      }*/

      //Added by E.N.

      HRESULT __stdcall Append(__in const VARIANT& pVar) throw()

      {

            HRESULT hr = S_OK;

            if (pVar.vt != VT_BSTR)

            {

                  VARIANT v = {0};

                  hr = ::VariantChangeTypeEx(&v,(VARIANT*)&pVar,

                        ::GetThreadLocale(), 0, VT_BSTR);

                  if (hr == S_OK) hr = AppendBSTR(v.bstrVal);

                  VariantClear(&v);

            }

            else

                  hr = AppendBSTR(pVar.bstrVal);

            return hr;

      }

      HRESULT __stdcall Append(__in_opt  LPCOLESTR lpsz) throw()

      {

            return Append(lpsz, (UINT)wcslen(lpsz));

      }

      // a BSTR is just a LPCOLESTR so we need a special version to signify

      // that we are appending a BSTR

      HRESULT __stdcall AppendBSTR(__in_opt BSTR p) throw()

      {

            return Append((LPCOLESTR)p, ::SysStringLen(p));

      }

      HRESULT __stdcall Append(__in_ecount_opt(nLen) LPCOLESTR lpsz, __in int nLen) throw()

      {

            if (lpsz == NULL || (m_str != NULL && nLen == 0))

                  return S_OK;

            int n1 = Length();

            HRESULT hr = SetLength(n1 + nLen);

            if ( FAILED(hr))       

                  return hr; 

            wmemcpy_s(m_str+n1, (n1 + nLen), lpsz, nLen);

            return S_OK;

      }

      HRESULT __stdcall Append(__in char ch) throw()

      {

            OLECHAR chO = ch;

            return( Append( &chO, 1 ) );

      }

      HRESULT __stdcall Append(__in wchar_t ch) throw()

      {

            return( Append( &ch, 1 ) );

      }

      HRESULT __stdcall AppendBytes(__in_ecount_opt(nLen) const char* lpsz, __in int nLen) throw()

      {

            if (lpsz == NULL || nLen == 0)

                  return S_OK;

            int n1 = ByteLength();

            BSTR b;

            b = ::SysAllocStringByteLen(NULL, n1+nLen);

            if (b == NULL)

                  return E_OUTOFMEMORY;

            memcpy_s(b, n1+nLen, m_str, n1);

            memcpy_s(((char*)b)+n1, nLen, lpsz, nLen);

            *((OLECHAR*)(((char*)b)+n1+nLen)) = NULL;

            Empty();

            m_str = b;

            return S_OK;

      }

      static int __stdcall CountChar(__in const BSTR a, __in wchar_t theChar) throw()

      {

            int retval = 0;

            if (a != NULL)

            {

                  unsigned int x  = ::SysStringLen(a);                            

                  while (x != 0)              

                        if (a[--x] == theChar) retval++;        

            }

            return retval;

      }

      //returns -1 for lesser (<); 0 for equality and 1 for GT (>)

      static int __stdcall Compare(__in const BSTR a, __in const BSTR b, __in bool ignoreCase, __in bool ignoreDiacritics, bool ignoreSymbols) throw()

      {

            int retval = 0;

            DWORD compareFlags = ignoreCase ? NORM_IGNORECASE : 0;

            if (ignoreDiacritics) compareFlags |= NORM_IGNORENONSPACE;

            if (ignoreSymbols) compareFlags |= NORM_IGNORESYMBOLS;

            if (a == NULL && b == NULL)

                  retval = 0;

            else if (a == NULL)

                  retval = -1;

            else if (b == NULL)

                  retval = 1;

            else

            {

                  switch(::VarBstrCmp(a, b, ::GetThreadLocale(), compareFlags))

                  {

                  case VARCMP_LT:

                        retval = -1;

                        break;

                  case VARCMP_EQ:

                        retval = 0;

                        break;

                  case VARCMP_GT:

                        retval = 1;

                        break;           

                  }

            }

            return retval;

      }

     

      int __stdcall CompareTo(__in const BSTR otherBstr) throw()

      {

            return Compare(m_str, otherBstr, false, false, false);

      }

      int __stdcall CompareTo(__in const BSTR otherBstr, __in bool ignoreCase) throw()

      {

            return Compare(m_str, otherBstr, ignoreCase, false, false);

      }

      int __stdcall CompareTo(__in const BSTR otherBstr, __in bool ignoreCase, __in bool ignoreDiacritics) throw()

      {

            return Compare(m_str, otherBstr, ignoreCase, ignoreDiacritics, false);

      }

      int __stdcall CompareTo(__in const BSTR otherBstr, __in bool ignoreCase, __in bool ignoreDiacritics, bool ignoreSymbols) throw()

      {

            return Compare(m_str, otherBstr, ignoreCase, ignoreDiacritics, ignoreSymbols);

      }

     

      unsigned long GetHashCode() throw()

      {

            return  LHashValOfName(::GetThreadLocale(), m_str);           

      }

      // added by e.n.

      static void __stdcall StringReverse(__in_opt const  BSTR s) throw()

      {

            //CComBSTR2 temp;

            //HRESULT  hr = temp.AssignBSTR(s); //create a copy

            UINT slen = SysStringLen(s), y = 0;

            while(y < slen--)

            {

                  WCHAR swp = s[slen];

                  s[slen] = sYes;

                  s[y++] = swp;

            }

      }

      void __stdcall Reverse() throw()

      {

            StringReverse(m_str);

      }

      GUID __stdcall ToGuid()

      {

            GUID retval = GUID_NULL;

            HRESULT hr = IIDFromString(m_str, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

     

      DATE __stdcall ToDate()

      {

            DATE retval = 0;

            HRESULT hr = ::VarDateFromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

      _Check_return_ STDMETHODIMP AssignDate(__in const DATE date) throw()

      {

            Empty();

            return ::VarBstrFromDate(date, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

      }

      _Check_return_ STDMETHODIMP AssignBSTR(__in const BSTR bstrSrc) throw()

      {

            HRESULT hr = S_OK;

#ifndef SysReAllocStringByteLen

            if (m_str != bstrSrc)

            {

                  Empty();

                  if (bstrSrc != NULL)              

                  {

                        m_str = ::SysAllocStringByteLen((PCSTR)bstrSrc, ::SysStringByteLen(bstrSrc)) ;

                        if (*this == NULL)

                             hr = E_OUTOFMEMORY;

                  }

                  else           

                        Empty();

           

            }

#else

            if (m_str != bstrSrc)

            {

                  if (bstrSrc == NULL)

                        Empty();

                  else

                  {

                        UINT ilen = ::SysStringByteLen(bstrSrc);

                        if (SysReAllocStringByteLen(&m_str, NULL,  ilen) == FALSE)

                             hr = E_OUTOFMEMORY;

                        if (ilen > 0) //fix since SysReAllocStringByteLen measures in lstrlenA

                             memcpy(m_str, bstrSrc, ilen);

                  }

            }

#endif

            return hr;

      }

      //creates a copy to a byteencoded string

      // optimal usage, attach to it...

      BSTR ToByteString(__in UINT codePage=CP_UTF8)

      {

            if (!IsEmpty())

            {

                  UINT len = Length();

                  UINT neededLen = len * 4;

                  BSTR pszA = SysAllocStringByteLen(NULL, neededLen);

                  int _convert = WideCharToMultiByte(codePage, 0, m_str, len, (PSTR)pszA, neededLen, NULL, NULL);

                  if (_convert == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)

                  {

                        _convert = WideCharToMultiByte(codePage, 0, m_str, len, NULL, 0, NULL, NULL);

                        _SetByteLength(&pszA, _convert);

                        _convert = WideCharToMultiByte(codePage, 0, m_str, len, (PSTR)pszA, _convert, NULL, NULL); 

                  }

                  if (_convert == 0 && GetLastError() > 0)

                        AtlThrowLastWin32();

                  _SetByteLength(&pszA, _convert);

                  return pszA;

            }

            else

                  return NULL;

      }

      //creates a copy to a wide-string

      // optimal usage, attach to it...

      BSTR ToWideString(__in UINT codePage=CP_UTF8) throw()

      {

            UINT len = ByteLength();

            int _convert = MultiByteToWideChar(codePage, 0, (PSTR) m_str, len, NULL, 0);

            BSTR pszW = SysAllocStringLen(NULL, _convert);

            if (pszW != NULL)

            {

                  MultiByteToWideChar(codePage, 0, (PSTR)m_str, len, pszW, _convert);

            }

            return pszW;

      }

      HRESULT ToLower() throw()

      {

            if (m_str != NULL)

            {

#ifdef _UNICODE

                  // Convert in place

                  CharLowerBuff(m_str, Length());

#else

                  // Cannot use conversion macros due to possible embedded NULLs

                  UINT _acp = _AtlGetConversionACP();

                  int _convert = WideCharToMultiByte(_acp, 0, m_str, Length(), NULL, 0, NULL, NULL);

                  CTempBuffer<char> pszA;

                  ATLTRY(pszA.Allocate(_convert));

                  if (pszA == NULL)

                        return E_OUTOFMEMORY;

                  int nRet = WideCharToMultiByte(_acp, 0, m_str, Length(), pszA, _convert, NULL, NULL);

                  if (nRet == 0)

                  {

                        ATLASSERT(0);

                        return AtlHresultFromLastError();

                  }

                  CharLowerBuff(pszA, nRet);

                  _convert = MultiByteToWideChar(_acp, 0, pszA, nRet, NULL, 0);

                  CTempBuffer<WCHAR> pszW;

                  ATLTRY(pszW.Allocate(_convert));

                  if (pszW == NULL)

                        return E_OUTOFMEMORY;

                  nRet = MultiByteToWideChar(_acp, 0, pszA, nRet, pszW, _convert);

                  if (nRet <= 0)

                  {

                        ATLASSERT(0);

                        return AtlHresultFromLastError();

                  }

                  UINT nBytes=0;   

                  HRESULT hr=S_OK;

                  if( FAILED(hr=::ATL::AtlMultiply(&nBytes, static_cast<UINT>(nRet), static_cast<UINT>(sizeof(OLECHAR)))))

                  {

                        return hr;

                  }

                  BSTR b = ::SysAllocStringByteLen((LPCSTR) (LPWSTR) pszW, nBytes);

                  if (b == NULL)

                        return E_OUTOFMEMORY;

                  SysFreeString(m_str);

                  m_str = b;

#endif

            }

            return S_OK;

      }

      HRESULT __stdcall ToUpper() throw()

      {

            if (m_str != NULL)

            {

#ifdef _UNICODE

                  // Convert in place

                  CharUpperBuff(m_str, Length());

#else

            // Cannot use conversion macros due to possible embedded NULLs

                  UINT _acp = _AtlGetConversionACP();

                  int _convert = WideCharToMultiByte(_acp, 0, m_str, Length(), NULL, 0, NULL, NULL);

                  CTempBuffer<char> pszA;

                  ATLTRY(pszA.Allocate(_convert));

                  if (pszA == NULL)

                        return E_OUTOFMEMORY;

                  int nRet = WideCharToMultiByte(_acp, 0, m_str, Length(), pszA, _convert, NULL, NULL);

                  if (nRet == 0)

                  {

                        ATLASSERT(0);

                        return AtlHresultFromLastError();

                  }

                  CharUpperBuff(pszA, nRet);

                  _convert = MultiByteToWideChar(_acp, 0, pszA, nRet, NULL, 0);

                  CTempBuffer<WCHAR> pszW;

                  ATLTRY(pszW.Allocate(_convert));

                  if (pszW == NULL)

                        return E_OUTOFMEMORY;

                  nRet = MultiByteToWideChar(_acp, 0, pszA, nRet, pszW, _convert);

                  if (nRet <= 0)

                  {

                        ATLASSERT(0);

                        return AtlHresultFromLastError();

                  }

                  UINT nBytes=0;

                  HRESULT hr=S_OK;

                  if( FAILED(hr=::ATL::AtlMultiply(&nBytes, static_cast<UINT>(nRet), static_cast<UINT>(sizeof(OLECHAR)))))

                  {          

                        return hr;

                  }

                  BSTR b = ::SysAllocStringByteLen((LPCSTR) (LPWSTR) pszW, nBytes);

                  if (b == NULL)

                        return E_OUTOFMEMORY;

                  SysFreeString(m_str);

                  m_str = b;

#endif

            }

            return S_OK;

      }

      bool __cdecl LoadNString(__in UINT uID,  ...) throw()

      {

            va_list args;

            va_start(args, uID);

            bool res = _LoadNString(uID, _AtlBaseModule.GetResourceInstance(), args);

            va_end(args);

            return res;

      }

      bool __cdecl LoadNString2(__in UINT uID, __in HINSTANCE hInst, ...) throw()

      {

            va_list args;

            va_start(args, hInst);

            bool res = _LoadNString(uID, hInst, args);

            va_end(args);

            return res;

      }

private:

      bool __cdecl _LoadNString(__in UINT uID, __in HINSTANCE hInst, __in va_list args) throw()

      {

           

            bool result = true;               

            PWSTR myMessage = NULL;

            LANGID langid = LANGIDFROMLCID(::GetThreadLocale());

            LANGID id= PRIMARYLANGID(langid);

            int trycnt = 0;

            UINT bufSize =0;

            DWORD dwerr;

            ::SetLastError(ERROR_SUCCESS); //fix

            do

            {

                  trycnt++;

                  bufSize = ::FormatMessageW(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER,

                        hInst, uID, id, (PWSTR) &myMessage, 0, &args);

                  id= SUBLANGID(langid);

                   dwerr = ::GetLastError();

            } while (dwerr == ERROR_RESOURCE_LANG_NOT_FOUND && trycnt == 1);

            if (dwerr != ERROR_SUCCESS)

            {

                  AtlTrace(L"_LoadNString %d", dwerr);

                  result = false;

            }

            else

            {

                  Empty();

                  if (bufSize > 0) bufSize -= 2;

                  m_str = ::SysAllocStringLen(myMessage, bufSize);

            }

            if (myMessage != NULL)

                  ::GlobalFree(myMessage);

     

            return result;

   }

public:

      bool __stdcall LoadString(__in HINSTANCE hInst, __in UINT nID) throw()

      {

            Empty();

            return LoadStringResource(hInst, nID, m_str);

      }

      bool __stdcall LoadString(__in UINT nID) throw()

      {

            Empty();

            return LoadStringResource(nID, m_str);

      }

      CComBSTR2& __stdcall operator+=(__in const CComBSTR2& bstrSrc)

      {

            HRESULT hr;

            hr = AppendBSTR(bstrSrc.m_str);

            if (FAILED(hr))

                  AtlThrow(hr);

            return *this;

      }

      CComBSTR2& __stdcall operator+=(__in const VARIANT& pVar)

      {

            HRESULT hr = Append(pVar);

            if (FAILED(hr))

                  AtlThrow(hr);

            return *this;

      }

      CComBSTR2& __stdcall operator+=(__in_opt LPCOLESTR pszSrc)

      {

            HRESULT hr = Append(pszSrc);

            if (FAILED(hr))

                  AtlThrow(hr);

            return *this;

      }

     

      bool __stdcall operator<(__in const CComBSTR2& bstrSrc) const throw()

      {

            return Compare(m_str, bstrSrc.m_str, true, true, true) == -1;

      }

      bool __stdcall operator<(LPCOLESTR pszSrc) const

      {

            CComBSTR2 bstr2(pszSrc);

            return operator<(bstr2);

      }

      bool __stdcall operator<(__in_opt LPOLESTR pszSrc) const throw()

      {

            return operator<((LPCOLESTR)pszSrc);

      }

      bool __stdcall operator>(__in const CComBSTR2& bstrSrc) const throw()

      {

            return Compare(m_str, bstrSrc.m_str, true, true, true) == -1;

      }

      bool operator>(__in_opt LPCOLESTR pszSrc) const

      {

            CComBSTR2 bstr2(pszSrc);

            return operator>(bstr2);

      }

      bool __stdcall operator>(__in_opt LPOLESTR pszSrc) const throw()

      {

            return operator>((LPCOLESTR)pszSrc);

      }

     

      bool __stdcall operator!=(__in const CComBSTR2& bstrSrc) const throw()

      {

            return !operator==(bstrSrc);

      }

      bool __stdcall operator!=(__in_opt LPCOLESTR pszSrc) const throw()

      {

            return !operator==(pszSrc);

      }

      bool operator!=(__in int nNull) const throw()

      {

            return !operator==(nNull);

      }

      bool __stdcall operator!=(__in_opt  LPOLESTR pszSrc) const throw()

      {

            return operator!=((LPCOLESTR)pszSrc);

      }

      bool __stdcall operator==(__in const CComBSTR2& bstrSrc) const throw()

      {

            return (Compare(m_str, bstrSrc.m_str, true, true, true) == 0);

      }

      bool __stdcall operator==(__in_opt LPCOLESTR pszSrc) const throw()

      {

            CComBSTR2 bstr2(pszSrc);

            return operator==(bstr2);

      }

      bool __stdcall operator==(__in_opt LPOLESTR pszSrc) const throw()

      {

            return operator==((LPCOLESTR)pszSrc);

      }

     

      bool __stdcall operator==(__in int nNull) const throw()

      {

            ATLASSERT(nNull == NULL);

            (void)nNull;

            return (m_str == NULL);

      }

      CComBSTR2(_In_opt_ LPCSTR pSrc)

      {

            if (pSrc != NULL)

            {

                  m_str = A2WBSTR(pSrc);

                  if (m_str == NULL)

                        AtlThrow(E_OUTOFMEMORY);

            }

            else

                  m_str = NULL;

      }

      CComBSTR2(_In_ int nSize, _In_opt_count_(nSize) LPCSTR sz)

      {

            if (nSize != 0 && sz == NULL)

            {

                  HRESULT hr = SetLength(nSize);

                  if (FAILED(hr))

                        AtlThrow(hr);

            }

            else

            {

                  m_str = A2WBSTR(sz, nSize);

                  if (m_str == NULL && nSize != 0)

                        AtlThrow(E_OUTOFMEMORY);

            }

      }

      HRESULT __stdcall Append(_In_opt_ LPCSTR lpsz) throw()

      {

            if (lpsz == NULL)

                  return S_OK;

            CComBSTR2 bstrTemp;

            ATLTRY(bstrTemp = lpsz);

            if (bstrTemp.m_str == NULL)

                  return E_OUTOFMEMORY;

            return Append(bstrTemp);

      }

     

     

      CComBSTR2& __stdcall operator=(__in ULONG ulong)

      {

            Empty();

            HRESULT hr = ::VarBstrFromUI4(ulong, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }

      // Define assignment operator.

      //A& A::operator=( const A& );

      ULONG __stdcall ToULong()

      {

            ULONG retval = 0;

            HRESULT hr = ::VarUI4FromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

      LONG __stdcall ToLong()

      {

            LONG retval = 0;

            HRESULT hr = ::VarI4FromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

      HRESULT __stdcall AssignWordHex(__in WORD pVal, __in bool fixedLen = true) throw()

      {

            PCWSTR fmt =  fixedLen ? L"%04x" : L"%x";

            return Format(fmt, pVal);

      }

      HRESULT __stdcall AssignLongHex(__in LONG pVal, __in bool fixedLen = true) throw()

      {

            PCWSTR fmt = fixedLen ?      L"%08x" : L"%x";

            return Format(fmt, pVal) ;        

      }

      CComBSTR2& __stdcall operator=(__in LONG pSrc)

      {

            Empty();

            HRESULT hr = ::VarBstrFromI4(pSrc, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }

      CComBSTR2& __stdcall operator=(__in USHORT pSrc)

      {

            Empty();

            HRESULT hr = ::VarBstrFromUI2(pSrc, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }

      SHORT __stdcall ToShort()

      {

            SHORT retval = 0;

            HRESULT hr = ::VarI2FromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

      CComBSTR2& __stdcall operator=(__in SHORT pSrc)

      {

            Empty();

            HRESULT hr = ::VarBstrFromI2(pSrc, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }

      BYTE __stdcall ToByte()

      {

            BYTE retval = 0;

            HRESULT hr = ::VarUI1FromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

      CComBSTR2& __stdcall operator=(__in BYTE pSrc)

      {

            Empty();

            HRESULT hr = ::VarBstrFromUI1(pSrc, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }

#if (_WIN32_WINNT >= 0x0501) || defined(_ATL_SUPPORT_VT_I8)

      CComBSTR2& operator =(LONG64 pSrc)

      {

            Empty();

            HRESULT hr = ::VarBstrFromI8(pSrc, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }

      LONG64 ToVlong()

      {

            LONG64 retval = 0;

            HRESULT hr = ::VarI8FromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr)) AtlThrow(hr);

            return retval;

      }

#endif

      FLOAT __stdcall ToFloat()

      {

            FLOAT retval = 0;

            HRESULT hr = ::VarR4FromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

      CComBSTR2& __stdcall operator=(__in FLOAT pSrc)

      {

            Empty();

            HRESULT hr = ::VarBstrFromR4(pSrc, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }

      DOUBLE __stdcall ToDouble()

      {

            DOUBLE retval = 0;

            HRESULT hr = ::VarR8FromStr(m_str, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &retval);

            if (FAILED(hr))

                  AtlThrow(hr);

            return retval;

      }

      CComBSTR2& __stdcall operator=(__in DOUBLE pSrc)

      {

            Empty();

            HRESULT hr = ::VarBstrFromR8(pSrc, ::GetThreadLocale(), LOCALE_NOUSEROVERRIDE, &m_str);

            if (FAILED(hr)) AtlThrow(hr);

            return *this;

      }    

      CComBSTR2& __stdcall operator=(__in_opt LPCSTR pSrc)

      {

            Empty();

            m_str = A2WBSTR(pSrc);

            if (m_str == NULL && pSrc != NULL)

                  AtlThrow(E_OUTOFMEMORY);

            return *this;

      }

      bool __stdcall operator<(__in_opt LPCSTR pszSrc) const

      {

            CComBSTR2 bstr2(pszSrc);

            return operator<(bstr2);

      }

      bool __stdcall operator>(__in_opt LPCSTR pszSrc) const

      {

            CComBSTR2 bstr2(pszSrc);

            return operator>(bstr2);

      }

      bool __stdcall operator!=(__in_opt LPCSTR pszSrc) const

      {

            return !operator==(pszSrc);

      }

      bool __stdcall operator==(__in_opt LPCSTR pszSrc) const

      {

            CComBSTR2 bstr2(pszSrc);

            return operator==(bstr2);

      }

      _Check_return_ STDMETHODIMP WriteToStream(_Inout_ IStream* pStream) throw()

      {

            ATLASSERT(pStream != NULL);

            if(pStream == NULL)

                  return E_INVALIDARG;

                 

            ULONG cb;

            ULONG cbStrLen = CComBSTR::GetStreamSize(m_str) - sizeof(ULONG);

            HRESULT hr = pStream->Write((void*) &cbStrLen, sizeof(cbStrLen), &cb);

            if (FAILED(hr))

                  return hr;

            return cbStrLen ? pStream->Write((void*) m_str, cbStrLen, &cb) : S_OK;

      }

      _Check_return_ STDMETHODIMP ReadFromStream(_Inout_ IStream* pStream) throw()

      {

            ATLASSERT(pStream != NULL);

            if(pStream == NULL)

            {

                  return E_INVALIDARG;

            }

                 

            ATLASSERT(!*this); // should be empty

            Empty();

           

            HRESULT hrSeek;

            ULARGE_INTEGER nBegOffset;

            {

                  LARGE_INTEGER nZeroOffset;

                  nZeroOffset.QuadPart = 0L;

                  hrSeek = pStream->Seek(nZeroOffset, STREAM_SEEK_CUR, &nBegOffset);

            }

            ULONG cbRead = 0;

            ULONG cbStrLen = 0;

            HRESULT hr = pStream->Read(reinterpret_cast<void*>(&cbStrLen), sizeof(cbStrLen), &cbRead);

            if (SUCCEEDED(hr))

            {

                  // invalid data size

                  if (sizeof(cbStrLen) != cbRead)

                  {

                        ATLTRACE(atlTraceCOM, 0, _T("Input stream is corrupted."));

                        hr = E_FAIL;

                  }

                  // read NULL string

                  else if (cbStrLen == 0)

                  {                      

                  }

                  // invalid data length 

                  else if (cbStrLen < sizeof(OLECHAR))

                  {

                        ATLTRACE(atlTraceCOM, 0, _T("Input stream is corrupted."));

                        hr = E_FAIL;

                  }

                  // security checks when system hang for huge stream of data

                  else if (cbStrLen > _ATL_STREAM_MAX_SIZE)

                  {

                        ATLTRACE(atlTraceCOM, 0, _T("String exceeded the maximum allowed size see _ATL_STREAM_MAX_SIZE."));

                        hr = E_ACCESSDENIED;

                  }

                  else

                  {

                        //subtract size for terminating NULL which we wrote out

                        cbStrLen -= sizeof(OLECHAR);

                        m_str = ::SysAllocStringByteLen(NULL, cbStrLen);

                        if (!*this)

                        {

                             hr = E_OUTOFMEMORY;

                        }

                        else

                        {

                             hr = pStream->Read(reinterpret_cast<void*>(m_str), cbStrLen, &cbRead);

                             if (SUCCEEDED(hr))

                             {

                                   if (cbRead != cbStrLen)

                                   {

                                         ATLTRACE(atlTraceCOM, 0, _T("Length of string data is different than expected."));

                                         hr = E_FAIL;

                                   }

                                   else

                                   {

                                         OLECHAR ch;

                                         hr = pStream->Read(reinterpret_cast<void*>(&ch), sizeof(OLECHAR), &cbRead);  

                                         if (SUCCEEDED(hr))

                                         {                                             

                                               if (cbRead != sizeof(OLECHAR))

                                               {

                                                     ATLTRACE(atlTraceCOM, 0, _T("Cannot read NULL terminator from stream."));

                                                     hr = E_FAIL;                                                   

                                               }

                                               else

                                               {

                                                     //check if string is properly terminated with NULL

                                                     ATLASSERT(ch == L'\0');

                                               }

                                         }

                                   }

                             }                

                                  

                             if (FAILED(hr))

                             {

                                   ::SysFreeString(m_str);

                                   m_str = NULL;

                             }

                        }

                  }

            }

           

            // If SysAllocStringByteLen or IStream::Read failed, reset seek

            // pointer to start of BSTR size.

            if (FAILED(hr) && SUCCEEDED(hrSeek))

            {

                  LARGE_INTEGER nOffset;

                  nOffset.QuadPart = static_cast<LONGLONG>(nBegOffset.QuadPart);

                  pStream->Seek(nOffset, STREAM_SEEK_SET, NULL);                  

            }

     

            return hr;

      }

      static bool __stdcall LoadStringResource(_In_ HINSTANCE hInstance, _In_ UINT uID, _Inout_ _Deref_post_opt_valid_ BSTR& bstrText) throw()

      {    

            const ATLSTRINGRESOURCEIMAGE* pImage;

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "The semantics of this function are about allocation, not content")

            ATLASSERT(bstrText == NULL);

#pragma prefast(pop)

#pragma warning(pop)

            pImage = AtlGetStringResourceImage(hInstance, uID);

            if (pImage != NULL)

            {

                  bstrText = ::SysAllocStringLen(pImage->achString, pImage->nLength);

            }

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "The semantics of this function are about allocation, not content")

            return (bstrText != NULL) ? true : false;

#pragma prefast(pop)

#pragma warning(pop)

      }

      static bool __stdcall LoadStringResource(_In_ UINT uID, _Inout_ _Deref_post_opt_valid_ BSTR& bstrText) throw()

      {

            const ATLSTRINGRESOURCEIMAGE* pImage;

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "The semantics of this function are about allocation, not content")

            ATLASSERT(bstrText == NULL);

#pragma prefast(pop)

#pragma warning(pop)

            pImage = AtlGetStringResourceImage(uID);

            if (pImage != NULL)

            {

                  bstrText = ::SysAllocStringLen(pImage->achString, pImage->nLength);

            }

#pragma warning(push)

#pragma warning(disable:4068)

#pragma prefast(push)

#pragma prefast(disable:325, "The semantics of this function are about allocation, not content")

            return (bstrText != NULL) ? true : false;

#pragma prefast(pop)

#pragma warning(pop)

      }

      // each character in BSTR is copied to each element in SAFEARRAY

      _Success_(SUCCEEDED(return)) HRESULT BSTRToArray(_Deref_out_ LPSAFEARRAY *ppArray) throw()

      {

            return VectorFromBstr(m_str, ppArray);

      }

      // first character of each element in SAFEARRAY is copied to BSTR

      _Check_return_ HRESULT ArrayToBSTR(_In_ const SAFEARRAY *pSrc) throw()

      {

            ::SysFreeString(m_str);

            return BstrFromVector((LPSAFEARRAY)pSrc, &m_str);

      }

};

//bstr alternative avoiding any BSTR cache 

#define MAXBSTRLEN unsigned int(~0)

MAXBSTRLEN unsigned int(~0)

#define m_align 16L

m_align 16L

#define MEMALIGN_16(cb) cb % m_align != 0 ? ((cb / m_align) + 1) * m_align : cb

MEMALIGN_16(cb) cb % m_align != 0 ? ((cb / m_align) + 1) * m_align : cb

// compile this with option 'not using precompiled headers'

#include "bstrnocache.h"

// see license in bstrnocache.h

void __stdcall FreeString(BSTR * theString) throw()

{

      if (theString != NULL && *theString != NULL )

      {

            SysFreeString(*theString);

            *theString = NULL;

      }

}

int __stdcall SysReAllocStringByteLen2(BSTR * bstr, PCSTR input, UINT cch) throw()

{

//    ATLASSERT(bstr != NULL);

      UINT_PTR copyIntoLen = input != NULL ?  strlen(input): 0;

      if (copyIntoLen > cch)

            copyIntoLen = cch;

      UINT_PTR newlen = cch + sizeof(OLECHAR) + sizeof(UINT_PTR);

      if (newlen > MAXBSTRLEN)

            return FALSE;    

      PSTR temp = (PSTR)::CoTaskMemRealloc(

                                                     *bstr == NULL ? NULL : (PSTR)(*bstr) - sizeof(UINT_PTR)

                                               , MEMALIGN_16(newlen));

      if (temp != NULL)

      {

            UINT_PTR* plen = (UINT_PTR*)temp;

            *bstr = (BSTR)&plen[1];

            UINT* plen2 = (UINT*)temp;

            plen2[sizePtrCorrection] = cch ; //asign bytelength for BSTR

            if (copyIntoLen > 0)    //copy values

            #if _MSC_VER >= 1310

                  strncpy_s((PSTR)*bstr, cch + 1, input, copyIntoLen);

            #else

                  strncpy((PSTR)*bstr, input, copyIntoLen);

            #endif

            temp[sizeof(UINT_PTR) + cch ] = 0; //terminate LPWSTR with a wide_string

            temp[sizeof(UINT_PTR) + cch + 1] = 0;         

      }

     

      return *bstr == NULL ? FALSE : TRUE;

}

int __stdcall SysReAllocStringLen(BSTR * bstr, const OLECHAR* input, UINT cch) throw()

{

      //ATLASSERT(bstr != NULL);

      UINT_PTR copyIntoLen = input != NULL ?  wcslen(input): 0;

      if (copyIntoLen > cch)

            copyIntoLen = cch;

      UINT_PTR newlen = cch * sizeof(OLECHAR) + sizeof(OLECHAR) + sizeof(UINT_PTR);

      if (newlen > MAXBSTRLEN)

            return FALSE;

     

      PWSTR temp = (PWSTR)::CoTaskMemRealloc(*bstr == NULL ? NULL : *bstr - sizeof(UINT_PTR) / sizeof(OLECHAR),

                                   MEMALIGN_16(newlen));  

      if (temp != NULL)

      {

            UINT_PTR* plen = (UINT_PTR*)temp;

            *bstr = (BSTR)&plen[1];

            UINT* plen2 = (UINT*)temp;

            plen2[sizePtrCorrection] = cch * sizeof(OLECHAR); //asign bytelength for BSTR

            if (copyIntoLen > 0)    //copy values

            #if _MSC_VER >= 1310

                  wcsncpy_s(*bstr, cch + 1, input, copyIntoLen );

            #else

                  wcsncpy(*bstr, input, copyIntoLen );

            #endif

            temp[sizeof(UINT_PTR) / sizeof(OLECHAR) + cch ] = 0; //terminate LPWSTR

      }

      return *bstr == NULL ? FALSE : TRUE;

}

///<summary>

///

///</summary>

BSTR __stdcall SysAllocStringLen(const OLECHAR * input, UINT cch)  throw()

{

      UINT_PTR copyIntoLen = input != NULL ?  wcslen(input) : 0;

      if (copyIntoLen > cch)

            copyIntoLen = cch;

      BSTR retval = NULL;

      UINT_PTR newlen = cch * sizeof(OLECHAR) + sizeof(OLECHAR) + sizeof(UINT_PTR);

      if (newlen > MAXBSTRLEN)

            return NULL;

      PWSTR temp = (PWSTR)::CoTaskMemAlloc(MEMALIGN_16(newlen));

      if (temp != NULL)

      {

            UINT_PTR* plen = (UINT_PTR*)temp;

            retval = (BSTR)&plen[1];

            UINT* plen2 = (UINT*)temp;        

            plen2[0] =0;

            plen2[sizePtrCorrection] = cch * sizeof(OLECHAR);

            if (copyIntoLen > 0)   

            #if _MSC_VER >= 1310

                  wcsncpy_s(retval, cch + 1, input, copyIntoLen);

            #else

                  wcsncpy(retval, input, copyIntoLen);

            #endif

            temp[sizeof(UINT_PTR) / sizeof(OLECHAR) + cch ] = 0;      

      }

      return retval;

}

///<summary>

///

///</summary>

BSTR __stdcall SysAllocStringByteLen2(PCSTR input, UINT cch)  throw()

{

      UINT_PTR copyIntoLen = input != NULL ?  strlen(input) : 0;

      if (copyIntoLen > cch)

            copyIntoLen = cch;

      BSTR retval = NULL;

      UINT_PTR newlen = cch + sizeof(OLECHAR) + sizeof(UINT_PTR);

      if (newlen > MAXBSTRLEN)

            return NULL;

      PWSTR temp = (PWSTR)::CoTaskMemAlloc(MEMALIGN_16(newlen));

      if (temp != NULL)

      {

            UINT_PTR* plen = (UINT_PTR*)temp;

            retval = (BSTR)&plen[1];

            UINT* plen2 = (UINT*)temp;        

            plen2[sizePtrCorrection] = cch;         

            if (copyIntoLen > 0)   

            #if _MSC_VER >= 1310

                  strncpy_s((PSTR)retval, cch + 1, input, copyIntoLen);

            #else

                  strncpy((PSTR)retval, input, copyIntoLen);

            #endif

            PSTR zeroit = (PSTR)temp;

            zeroit[sizeof(UINT_PTR) + cch ] = 0;     //terminate LPWSTR with a wide_string

            zeroit[sizeof(UINT_PTR) + cch + 1] = 0;       

      }

      return retval;

}

///<summary>

/// Identical to SysAllocString

///</summary>

BSTR __stdcall SysAllocString(const OLECHAR * input)  throw()

{

      BSTR retval = NULL;

      if (input != NULL)

      {          

            UINT_PTR slen = wcslen(input);

            UINT_PTR newlen = slen * sizeof(OLECHAR)+ sizeof(UINT_PTR) + sizeof(OLECHAR);

            if (newlen > MAXBSTRLEN)

                  return NULL;

            PWSTR temp = NULL;          

            temp = (PWSTR)::CoTaskMemAlloc(MEMALIGN_16(newlen));

            if (temp != NULL)

            {

                  UINT_PTR* plen = (UINT_PTR*)temp;

                  retval = (BSTR)&plen[1];

                  UINT* plen2 = (UINT*)temp;

                  plen2[sizePtrCorrection] = (UINT)slen * sizeof(OLECHAR);

                  if (slen > 0)

                  #if _MSC_VER >= 1310

                        wcsncpy_s(retval, slen + 1, input, slen);

                  #else

                        wcsncpy(retval, input, slen);

                  #endif

            }

      }    

      return retval;

}

int __stdcall SysReAllocString(BSTR * bstr, const OLECHAR * input) throw()

{

      //ATLASSERT(bstr != NULL);

      bool didEmpty = FALSE;

      if (input == NULL)

      {

            FreeString(bstr);

            didEmpty = true;

      }

      else

      {

            UINT_PTR slen = wcslen(input);

            UINT_PTR newlen = slen * sizeof(OLECHAR) + sizeof(OLECHAR) + sizeof(UINT_PTR);

            if (newlen > MAXBSTRLEN)

                  return FALSE;

            //get real address

            BSTR temp = (BSTR)::CoTaskMemRealloc(   

                                               *bstr == NULL ? NULL : *bstr - sizeof(INT_PTR) / sizeof(OLECHAR),

                                               MEMALIGN_16(newlen));

            if (temp != NULL)

            {

                  UINT_PTR* plen = (UINT_PTR*)temp;

                  *bstr = (BSTR)&plen[1];

                  UINT* plen2 = (UINT*)temp;

                  plen2[sizePtrCorrection] = (UINT)slen * sizeof(OLECHAR);                    

                  #if _MSC_VER >= 1310

                        wcsncpy_s(*bstr, slen + 1, input, slen);

                  #else

                        wcsncpy(*bstr, input, slen);

                  #endif

            }

      }

      return *bstr == NULL && didEmpty == false? FALSE : TRUE;

}

HRESULT __stdcall AllocPSTR(PSTR *lpMulti, SIZE_T lSize, SIZE_T *oldSize) throw()

{

//    ATLASSERT(lpMulti != NULL && oldSize != NULL);

      HRESULT hr = S_OK;

      if (*oldSize < lSize)

      {

            PSTR temp = (PSTR)::CoTaskMemRealloc(*lpMulti, lSize);//HeapReAlloc(h, 0, *lpMulti, lSize);

            if (temp == NULL)      

            {    

                  ::CoTaskMemFree(*lpMulti);//(h, 0, *lpMulti);

                  *lpMulti = NULL;

                  *oldSize = 0;

                  hr =E_OUTOFMEMORY;

            }

            else

            {

                  *lpMulti = temp;//successfull realloc

                  *oldSize = lSize;

            }

      }

      return hr;

}

UINT __stdcall SysStringByteLen2(BSTR theString) throw()

{

      //UINT bogus = *(((UINT*)theString) -2 );

      return theString != NULL ? *(((UINT*)theString) -1 ) : 0;

}

UINT __stdcall SysStringLen2(BSTR theString) throw()

{

      //UINT bogus = *(((UINT*)theString) -2 );

      return theString != NULL ? (*(((UINT*)theString) -1 )) / sizeof(OLECHAR) : 0;

}

void __stdcall SysFreeString2(BSTR theString) throw()

{

      //ATLASSERT(theString != NULL);

      if (theString != NULL)

      {

            UINT_PTR* temp = (UINT_PTR*)theString;

            temp--;

            CoTaskMemFree(temp);

      }

}