Egbert Nierop (MVP for IIS) said:
Hi,
I'm 'improving' CComBSTR (yes, I do still program unmanaged code in
addition to C# ) to contain features, not found in it.
Does anybody have good code which maches LastIndexOf()?
Ok, ok, I did it myself, however it could be improved a little, still...
These improvements also has been optimized -not- to reallocate the string so
often as the original does with Append
/////////////////////////////////////////////////////////////////////////////
// CComBSTR
class CComBSTR
{
public:
BSTR m_str;
CComBSTR() throw()
{
m_str = NULL;
}
CComBSTR(int nSize)
{
//if (nSize == 0) //BUG it should be possible to assign a L"" string
// m_str = NULL;
m_str = ::SysAllocStringLen(NULL, nSize);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
CComBSTR(int nSize, LPCOLESTR sz)
{
if (nSize == 0)
m_str = NULL;
else
{
m_str = ::SysAllocStringLen(sz, nSize);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
}
CComBSTR(LPCOLESTR pSrc)
{
if (pSrc == NULL)
m_str = NULL;
else
{
m_str = ::SysAllocString(pSrc);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
}
CComBSTR(const CComBSTR& src)
{
m_str = src.Copy();
if (!!src && m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
CComBSTR(REFGUID guid)
{
OLECHAR szGUID[64];
::StringFromGUID2(guid, szGUID, 64);
m_str = ::SysAllocString(szGUID);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
CComBSTR& operator=(const CComBSTR& src)
{
if (m_str != src.m_str)
{
if
:SysReAllocStringLen(&m_str, src, src.Length()) == FALSE)
AtlThrow(E_OUTOFMEMORY);
}
return *this;
}
CComBSTR& operator=(LPCOLESTR pSrc)
{
if (pSrc != m_str)
{
if (pSrc != NULL)
{
if
:SysReAllocString(&m_str, pSrc) == FALSE)
AtlThrow(E_OUTOFMEMORY);
}
else
{
::SysFreeString(m_str);
m_str = NULL;
}
}
return *this;
}
~CComBSTR() throw()
{
::SysFreeString(m_str);
}
unsigned int Length() const throw()
{
return (m_str == NULL)? 0 : SysStringLen(m_str);
}
unsigned int ByteLength() const throw()
{
return (m_str == NULL)? 0 : SysStringByteLen(m_str);
}
operator BSTR() const throw()
{
return m_str;
}
BSTR* operator&() throw()
{
return &m_str;
}
BSTR Copy() const throw()
{
if (m_str == NULL)
return NULL;
return ::SysAllocStringByteLen((char*)m_str, ::SysStringByteLen(m_str));
}
HRESULT CopyTo(BSTR* pbstr) throw()
{
ATLASSERT(pbstr != NULL);
if (pbstr == NULL)
return E_POINTER;
*pbstr = Copy();
if ((*pbstr == NULL) && (m_str != NULL))
return E_OUTOFMEMORY;
return S_OK;
}
/* added by may 2005 e.n. needs #include 'wchar.h'*/
HRESULT __cdecl Format(PCWSTR pszFormat, ...) throw()
{
va_list args;
HRESULT hr = S_OK;
va_start( args, pszFormat );
int len = _vscwprintf( pszFormat, args );
if
:SysReAllocStringLen(&m_str, NULL, len) == FALSE)
hr = E_OUTOFMEMORY;
else
vswprintf( m_str, (SIZE_T)Length(), pszFormat, args );
va_end(args);
return hr;
}
BSTR Substring(const int startIndex) throw()
{
int maxIdx = Length();
if (m_str != NULL && startIndex >= 0 && startIndex <= maxIdx)
{
return ::SysAllocStringLen(m_str + startIndex, maxIdx - startIndex);
}
else
return NULL;
}
BSTR Substring(const int startIndex, int length) throw()
{
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;
}
int LastIndexOf(const wchar_t *src, const unsigned int startIndex = 0,
const bool caseInsensitive = false) throw()
{
// use _wcsnicmp and wcsncmp
int result = -1;
if (m_str != NULL && src != NULL)
{
int compLen = ocslen(src);
int maxLen = Length();
for(int x = maxLen - compLen; x >= 0; x--)
{
if (
(caseInsensitive ? _wcsnicmp(m_str + x, src, compLen) : wcsncmp(m_str
+ x, src, compLen)
) == 0)
{
result = x;
break;
}
}
}
return result;
}
int IndexOf(const wchar_t src, const unsigned int startIndex = 0, const
bool caseInsensitive = false) throw()
{
int result = -1;
if (m_str != NULL)
{
unsigned int maxLen = Length();
unsigned int startIdx = startIndex > maxLen ? maxLen : startIndex;
unsigned int maxScan = maxLen - startIndex;
BSTR temp;
wchar_t src2;
if (caseInsensitive)
{
wchar_t tol[2] = {src, 0};
_wcslwr(tol);
src2 = tol[0];
temp = _wcslwr(Substring((UINT)startIdx));
}
else
{
temp = m_str;
src2 = src;
}
wchar_t *found = wmemchr(temp, src2, maxScan);
if (found != NULL)
result = PtrToInt(found - temp);
if (caseInsensitive)
::SysFreeString(temp);
}
return result;
}
/*
* Addded by E.N.
*/
int IndexOf(const PWSTR src, const unsigned int startIndex = 0, const bool
caseInsensitive = false) throw()
{
int result = -1;
if (src != NULL && m_str != NULL)
{
unsigned int maxLen = Length();
unsigned int startIdx = startIndex > maxLen ? maxLen : startIndex;
BSTR temp, findThis;
if (caseInsensitive)
{
temp = _wcslwr(Substring(startIdx));
findThis = _wcslwr
:SysAllocString(src));
}
else
{
temp = m_str + startIdx;
findThis = src;
}
wchar_t *found = wcsstr(temp, findThis);
if (found != NULL)
result = PtrToInt(found - temp);
if (caseInsensitive)
{
::SysFreeString(temp);
::SysFreeString(findThis);
}
}
return result;
}
// copy BSTR to VARIANT
HRESULT CopyTo(VARIANT *pvarDest) throw()
{
ATLASSERT(pvarDest != NULL);
HRESULT hRes = E_POINTER;
if (pvarDest != NULL)
{
pvarDest->vt = VT_BSTR;
pvarDest->bstrVal = Copy();
if (pvarDest->bstrVal == NULL && m_str != NULL)
hRes = E_OUTOFMEMORY;
else
hRes = S_OK;
}
return hRes;
}
void Attach(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 operator!() const throw()
{
return (m_str == NULL);
}
HRESULT Append(const CComBSTR& bstrSrc) throw()
{
return AppendBSTR(bstrSrc.m_str);
}
HRESULT Append(LPCOLESTR lpsz) throw()
{
return Append(lpsz, UINT(ocslen(lpsz)));
}
// a BSTR is just a LPCOLESTR so we need a special version to signify
// that we are appending a BSTR
HRESULT AppendBSTR(BSTR p) throw()
{
return Append((LPCOLESTR)p, ::SysStringLen(p));
}
HRESULT Append(LPCOLESTR lpsz, int nLen) throw()
{
if (lpsz == NULL || (m_str != NULL && nLen == 0))
return S_OK;
int n1 = Length();
if ( ::SysReAllocStringLen(&m_str, NULL, n1+nLen) == FALSE)
return E_OUTOFMEMORY;
memcpy(m_str+n1, lpsz, nLen*sizeof(OLECHAR));
return S_OK;
}
HRESULT Append(char ch) throw()
{
OLECHAR chO = ch;
return( Append( &chO, 1 ) );
}
HRESULT Append(wchar_t ch) throw()
{
return( Append( &ch, 1 ) );
}
HRESULT AppendBytes(const char* lpsz, 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(b, m_str, n1);
memcpy(((char*)b)+n1, lpsz, nLen);
*((OLECHAR*)(((char*)b)+n1+nLen)) = NULL;
SysFreeString(m_str);
m_str = b;
return S_OK;
}
HRESULT AssignBSTR(const BSTR bstrSrc) throw()
{
HRESULT hr = S_OK;
if (m_str != bstrSrc)
{
if (bstrSrc != NULL)
{
if
:SysReAllocStringLen(&m_str, bstrSrc, ::SysStringLen(bstrSrc)) ==
FALSE)
hr = E_OUTOFMEMORY;
}
else
m_str = NULL;
}
return hr;
}
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();
}
if
:SysReAllocStringLen(&m_str, pszW, nRet) == FALSE)
return E_OUTOFMEMORY;
#endif
}
return S_OK;
}
HRESULT 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();
}
if
:SysReAllocStringLen(&m_str, pszW, nRet) == FALSE);
return E_OUTOFMEMORY;
#endif
}
return S_OK;
}
bool LoadString(HINSTANCE hInst, UINT nID) throw()
{
::SysFreeString(m_str);
m_str = NULL;
return LoadStringResource(hInst, nID, m_str);
}
bool LoadString(UINT nID) throw()
{
::SysFreeString(m_str);
m_str = NULL;
return LoadStringResource(nID, m_str);
}
CComBSTR& operator+=(const CComBSTR& bstrSrc)
{
HRESULT hr;
hr = AppendBSTR(bstrSrc.m_str);
if (FAILED(hr))
AtlThrow(hr);
return *this;
}
CComBSTR& operator+=(LPCOLESTR pszSrc)
{
HRESULT hr;
hr = Append(pszSrc);
if (FAILED(hr))
AtlThrow(hr);
return *this;
}
bool operator<(const CComBSTR& bstrSrc) const throw()
{
return VarBstrCmp(m_str, bstrSrc.m_str, LOCALE_USER_DEFAULT, 0) ==
VARCMP_LT;
}
bool operator<(LPCOLESTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator<(bstr2);
}
bool operator<(LPOLESTR pszSrc) const
{
return operator<((LPCOLESTR)pszSrc);
}
bool operator>(const CComBSTR& bstrSrc) const throw()
{
return VarBstrCmp(m_str, bstrSrc.m_str, LOCALE_USER_DEFAULT, 0) ==
VARCMP_GT;
}
bool operator>(LPCOLESTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator>(bstr2);
}
bool operator>(LPOLESTR pszSrc) const
{
return operator>((LPCOLESTR)pszSrc);
}
bool operator!=(const CComBSTR& bstrSrc) const throw()
{
return !operator==(bstrSrc);
}
bool operator!=(LPCOLESTR pszSrc) const
{
return !operator==(pszSrc);
}
bool operator!=(int nNull) const throw()
{
return !operator==(nNull);
}
bool operator!=(LPOLESTR pszSrc) const
{
return operator!=((LPCOLESTR)pszSrc);
}
bool operator==(const CComBSTR& bstrSrc) const throw()
{
return VarBstrCmp(m_str, bstrSrc.m_str, LOCALE_USER_DEFAULT, 0) ==
VARCMP_EQ;
}
bool operator==(LPCOLESTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator==(bstr2);
}
bool operator==(LPOLESTR pszSrc) const
{
return operator==((LPCOLESTR)pszSrc);
}
bool operator==(int nNull) const throw()
{
ATLASSERT(nNull == NULL);
(void)nNull;
return (m_str == NULL);
}
CComBSTR(LPCSTR pSrc)
{
if (pSrc != NULL)
{
m_str = A2WBSTR(pSrc);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
else
m_str = NULL;
}
CComBSTR(int nSize, LPCSTR sz)
{
if (nSize != 0 && sz == NULL)
{
m_str = ::SysAllocStringLen(NULL, nSize);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
return;
}
m_str = A2WBSTR(sz, nSize);
if (m_str == NULL && nSize != 0)
AtlThrow(E_OUTOFMEMORY);
}
HRESULT Append(LPCSTR lpsz) throw()
{
if (lpsz == NULL)
return S_OK;
CComBSTR bstrTemp;
ATLTRY(bstrTemp = lpsz);
if (bstrTemp.m_str == NULL)
return E_OUTOFMEMORY;
return Append(bstrTemp);
}
CComBSTR& operator=(LPCSTR pSrc)
{
::SysFreeString(m_str);
m_str = A2WBSTR(pSrc);
if (m_str == NULL && pSrc != NULL)
AtlThrow(E_OUTOFMEMORY);
return *this;
}
bool operator<(LPCSTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator<(bstr2);
}
bool operator>(LPCSTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator>(bstr2);
}
bool operator!=(LPCSTR pszSrc) const
{
return !operator==(pszSrc);
}
bool operator==(LPCSTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator==(bstr2);
}
HRESULT WriteToStream(IStream* pStream) throw()
{
ATLASSERT(pStream != NULL);
if(pStream == NULL)
return E_INVALIDARG;
ULONG cb;
ULONG cbStrLen = ULONG(m_str ? SysStringByteLen(m_str)+sizeof(OLECHAR) :
0);
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;
}
HRESULT ReadFromStream(IStream* pStream) throw()
{
ATLASSERT(pStream != NULL);
if(pStream == NULL)
return E_INVALIDARG;
ATLASSERT(m_str == NULL); // should be empty
Empty();
ULONG cbStrLen = 0;
HRESULT hr = pStream->Read((void*) &cbStrLen, sizeof(cbStrLen), NULL);
if ((hr == S_OK) && (cbStrLen != 0))
{
//subtract size for terminating NULL which we wrote out
//since SysAllocStringByteLen overallocates for the NULL
m_str = SysAllocStringByteLen(NULL, cbStrLen-sizeof(OLECHAR));
if (m_str == NULL)
hr = E_OUTOFMEMORY;
else
hr = pStream->Read((void*) m_str, cbStrLen, NULL);
// If SysAllocStringByteLen or IStream::Read failed, reset seek
// pointer to start of BSTR size.
if (hr != S_OK)
{
LARGE_INTEGER nOffset;
nOffset.QuadPart = -(static_cast<LONGLONG>(sizeof(cbStrLen)));
pStream->Seek(nOffset, STREAM_SEEK_CUR, NULL);
}
}
if (hr == S_FALSE)
hr = E_FAIL;
return hr;
}
static bool LoadStringResource(HINSTANCE hInstance, UINT uID, BSTR&
bstrText) throw()
{
const ATLSTRINGRESOURCEIMAGE* pImage;
ATLASSERT(bstrText == NULL);
pImage = AtlGetStringResourceImage(hInstance, uID);
if (pImage != NULL)
{
bstrText = ::SysAllocStringLen(pImage->achString, pImage->nLength);
}
return (bstrText != NULL) ? true : false;
}
static bool LoadStringResource(UINT uID, BSTR& bstrText) throw()
{
const ATLSTRINGRESOURCEIMAGE* pImage;
ATLASSERT(bstrText == NULL);
pImage = AtlGetStringResourceImage(uID);
if (pImage != NULL)
{
bstrText = ::SysAllocStringLen(pImage->achString, pImage->nLength);
}
return (bstrText != NULL) ? true : false;
}
// each character in BSTR is copied to each element in SAFEARRAY
HRESULT BSTRToArray(LPSAFEARRAY *ppArray) throw()
{
return VectorFromBstr(m_str, ppArray);
}
// first character of each element in SAFEARRAY is copied to BSTR
HRESULT ArrayToBSTR(const SAFEARRAY *pSrc) throw()
{
::SysFreeString(m_str);
return BstrFromVector((LPSAFEARRAY)pSrc, &m_str);
}
};