// Local BSTR derived from WTL3.1 CString Class #ifndef __LOCAL_BSTR__ #define __LOCAL_BSTR__ #pragma once #ifndef __cplusplus #error ATL requires C++ compilation (use a .cpp suffix) #endif #ifndef __ATLAPP_H__ #error atlmisc.h requires atlapp.h to be included first #endif namespace LBSTR { ///////////////////////////////////////////////////////////////////////////// // Forward declarations #ifndef _WTL_NO_LOCAL_BSTR class CString; #endif //!_WTL_NO_CSTRING ///////////////////////////////////////////////////////////////////////////// // CString - String class #ifdef DEBUG #define VERIFY_LOCAL_BSTR #endif #ifdef VERIFY_LOCAL_BSTR #ifdef DEBUG #define VERIFY_ASSERT(expr) ASSERT(expr) #else #if _MSC_VER >= 1300 && !defined(_CRT_PORTABLE) #define _CrtRetailBreak() __debugbreak() #elif defined(_M_IX86) && !defined(_CRT_PORTABLE) #define _CrtRetailBreak() __asm { int 3 } #elif defined(_M_ALPHA) && !defined(_CRT_PORTABLE) void _BPT(); #pragma intrinsic(_BPT) #define _CrtRetailBreak() _BPT() #elif defined(_M_IA64) && !defined(_CRT_PORTABLE) void __break(int); #pragma intrinsic (__break) #define _CrtRetailBreak() __break(0x80016) #else _CRTIMP void __cdecl _CrtRetailBreak( void ); #endif #if _MSC_VER >= 1300 || !defined(_M_IX86) || defined(_CRT_PORTABLE) #define _RETAIL_ASSERT_BASE(expr) \ (void) ((expr) || \ (_CrtRetailBreak(), 0)) #else #define _RETAIL_ASSERT_BASE(expr) \ do { if (!(expr)) \ _CrtRetailBreak(); } while (0) #endif #define VERIFY_ASSERT(expr) _RETAIL_ASSERT_BASE(expr) #endif // DEBUG #endif // VERIFY_LOCAL_BSTR #ifndef _WTL_NO_LOCAL_BSTR class CStringData { public: #ifdef VERIFY_LOCAL_BSTR int nStartTag; #endif long nRefs; // reference count int nDataLength; int nAllocLength; #ifdef VERIFY_LOCAL_BSTR int nMiddleTag; #endif ULONG cbDataLength; void SetDataLength( int p_nDataLength ) { ASSERT( p_nDataLength >= 0 ); nDataLength = p_nDataLength; cbDataLength = p_nDataLength * sizeof(TCHAR); } // TCHAR data[nAllocLength] TCHAR* data() { return (TCHAR*)(this + 1); } #ifdef VERIFY_LOCAL_BSTR BYTE * GetTagPtr(void) { TCHAR * pzEndTag; pzEndTag = data() + ( nAllocLength + 1 ); return (BYTE *) pzEndTag; } int GetEndTag(void) { int nEnd; memcpy( &nEnd, GetTagPtr(), sizeof(int) ); return nEnd; } void SetEndTag( int nEnd ) { memcpy( GetTagPtr(), &nEnd, sizeof(int) ); } #endif }; // Globals // For an empty string, m_pchData will point here // (note: avoids special case of checking for NULL m_pchData) // empty string data (and locked) #ifdef VERIFY_LOCAL_BSTR const int START_TAG = 0x01234567; const int MIDDLE_TAG = 0x89abcdef; const int END_TAG = 0xfedcba98; _declspec(selectany) int rgInitData[] = { START_TAG, -1, 0, 0, MIDDLE_TAG, 0, 0, END_TAG }; #else _declspec(selectany) int rgInitData[] = { -1, 0, 0, 0, 0 }; #endif _declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData; _declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData)); class CString { public: // Constructors CString(); CString(const CString& stringSrc); CString(TCHAR ch, int nRepeat = 1); CString(LPCSTR lpsz); CString(LPCWSTR lpsz); CString(LPCTSTR lpch, int nLength); CString(const unsigned char* psz); #ifdef VERIFY_LOCAL_BSTR void VerifyInitData( void ) { VerifyBlankData(); CStringData * pData = GetData(); VerifyTagData( pData ); } #endif // Attributes & Operations // as an array of characters int GetLength() const; BOOL IsEmpty() const; void Empty(); // free up the data TCHAR GetAt(int nIndex) const; // 0 based TCHAR operator[](int nIndex) const; // same as GetAt void SetAt(int nIndex, TCHAR ch); operator LPCTSTR() const; // as a C string operator BSTR() const; // as a BSTR string // overloaded assignment const CString& operator=(const CString& stringSrc); const CString& operator=(TCHAR ch); #ifdef _UNICODE const CString& operator=(char ch); #endif const CString& operator=(LPCSTR lpsz); const CString& operator=(LPCWSTR lpsz); const CString& operator=(const unsigned char* psz); // string concatenation const CString& operator+=(const CString& string); const CString& operator+=(TCHAR ch); #ifdef _UNICODE const CString& operator+=(char ch); #endif const CString& operator+=(LPCTSTR lpsz); friend CString __stdcall operator+(const CString& string1, const CString& string2); friend CString __stdcall operator+(const CString& string, TCHAR ch); friend CString __stdcall operator+(TCHAR ch, const CString& string); #ifdef _UNICODE friend CString __stdcall operator+(const CString& string, char ch); friend CString __stdcall operator+(char ch, const CString& string); #endif friend CString __stdcall operator+(const CString& string, LPCTSTR lpsz); friend CString __stdcall operator+(LPCTSTR lpsz, const CString& string); // string comparison int Compare(LPCTSTR lpsz) const; // straight character int CompareNoCase(LPCTSTR lpsz) const; // ignore case int Collate(LPCTSTR lpsz) const; // NLS aware // simple sub-string extraction CString Mid(int nFirst, int nCount) const; CString Mid(int nFirst) const; CString Left(int nCount) const; CString Right(int nCount) const; CString SpanIncluding(LPCTSTR lpszCharSet) const; CString SpanExcluding(LPCTSTR lpszCharSet) const; // upper/lower/reverse conversion void MakeUpper(); void MakeLower(); void MakeReverse(); // trimming whitespace (either side) void TrimRight(); void TrimLeft(); // advanced manipulation // replace occurrences of chOld with chNew int Replace(TCHAR chOld, TCHAR chNew); // replace occurrences of substring lpszOld with lpszNew; // empty lpszNew removes instances of lpszOld int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew); // remove occurrences of chRemove int Remove(TCHAR chRemove); // insert character at zero-based index; concatenates // if index is past end of string int Insert(int nIndex, TCHAR ch); // insert substring at zero-based index; concatenates // if index is past end of string int Insert(int nIndex, LPCTSTR pstr); // delete nCount characters starting at zero-based index int Delete(int nIndex, int nCount = 1); // searching (return starting index, or -1 if not found) // look for a single character match int Find(TCHAR ch) const; // like "C" strchr int ReverseFind(TCHAR ch) const; int FindOneOf(LPCTSTR lpszCharSet) const; // look for a specific sub-string int Find(LPCTSTR lpszSub) const; // like "C" strstr // Concatentation for non strings const CString& Append(int n) { TCHAR szBuffer[10]; wsprintf(szBuffer,_T("%d"),n); ConcatInPlace(SafeStrlen(szBuffer), szBuffer); return *this; } // simple formatting void __cdecl Format(LPCTSTR lpszFormat, ...); void __cdecl Format(UINT nFormatID, ...); // formatting for localization (uses FormatMessage API) BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...); BOOL __cdecl FormatMessage(UINT nFormatID, ...); // Windows support BOOL LoadString(UINT nID); // load from string resource // 255 chars max #ifndef _UNICODE // ANSI <-> OEM support (convert string in place) void AnsiToOem(); void OemToAnsi(); #endif #ifndef _ATL_NO_COM // OLE BSTR support (use for OLE automation) BSTR AllocSysString() const; BSTR SetSysString(BSTR* pbstr) const; #endif //!_ATL_NO_COM // Access to string implementation buffer as "C" character array LPTSTR GetBuffer(int nMinBufLength); void ReleaseBuffer(int nNewLength = -1); LPTSTR GetBufferSetLength(int nNewLength); void FreeExtra(); // Use LockBuffer/UnlockBuffer to turn refcounting off LPTSTR LockBuffer(); void UnlockBuffer(); // Implementation public: ~CString(); int GetAllocLength() const; static BOOL __stdcall _IsValidString(LPCWSTR lpsz, int nLength) { if(lpsz == NULL) return FALSE; return !::IsBadStringPtrW(lpsz, nLength); } static BOOL __stdcall _IsValidString(LPCSTR lpsz, int nLength) { if(lpsz == NULL) return FALSE; return !::IsBadStringPtrA(lpsz, nLength); } protected: LPTSTR m_pchData; // pointer to ref counted string data // implementation helpers CStringData* GetData() const; void Init(); void AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const; BOOL AllocBuffer(int nLen); void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData); BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data); void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData); void FormatV(LPCTSTR lpszFormat, va_list argList); void CopyBeforeWrite(); BOOL AllocBeforeWrite(int nLen); void Release(); static void PASCAL Release(CStringData* pData); static int PASCAL SafeStrlen(LPCTSTR lpsz); static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf) { #ifdef _DEBUG // LoadString without annoying warning from the Debug kernel if the // segment containing the string is not present if (::FindResource(_Module.GetResourceInstance(), MAKEINTRESOURCE((nID>>4) + 1), RT_STRING) == NULL) { lpszBuf[0] = '\0'; return 0; // not found } #endif //_DEBUG int nLen = ::LoadString(_Module.GetResourceInstance(), nID, lpszBuf, nMaxBuf); if (nLen == 0) lpszBuf[0] = '\0'; return nLen; } #ifdef VERIFY_LOCAL_BSTR static void VerifyBlankData( void ) { VERIFY_ASSERT( rgInitData[0] == START_TAG ); VERIFY_ASSERT( rgInitData[1] == -1 ); VERIFY_ASSERT( rgInitData[2] == 0 ); VERIFY_ASSERT( rgInitData[3] == 0 ); VERIFY_ASSERT( rgInitData[4] == MIDDLE_TAG ); VERIFY_ASSERT( rgInitData[5] == 0 ); VERIFY_ASSERT( rgInitData[6] == 0 ); VERIFY_ASSERT( rgInitData[7] == END_TAG ); } static void VerifyTagData( CStringData * pData ) { if ( pData && pData != _atltmpDataNil ) { VERIFY_ASSERT( pData->nStartTag == START_TAG ); VERIFY_ASSERT( pData->nMiddleTag == MIDDLE_TAG ); VERIFY_ASSERT( pData->GetEndTag() == END_TAG ); } } #endif static const CString& __stdcall _GetEmptyString() { #ifdef VERIFY_LOCAL_BSTR VerifyBlankData(); #endif return *(CString*)&_atltmpPchNil; } // CString conversion helpers static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count) { if (count == 0 && mbstr != NULL) return 0; int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL); ATLASSERT(mbstr == NULL || result <= (int)count); if (result > 0) mbstr[result - 1] = 0; return result; } static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count) { if (count == 0 && wcstr != NULL) return 0; int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count); ATLASSERT(wcstr == NULL || result <= (int)count); if (result > 0) wcstr[result - 1] = 0; return result; } // Helpers to avoid CRT startup code static TCHAR* _cstrchr(const TCHAR* p, TCHAR ch) { //strchr for '\0' should succeed while (*p != 0) { if (*p == ch) break; p = ::CharNext(p); } return (TCHAR*)((*p == ch) ? p : NULL); } static TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2) { const TCHAR* lpsz = NULL; while (*p != 0) { if (*p == ch1 && *(p + 1) == ch2) { lpsz = p; break; } p = ::CharNext(p); } return (TCHAR*)lpsz; } static TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch) { const TCHAR* lpsz = NULL; while (*p != 0) { if (*p == ch) lpsz = p; p = ::CharNext(p); } return (TCHAR*)lpsz; } static TCHAR* _cstrrev(TCHAR* pStr) { // Optimize NULL, zero-length, and single-char case. if ((pStr == NULL) || (pStr[0] == '\0') || (pStr[1] == '\0')) return pStr; TCHAR* p = pStr; while (p[1] != 0) { TCHAR* pNext = ::CharNext(p); if(pNext > p + 1) { char p1 = *(char*)p; *(char*)p = *(char*)(p + 1); *(char*)(p + 1) = p1; } p = pNext; } TCHAR* q = pStr; while (q < p) { TCHAR t = *q; *q = *p; *p = t; q++; p--; } return (TCHAR*)pStr; } static TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet) { int nLen = lstrlen(pCharSet); if (nLen == 0) return (TCHAR*)pStr; const TCHAR* pRet = NULL; const TCHAR* pCur = pStr; while((pStr = _cstrchr(pCur, *pCharSet)) != NULL) { if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0) { pRet = pCur; break; } pCur = ::CharNext(pCur); } return (TCHAR*) pRet; } static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet) { int nRet = 0; TCHAR* p = (TCHAR*)pStr; while (*p != 0) { TCHAR* pNext = ::CharNext(p); if(pNext > p + 1) { if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL) break; nRet += 2; } else { if(_cstrchr(pCharSet, *p) == NULL) break; nRet++; } p = pNext; } return nRet; } static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet) { int nRet = 0; TCHAR* p = (TCHAR*)pStr; while (*p != 0) { TCHAR* pNext = ::CharNext(p); if(pNext > p + 1) { if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL) break; nRet += 2; } else { if(_cstrchr(pCharSet, *p) != NULL) break; nRet++; } p = pNext; } return nRet; } static TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet) { while (*p != 0) { if (_cstrchr(lpszCharSet, *p) != NULL) { return (TCHAR*)p; break; } p = ::CharNext(p); } return NULL; } static int _cstrisdigit(TCHAR ch) { WORD type; GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type); return (type & C1_DIGIT) == C1_DIGIT; } static int _cstrisspace(TCHAR ch) { WORD type; GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type); return (type & C1_SPACE) == C1_SPACE; } static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther) { return lstrcmp(pstrOne, pstrOther); } static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther) { return lstrcmpi(pstrOne, pstrOther); } static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther) { int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1); ATLASSERT(nRet != 0); return nRet - 2; // Convert to strcmp convention. This really is documented. } static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther) { int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1); ATLASSERT(nRet != 0); return nRet - 2; // Convert to strcmp convention. This really is documented. } }; // Compare helpers bool __stdcall operator==(const CString& s1, const CString& s2); bool __stdcall operator==(const CString& s1, LPCTSTR s2); bool __stdcall operator==(LPCTSTR s1, const CString& s2); bool __stdcall operator!=(const CString& s1, const CString& s2); bool __stdcall operator!=(const CString& s1, LPCTSTR s2); bool __stdcall operator!=(LPCTSTR s1, const CString& s2); bool __stdcall operator<(const CString& s1, const CString& s2); bool __stdcall operator<(const CString& s1, LPCTSTR s2); bool __stdcall operator<(LPCTSTR s1, const CString& s2); bool __stdcall operator>(const CString& s1, const CString& s2); bool __stdcall operator>(const CString& s1, LPCTSTR s2); bool __stdcall operator>(LPCTSTR s1, const CString& s2); bool __stdcall operator<=(const CString& s1, const CString& s2); bool __stdcall operator<=(const CString& s1, LPCTSTR s2); bool __stdcall operator<=(LPCTSTR s1, const CString& s2); bool __stdcall operator>=(const CString& s1, const CString& s2); bool __stdcall operator>=(const CString& s1, LPCTSTR s2); bool __stdcall operator>=(LPCTSTR s1, const CString& s2); ///////////////////////////////////////////////////////////////////////////// // CString Implementation inline CStringData* CString::GetData() const { ATLASSERT(m_pchData != NULL); return ((CStringData*)m_pchData) - 1; } inline void CString::Init() { m_pchData = _GetEmptyString().m_pchData; } inline CString::CString(const unsigned char* lpsz) { Init(); *this = (LPCSTR)lpsz; } inline const CString& CString::operator=(const unsigned char* lpsz) { *this = (LPCSTR)lpsz; return *this; } #ifdef _UNICODE inline const CString& CString::operator+=(char ch) { *this += (TCHAR)ch; return *this; } inline const CString& CString::operator=(char ch) { *this = (TCHAR)ch; return *this; } inline CString __stdcall operator+(const CString& string, char ch) { return string + (TCHAR)ch; } inline CString __stdcall operator+(char ch, const CString& string) { return (TCHAR)ch + string; } #endif inline int CString::GetLength() const { return GetData()->nDataLength; } inline int CString::GetAllocLength() const { return GetData()->nAllocLength; } inline BOOL CString::IsEmpty() const { return GetData()->nDataLength == 0; } inline CString::operator LPCTSTR() const { return m_pchData; } inline CString::operator BSTR() const { return (BSTR) m_pchData; } inline int PASCAL CString::SafeStrlen(LPCTSTR lpsz) { return (lpsz == NULL) ? 0 : lstrlen(lpsz); } // CString support (windows specific) inline int CString::Compare(LPCTSTR lpsz) const { return _cstrcmp(m_pchData, lpsz); } // MBCS/Unicode aware inline int CString::CompareNoCase(LPCTSTR lpsz) const { return _cstrcmpi(m_pchData, lpsz); } // MBCS/Unicode aware // CString::Collate is often slower than Compare but is MBSC/Unicode // aware as well as locale-sensitive with respect to sort order. inline int CString::Collate(LPCTSTR lpsz) const { return _cstrcoll(m_pchData, lpsz); } // locale sensitive inline TCHAR CString::GetAt(int nIndex) const { ATLASSERT(nIndex >= 0); ATLASSERT(nIndex < GetData()->nDataLength); return m_pchData[nIndex]; } inline TCHAR CString::operator[](int nIndex) const { // same as GetAt ATLASSERT(nIndex >= 0); ATLASSERT(nIndex < GetData()->nDataLength); return m_pchData[nIndex]; } inline bool __stdcall operator==(const CString& s1, const CString& s2) { return s1.Compare(s2) == 0; } inline bool __stdcall operator==(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) == 0; } inline bool __stdcall operator==(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) == 0; } inline bool __stdcall operator!=(const CString& s1, const CString& s2) { return s1.Compare(s2) != 0; } inline bool __stdcall operator!=(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) != 0; } inline bool __stdcall operator!=(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) != 0; } inline bool __stdcall operator<(const CString& s1, const CString& s2) { return s1.Compare(s2) < 0; } inline bool __stdcall operator<(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) < 0; } inline bool __stdcall operator<(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) > 0; } inline bool __stdcall operator>(const CString& s1, const CString& s2) { return s1.Compare(s2) > 0; } inline bool __stdcall operator>(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) > 0; } inline bool __stdcall operator>(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) < 0; } inline bool __stdcall operator<=(const CString& s1, const CString& s2) { return s1.Compare(s2) <= 0; } inline bool __stdcall operator<=(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) <= 0; } inline bool __stdcall operator<=(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) >= 0; } inline bool __stdcall operator>=(const CString& s1, const CString& s2) { return s1.Compare(s2) >= 0; } inline bool __stdcall operator>=(const CString& s1, LPCTSTR s2) { return s1.Compare(s2) >= 0; } inline bool __stdcall operator>=(LPCTSTR s1, const CString& s2) { return s2.Compare(s1) <= 0; } inline CString::CString() { Init(); } inline CString::CString(const CString& stringSrc) { ATLASSERT(stringSrc.GetData()->nRefs != 0); if (stringSrc.GetData()->nRefs >= 0) { ATLASSERT(stringSrc.GetData() != _atltmpDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } else { Init(); *this = stringSrc.m_pchData; } } inline BOOL CString::AllocBuffer(int nLen) // always allocate one extra character for '\0' termination // assumes [optimistically] that data length will equal allocation length { ATLASSERT(nLen >= 0); ATLASSERT(nLen <= INT_MAX - 1); // max size (enough room for 1 extra) #ifdef VERIFY_LOCAL_BSTR VerifyInitData(); #endif if (nLen == 0) { Init(); } else { CStringData* pData = NULL; int cbBufLen = sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR); #ifdef VERIFY_LOCAL_BSTR cbBufLen += sizeof(int); // End Tag #endif ATLTRY(pData = (CStringData*)new BYTE[ cbBufLen ]); if(pData == NULL) return FALSE; pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->SetDataLength( nLen ); pData->nAllocLength = nLen; m_pchData = pData->data(); #ifdef VERIFY_LOCAL_BSTR pData->nStartTag = START_TAG; pData->nMiddleTag = MIDDLE_TAG; pData->SetEndTag( END_TAG ); #endif } return TRUE; } inline void CString::Release() { #ifdef VERIFY_LOCAL_BSTR VerifyInitData(); #endif if (GetData() != _atltmpDataNil) { ATLASSERT(GetData()->nRefs != 0); if (InterlockedDecrement(&GetData()->nRefs) <= 0) delete[] (BYTE*)GetData(); Init(); } } inline void PASCAL CString::Release(CStringData* pData) { #ifdef VERIFY_LOCAL_BSTR VerifyBlankData(); VerifyTagData( pData ); #endif if (pData != _atltmpDataNil) { ATLASSERT(pData->nRefs != 0); if (InterlockedDecrement(&pData->nRefs) <= 0) delete[] (BYTE*)pData; } } inline void CString::Empty() { if (GetData()->nDataLength == 0) return; if (GetData()->nRefs >= 0) Release(); else *this = _T(""); ATLASSERT(GetData()->nDataLength == 0); ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0); #ifdef VERIFY_LOCAL_BSTR VerifyInitData(); #endif } inline void CString::CopyBeforeWrite() { if (GetData()->nRefs > 1) { CStringData* pData = GetData(); Release(); if(AllocBuffer(pData->nDataLength)) memcpy(m_pchData, pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR)); } ATLASSERT(GetData()->nRefs <= 1); } inline BOOL CString::AllocBeforeWrite(int nLen) { BOOL bRet = TRUE; if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) { Release(); bRet = AllocBuffer(nLen); } ATLASSERT(GetData()->nRefs <= 1); return bRet; } inline CString::~CString() // free any attached data { #ifdef VERIFY_LOCAL_BSTR VerifyInitData(); #endif if (GetData() != _atltmpDataNil) { if (InterlockedDecrement(&GetData()->nRefs) <= 0) delete[] (BYTE*)GetData(); } } inline void CString::AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const { // will clone the data attached to this string // allocating 'nExtraLen' characters // Places results in uninitialized string 'dest' // Will copy the part or all of original data to start of new string int nNewLen = nCopyLen + nExtraLen; if (nNewLen == 0) { dest.Init(); } else { if(dest.AllocBuffer(nNewLen)) memcpy(dest.m_pchData, m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR)); } } inline CString::CString(LPCTSTR lpsz) { Init(); if (lpsz != NULL && HIWORD(lpsz) == NULL) { UINT nID = LOWORD((DWORD_PTR)lpsz); if (!LoadString(nID)) ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID); } else { int nLen = SafeStrlen(lpsz); if (nLen != 0) { if(AllocBuffer(nLen)) memcpy(m_pchData, lpsz, nLen * sizeof(TCHAR)); } } } #ifdef _UNICODE inline CString::CString(LPCSTR lpsz) { Init(); int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; if (nSrcLen != 0) { if(AllocBuffer(nSrcLen)) { _mbstowcsz(m_pchData, lpsz, nSrcLen + 1); ReleaseBuffer(); } } } #else //_UNICODE inline CString::CString(LPCWSTR lpsz) { Init(); int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; if (nSrcLen != 0) { if(AllocBuffer(nSrcLen * 2)) { _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1); ReleaseBuffer(); } } } #endif //!_UNICODE // Assignment operators // All assign a new value to the string // (a) first see if the buffer is big enough // (b) if enough room, copy on top of old buffer, set size and type // (c) otherwise free old string data, and create a new one // // All routines return the new string (but as a 'const CString&' so that // assigning it again will cause a copy, eg: s1 = s2 = "hi there". // inline void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { if(AllocBeforeWrite(nSrcLen)) { memcpy(m_pchData, lpszSrcData, nSrcLen * sizeof(TCHAR)); GetData()->SetDataLength( nSrcLen ); m_pchData[nSrcLen] = '\0'; } } inline const CString& CString::operator=(const CString& stringSrc) { if (m_pchData != stringSrc.m_pchData) { if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0) { // actual copy necessary since one of the strings is locked AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); } else { // can just copy references around Release(); ATLASSERT(stringSrc.GetData() != _atltmpDataNil); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } } return *this; } inline const CString& CString::operator=(LPCTSTR lpsz) { ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; } #ifdef _UNICODE inline const CString& CString::operator=(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; if(AllocBeforeWrite(nSrcLen)) { _mbstowcsz(m_pchData, lpsz, nSrcLen + 1); ReleaseBuffer(); } return *this; } #else //!_UNICODE inline const CString& CString::operator=(LPCWSTR lpsz) { int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; if(AllocBeforeWrite(nSrcLen * 2)) { _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1); ReleaseBuffer(); } return *this; } #endif //!_UNICODE // Concatenation // NOTE: "operator+" is done as friend functions for simplicity // There are three variants: // CString + CString // and for ? = TCHAR, LPCTSTR // CString + ? // ? + CString inline BOOL CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) { // -- master concatenation routine // Concatenate two sources // -- assume that 'this' is a new CString object BOOL bRet = TRUE; int nNewLen = nSrc1Len + nSrc2Len; if (nNewLen != 0) { bRet = AllocBuffer(nNewLen); if (bRet) { memcpy(m_pchData, lpszSrc1Data, nSrc1Len * sizeof(TCHAR)); memcpy(m_pchData + nSrc1Len, lpszSrc2Data, nSrc2Len * sizeof(TCHAR)); } } return bRet; } inline CString __stdcall operator+(const CString& string1, const CString& string2) { CString s; s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData); return s; } inline CString __stdcall operator+(const CString& string, LPCTSTR lpsz) { ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE)); CString s; s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz); return s; } inline CString __stdcall operator+(LPCTSTR lpsz, const CString& string) { ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz, FALSE)); CString s; s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData); return s; } inline void CString::ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData) { // -- the main routine for += operators // concatenating an empty string is a no-op! if (nSrcLen == 0) return; // if the buffer is too small, or we have a width mis-match, just // allocate a new buffer (slow but sure) if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) { // we have to grow the buffer, use the ConcatCopy routine CStringData* pOldData = GetData(); if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData)) { ATLASSERT(pOldData != NULL); CString::Release(pOldData); } } else { // fast concatenation when buffer big enough memcpy(m_pchData + GetData()->nDataLength, lpszSrcData, nSrcLen * sizeof(TCHAR)); GetData()->SetDataLength( GetData()->nDataLength + nSrcLen ); ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength); m_pchData[GetData()->nDataLength] = '\0'; } } inline const CString& CString::operator+=(LPCTSTR lpsz) { ATLASSERT(lpsz == NULL || _IsValidString(lpsz, FALSE)); ConcatInPlace(SafeStrlen(lpsz), lpsz); return *this; } inline const CString& CString::operator+=(TCHAR ch) { ConcatInPlace(1, &ch); return *this; } inline const CString& CString::operator+=(const CString& string) { ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); return *this; } inline LPTSTR CString::GetBuffer(int nMinBufLength) { ATLASSERT(nMinBufLength >= 0); if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) { // we have to grow the buffer CStringData* pOldData = GetData(); int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it if (nMinBufLength < nOldLen) nMinBufLength = nOldLen; if(AllocBuffer(nMinBufLength)) { memcpy(m_pchData, pOldData->data(), (nOldLen + 1) * sizeof(TCHAR)); GetData()->SetDataLength( nOldLen ); CString::Release(pOldData); } } ATLASSERT(GetData()->nRefs <= 1); #ifdef VERIFY_LOCAL_BSTR VerifyInitData(); #endif // return a pointer to the character storage for this string ATLASSERT(m_pchData != NULL); return m_pchData; } inline void CString::ReleaseBuffer(int nNewLength) { CopyBeforeWrite(); // just in case GetBuffer was not called if (nNewLength == -1) nNewLength = lstrlen(m_pchData); // zero terminated ATLASSERT(nNewLength <= GetData()->nAllocLength); GetData()->SetDataLength( nNewLength ); m_pchData[nNewLength] = '\0'; #ifdef VERIFY_LOCAL_BSTR VerifyInitData(); #endif } inline LPTSTR CString::GetBufferSetLength(int nNewLength) { ATLASSERT(nNewLength >= 0); GetBuffer(nNewLength); if ( GetAllocLength() >= nNewLength ) { GetData()->SetDataLength( nNewLength ); m_pchData[nNewLength] = '\0'; } #ifdef VERIFY_LOCAL_BSTR VerifyInitData(); #endif return m_pchData; } inline void CString::FreeExtra() { ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength); if (GetData()->nDataLength != GetData()->nAllocLength) { CStringData* pOldData = GetData(); if(AllocBuffer(GetData()->nDataLength)) { memcpy(m_pchData, pOldData->data(), pOldData->nDataLength * sizeof(TCHAR)); ATLASSERT(m_pchData[GetData()->nDataLength] == '\0'); CString::Release(pOldData); } } ATLASSERT(GetData() != NULL); } inline LPTSTR CString::LockBuffer() { LPTSTR lpsz = GetBuffer(0); GetData()->nRefs = -1; return lpsz; } inline void CString::UnlockBuffer() { ATLASSERT(GetData()->nRefs == -1); if (GetData() != _atltmpDataNil) GetData()->nRefs = 1; } inline int CString::Find(TCHAR ch) const { // find first single character LPTSTR lpsz = _cstrchr(m_pchData, (_TUCHAR)ch); // return -1 if not found and index otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } inline int CString::FindOneOf(LPCTSTR lpszCharSet) const { ATLASSERT(_IsValidString(lpszCharSet, FALSE)); LPTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet); return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } inline void CString::MakeUpper() { CopyBeforeWrite(); CharUpper(m_pchData); } inline void CString::MakeLower() { CopyBeforeWrite(); CharLower(m_pchData); } inline void CString::MakeReverse() { CopyBeforeWrite(); _cstrrev(m_pchData); } inline void CString::SetAt(int nIndex, TCHAR ch) { ATLASSERT(nIndex >= 0); ATLASSERT(nIndex < GetData()->nDataLength); CopyBeforeWrite(); m_pchData[nIndex] = ch; } #ifndef _UNICODE inline void CString::AnsiToOem() { CopyBeforeWrite(); ::AnsiToOem(m_pchData, m_pchData); } inline void CString::OemToAnsi() { CopyBeforeWrite(); ::OemToAnsi(m_pchData, m_pchData); } #endif inline CString::CString(TCHAR ch, int nLength) { ATLASSERT(!_istlead(ch)); // can't create a lead byte string Init(); if (nLength >= 1) { if(AllocBuffer(nLength)) { #ifdef _UNICODE for (int i = 0; i < nLength; i++) m_pchData[i] = ch; #else memset(m_pchData, ch, nLength); #endif } } } inline CString::CString(LPCTSTR lpch, int nLength) { Init(); if (nLength != 0) { if(AllocBuffer(nLength)) memcpy(m_pchData, lpch, nLength * sizeof(TCHAR)); } } inline const CString& CString::operator=(TCHAR ch) { ATLASSERT(!_istlead(ch)); // can't set single lead byte AssignCopy(1, &ch); return *this; } inline CString __stdcall operator+(const CString& string1, TCHAR ch) { CString s; s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, 1, &ch); return s; } inline CString __stdcall operator+(TCHAR ch, const CString& string) { CString s; s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData); return s; } inline CString CString::Mid(int nFirst) const { return Mid(nFirst, GetData()->nDataLength - nFirst); } inline CString CString::Mid(int nFirst, int nCount) const { // out-of-bounds requests return sensible things if (nFirst < 0) nFirst = 0; if (nCount < 0) nCount = 0; if (nFirst + nCount > GetData()->nDataLength) nCount = GetData()->nDataLength - nFirst; if (nFirst > GetData()->nDataLength) nCount = 0; CString dest; AllocCopy(dest, nCount, nFirst, 0); return dest; } inline CString CString::Right(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength; CString dest; AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0); return dest; } inline CString CString::Left(int nCount) const { if (nCount < 0) nCount = 0; else if (nCount > GetData()->nDataLength) nCount = GetData()->nDataLength; CString dest; AllocCopy(dest, nCount, 0, 0); return dest; } // strspn equivalent inline CString CString::SpanIncluding(LPCTSTR lpszCharSet) const { ATLASSERT(_IsValidString(lpszCharSet, FALSE)); return Left(_cstrspn(m_pchData, lpszCharSet)); } // strcspn equivalent inline CString CString::SpanExcluding(LPCTSTR lpszCharSet) const { ATLASSERT(_IsValidString(lpszCharSet, FALSE)); return Left(_cstrcspn(m_pchData, lpszCharSet)); } inline int CString::ReverseFind(TCHAR ch) const { // find last single character LPTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch); // return -1 if not found, distance from beginning otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } // find a sub-string (like strstr) inline int CString::Find(LPCTSTR lpszSub) const { ATLASSERT(_IsValidString(lpszSub, FALSE)); // find first matching substring LPTSTR lpsz = _cstrstr(m_pchData, lpszSub); // return -1 for not found, distance from beginning otherwise return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); } inline void CString::FormatV(LPCTSTR lpszFormat, va_list argList) { ATLASSERT(_IsValidString(lpszFormat, FALSE)); enum _FormatModifiers { FORCE_ANSI = 0x10000, FORCE_UNICODE = 0x20000, FORCE_INT64 = 0x40000 }; va_list argListSave = argList; // make a guess at the maximum length of the resulting string int nMaxLen = 0; for (LPCTSTR lpsz = lpszFormat; *lpsz != '\0'; lpsz = ::CharNext(lpsz)) { // handle '%' character, but watch out for '%%' if (*lpsz != '%' || *(lpsz = ::CharNext(lpsz)) == '%') { nMaxLen += (int)lstrlen(lpsz); continue; } int nItemLen = 0; // handle '%' character with format int nWidth = 0; for (; *lpsz != '\0'; lpsz = ::CharNext(lpsz)) { // check for valid flags if (*lpsz == '#') nMaxLen += 2; // for '0x' else if (*lpsz == '*') nWidth = va_arg(argList, int); else if (*lpsz == '-' || *lpsz == '+' || *lpsz == '0' || *lpsz == ' ') ; else // hit non-flag character break; } // get width and skip it if (nWidth == 0) { // width indicated by nWidth = _ttoi(lpsz); for (; *lpsz != '\0' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz)) ; } ATLASSERT(nWidth >= 0); int nPrecision = 0; if (*lpsz == '.') { // skip past '.' separator (width.precision) lpsz = ::CharNext(lpsz); // get precision and skip it if (*lpsz == '*') { nPrecision = va_arg(argList, int); lpsz = ::CharNext(lpsz); } else { nPrecision = _ttoi(lpsz); for (; *lpsz != '\0' && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz)) ; } ATLASSERT(nPrecision >= 0); } // should be on type modifier or specifier int nModifier = 0; if(lpsz[0] == _T('I') && lpsz[1] == _T('6') && lpsz[2] == _T('4')) { lpsz += 3; nModifier = FORCE_INT64; } else { switch (*lpsz) { // modifiers that affect size case 'h': nModifier = FORCE_ANSI; lpsz = ::CharNext(lpsz); break; case 'l': nModifier = FORCE_UNICODE; lpsz = ::CharNext(lpsz); break; // modifiers that do not affect size case 'F': case 'N': case 'L': lpsz = ::CharNext(lpsz); break; } } // now should be on specifier switch (*lpsz | nModifier) { // single characters case 'c': case 'C': nItemLen = 2; va_arg(argList, TCHAR); break; case 'c' | FORCE_ANSI: case 'C' | FORCE_ANSI: nItemLen = 2; va_arg(argList, char); break; case 'c' | FORCE_UNICODE: case 'C' | FORCE_UNICODE: nItemLen = 2; va_arg(argList, WCHAR); break; // strings case 's': { LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = lstrlen(pstrNextArg); nItemLen = max(1, nItemLen); } break; } case 'S': { #ifndef _UNICODE LPWSTR pstrNextArg = va_arg(argList, LPWSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = (int)wcslen(pstrNextArg); nItemLen = max(1, nItemLen); } #else LPCSTR pstrNextArg = va_arg(argList, LPCSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = lstrlenA(pstrNextArg); nItemLen = max(1, nItemLen); } #endif break; } case 's' | FORCE_ANSI: case 'S' | FORCE_ANSI: { LPCSTR pstrNextArg = va_arg(argList, LPCSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = lstrlenA(pstrNextArg); nItemLen = max(1, nItemLen); } break; } case 's' | FORCE_UNICODE: case 'S' | FORCE_UNICODE: { LPWSTR pstrNextArg = va_arg(argList, LPWSTR); if (pstrNextArg == NULL) { nItemLen = 6; // "(null)" } else { nItemLen = (int)wcslen(pstrNextArg); nItemLen = max(1, nItemLen); } break; } } // adjust nItemLen for strings if (nItemLen != 0) { nItemLen = max(nItemLen, nWidth); if (nPrecision != 0) nItemLen = min(nItemLen, nPrecision); } else { switch (*lpsz) { // integers case 'd': case 'i': case 'u': case 'x': case 'X': case 'o': if (nModifier & FORCE_INT64) va_arg(argList, __int64); else va_arg(argList, int); nItemLen = 32; nItemLen = max(nItemLen, nWidth + nPrecision); break; #ifndef _ATL_USE_CSTRING_FLOAT case 'e': case 'f': case 'g': case 'G': ATLASSERT(!"Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class."); #ifndef _DEBUG ::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class.")); ::DebugBreak(); #endif //!_DEBUG break; #else //_ATL_USE_CSTRING_FLOAT case 'e': case 'g': case 'G': va_arg(argList, double); nItemLen = 128; nItemLen = max(nItemLen, nWidth + nPrecision); break; case 'f': { double f; LPTSTR pszTemp; // 312 == strlen("-1+(309 zeroes).") // 309 zeroes == max precision of a double // 6 == adjustment in case precision is not specified, // which means that the precision defaults to 6 pszTemp = (LPTSTR)_alloca(max(nWidth, 312 + nPrecision + 6)); f = va_arg(argList, double); _stprintf(pszTemp, _T( "%*.*f" ), nWidth, nPrecision + 6, f); nItemLen = _tcslen(pszTemp); } break; #endif //_ATL_USE_CSTRING_FLOAT case 'p': va_arg(argList, void*); nItemLen = 32; nItemLen = max(nItemLen, nWidth + nPrecision); break; // no output case 'n': va_arg(argList, int*); break; default: ATLASSERT(FALSE); // unknown formatting option } } // adjust nMaxLen for output nItemLen nMaxLen += nItemLen; } GetBuffer(nMaxLen); #ifndef _ATL_USE_CSTRING_FLOAT int nRet = wvsprintf(m_pchData, lpszFormat, argListSave); #else //_ATL_USE_CSTRING_FLOAT int nRet = _vstprintf(m_pchData, lpszFormat, argListSave); #endif //_ATL_USE_CSTRING_FLOAT nRet; // ref ATLASSERT(nRet <= GetAllocLength()); ReleaseBuffer(); va_end(argListSave); } // formatting (using wsprintf style formatting) inline void __cdecl CString::Format(LPCTSTR lpszFormat, ...) { ATLASSERT(_IsValidString(lpszFormat, FALSE)); va_list argList; va_start(argList, lpszFormat); FormatV(lpszFormat, argList); va_end(argList); } inline void __cdecl CString::Format(UINT nFormatID, ...) { CString strFormat; BOOL bRet = strFormat.LoadString(nFormatID); bRet; // ref ATLASSERT(bRet != 0); va_list argList; va_start(argList, nFormatID); FormatV(strFormat, argList); va_end(argList); } // formatting (using FormatMessage style formatting) inline BOOL __cdecl CString::FormatMessage(LPCTSTR lpszFormat, ...) { // format message into temporary buffer lpszTemp va_list argList; va_start(argList, lpszFormat); LPTSTR lpszTemp; BOOL bRet = TRUE; if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL) bRet = FALSE; // assign lpszTemp into the resulting string and free the temporary *this = lpszTemp; LocalFree(lpszTemp); va_end(argList); return bRet; } inline BOOL __cdecl CString::FormatMessage(UINT nFormatID, ...) { // get format string from string table CString strFormat; BOOL bRetTmp = strFormat.LoadString(nFormatID); bRetTmp; // ref ATLASSERT(bRetTmp != 0); // format message into temporary buffer lpszTemp va_list argList; va_start(argList, nFormatID); LPTSTR lpszTemp; BOOL bRet = TRUE; if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL) bRet = FALSE; // assign lpszTemp into the resulting string and free lpszTemp *this = lpszTemp; LocalFree(lpszTemp); va_end(argList); return bRet; } inline void CString::TrimRight() { CopyBeforeWrite(); // find beginning of trailing spaces by starting at beginning (DBCS aware) LPTSTR lpsz = m_pchData; LPTSTR lpszLast = NULL; while (*lpsz != '\0') { if (_cstrisspace(*lpsz)) { if (lpszLast == NULL) lpszLast = lpsz; } else { lpszLast = NULL; } lpsz = ::CharNext(lpsz); } if (lpszLast != NULL) { // truncate at trailing space start *lpszLast = '\0'; GetData()->SetDataLength( (int)(DWORD_PTR)(lpszLast - m_pchData) ); } } inline void CString::TrimLeft() { CopyBeforeWrite(); // find first non-space character LPCTSTR lpsz = m_pchData; while (_cstrisspace(*lpsz)) lpsz = ::CharNext(lpsz); // fix up data and length int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData); memmove(m_pchData, lpsz, (nDataLength + 1) * sizeof(TCHAR)); GetData()->SetDataLength( nDataLength ); } inline int CString::Delete(int nIndex, int nCount /* = 1 */) { if (nIndex < 0) nIndex = 0; int nNewLength = GetData()->nDataLength; if (nCount > 0 && nIndex < nNewLength) { CopyBeforeWrite(); int nBytesToCopy = nNewLength - (nIndex + nCount) + 1; memmove(m_pchData + nIndex, m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR)); GetData()->SetDataLength( nNewLength - nCount ); } return nNewLength; } inline int CString::Insert(int nIndex, TCHAR ch) { CopyBeforeWrite(); if (nIndex < 0) nIndex = 0; int nNewLength = GetData()->nDataLength; if (nIndex > nNewLength) nIndex = nNewLength; nNewLength++; if (GetData()->nAllocLength < nNewLength) { CStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; if(!AllocBuffer(nNewLength)) return -1; memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR)); CString::Release(pOldData); } // move existing bytes down memmove(m_pchData + nIndex + 1, m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR)); m_pchData[nIndex] = ch; GetData()->SetDataLength( nNewLength ); return nNewLength; } inline int CString::Insert(int nIndex, LPCTSTR pstr) { if (nIndex < 0) nIndex = 0; int nInsertLength = SafeStrlen(pstr); int nNewLength = GetData()->nDataLength; if (nInsertLength > 0) { CopyBeforeWrite(); if (nIndex > nNewLength) nIndex = nNewLength; nNewLength += nInsertLength; if (GetData()->nAllocLength < nNewLength) { CStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; if(!AllocBuffer(nNewLength)) return -1; memcpy(m_pchData, pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR)); CString::Release(pOldData); } // move existing bytes down memmove(m_pchData + nIndex + nInsertLength, m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR)); memcpy(m_pchData + nIndex, pstr, nInsertLength * sizeof(TCHAR)); GetData()->SetDataLength( nNewLength ); } return nNewLength; } inline int CString::Replace(TCHAR chOld, TCHAR chNew) { int nCount = 0; // short-circuit the nop case if (chOld != chNew) { // otherwise modify each character that matches in the string CopyBeforeWrite(); LPTSTR psz = m_pchData; LPTSTR pszEnd = psz + GetData()->nDataLength; while (psz < pszEnd) { // replace instances of the specified character only if (*psz == chOld) { *psz = chNew; nCount++; } psz = ::CharNext(psz); } } return nCount; } inline int CString::Replace(LPCTSTR lpszOld, LPCTSTR lpszNew) { // can't have empty or NULL lpszOld int nSourceLen = SafeStrlen(lpszOld); if (nSourceLen == 0) return 0; int nReplacementLen = SafeStrlen(lpszNew); // loop once to figure out the size of the result string int nCount = 0; LPTSTR lpszStart = m_pchData; LPTSTR lpszEnd = m_pchData + GetData()->nDataLength; LPTSTR lpszTarget; while (lpszStart < lpszEnd) { while ((lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL) { nCount++; lpszStart = lpszTarget + nSourceLen; } lpszStart += lstrlen(lpszStart) + 1; } // if any changes were made, make them if (nCount > 0) { CopyBeforeWrite(); // if the buffer is too small, just // allocate a new buffer (slow but sure) int nOldLength = GetData()->nDataLength; int nNewLength = nOldLength + (nReplacementLen - nSourceLen) * nCount; if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1) { CStringData* pOldData = GetData(); LPTSTR pstr = m_pchData; if(!AllocBuffer(nNewLength)) return -1; memcpy(m_pchData, pstr, pOldData->nDataLength * sizeof(TCHAR)); CString::Release(pOldData); } // else, we just do it in-place lpszStart = m_pchData; lpszEnd = m_pchData + GetData()->nDataLength; // loop again to actually do the work while (lpszStart < lpszEnd) { while ( (lpszTarget = _cstrstr(lpszStart, lpszOld)) != NULL) { int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen); memmove(lpszTarget + nReplacementLen, lpszTarget + nSourceLen, nBalance * sizeof(TCHAR)); memcpy(lpszTarget, lpszNew, nReplacementLen * sizeof(TCHAR)); lpszStart = lpszTarget + nReplacementLen; lpszStart[nBalance] = '\0'; nOldLength += (nReplacementLen - nSourceLen); } lpszStart += lstrlen(lpszStart) + 1; } ATLASSERT(m_pchData[nNewLength] == '\0'); GetData()->SetDataLength( nNewLength ); } return nCount; } inline int CString::Remove(TCHAR chRemove) { CopyBeforeWrite(); LPTSTR pstrSource = m_pchData; LPTSTR pstrDest = m_pchData; LPTSTR pstrEnd = m_pchData + GetData()->nDataLength; while (pstrSource < pstrEnd) { if (*pstrSource != chRemove) { *pstrDest = *pstrSource; pstrDest = ::CharNext(pstrDest); } pstrSource = ::CharNext(pstrSource); } *pstrDest = '\0'; int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest); GetData()->SetDataLength( GetData()->nDataLength - nCount ); return nCount; } #ifdef _UNICODE #define CHAR_FUDGE 1 // one TCHAR unused is good enough #else #define CHAR_FUDGE 2 // two BYTES unused for case of DBC last char #endif inline BOOL CString::LoadString(UINT nID) { // try fixed buffer first (to avoid wasting space in the heap) TCHAR szTemp[256]; int nCount = sizeof(szTemp) / sizeof(szTemp[0]); int nLen = _LoadString(nID, szTemp, nCount); if (nCount - nLen > CHAR_FUDGE) { *this = szTemp; return nLen > 0; } // try buffer size of 512, then larger size until entire string is retrieved int nSize = 256; do { nSize += 256; nLen = _LoadString(nID, GetBuffer(nSize - 1), nSize); } while (nSize - nLen <= CHAR_FUDGE); ReleaseBuffer(); return nLen > 0; } #ifndef _ATL_NO_COM inline BSTR CString::AllocSysString() const { #if defined(_UNICODE) || defined(OLE2ANSI) BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength); #else int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, NULL, NULL); BSTR bstr = ::SysAllocStringLen(NULL, nLen); if(bstr != NULL) MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen); #endif return bstr; } inline BSTR CString::SetSysString(BSTR* pbstr) const { #if defined(_UNICODE) || defined(OLE2ANSI) ::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength); #else int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, NULL, NULL); if(::SysReAllocStringLen(pbstr, NULL, nLen)) MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen); #endif ATLASSERT(*pbstr != NULL); return *pbstr; } #endif //!_ATL_NO_COM #endif //!_WTL_NO_CSTRING }; //namespace LBSTR #endif // __LOCAL_BSTR__