// // MODULE: APGTSSTR.CPP // // PURPOSE: implements DLL Growable string object CString // (pretty much a la MFC, but avoids all that MFC overhead) // // PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint // // COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com // // AUTHOR: Joe Mabel (reworked code from Microsoft's MFC sources) // // ORIGINAL DATE: 8-2-96 Roman Mach; totally re-implemented 1/15/99 Joe Mabel // // NOTES: // 1. As of 1/99, re-implemented based on MFC's implementation. Pared down // to what we use. // 2. This file modified 5/26/01 by Davide Massarenti from MS to remove reference counting // from CString implementation to resolve thread safety issue discovered on dual processor // systems when dll compiled for WinXP by MS compiler. // // Version Date By Comments //-------------------------------------------------------------------- // V0.1 - RM Original // V3.0 7-24-98 JM Abstracted this out as a separate header. // V3.1 1-15-99 JM Redo based on MFC implementation // #include "stdafx.h" #include #include "apgtsstr.h" #include "apgtsmfc.h" // Windows extensions to strings #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 ///////////////////////////////////////////////////////////////////////////// // static class data, special inlines // afxChNil is left for backward compatibility TCHAR afxChNil = '\0'; // For an empty string, m_pchData will point here // (note: avoids special case of checking for NULL m_pchData) // empty string data (and locked) // [BC - 20010529] - change below array from int to long and add extra 0 for padding // to avoid problems indicated by Davide Massarenti in 64-bit environments static long rgInitData[] = { -1, 0, 0, 0, 0 }; static CStringData* afxDataNil = (CStringData*)&rgInitData; static LPCTSTR afxPchNil = (LPCTSTR)(((BYTE*)&rgInitData)+sizeof(CStringData)); // special function to make afxEmptyString work even during initialization const CString& AfxGetEmptyString() { return *(CString*)&afxPchNil; } ////////////////////////////////////////////////////////////////////////////// // Construction/Destruction CString::CString() { Init(); } CString::CString(const CString& stringSrc) { // REMOVING REF COUNTING: ASSERT(stringSrc.GetData()->nRefs != 0); // REMOVING REF COUNTING: if (stringSrc.GetData()->nRefs >= 0) // REMOVING REF COUNTING: { // REMOVING REF COUNTING: ASSERT(stringSrc.GetData() != afxDataNil); // REMOVING REF COUNTING: m_pchData = stringSrc.m_pchData; // REMOVING REF COUNTING: InterlockedIncrement(&GetData()->nRefs); // REMOVING REF COUNTING: } // REMOVING REF COUNTING: else // REMOVING REF COUNTING: { Init(); *this = stringSrc.m_pchData; // REMOVING REF COUNTING: } } void CString::AllocBuffer(int nLen) // always allocate one extra character for '\0' termination // assumes [optimistically] that data length will equal allocation length { ASSERT(nLen >= 0); // MFC had the following assertion. I've killed it because we // don't have INT_MAX. JM 1/15/99 //ASSERT(nLen <= INT_MAX-1); // max size (enough room for 1 extra) if (nLen == 0) Init(); else { CStringData* pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)]; //[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool. if(pData) { pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; pData->nAllocLength = nLen; m_pchData = pData->data(); } } } void CString::Release() { if (GetData() != afxDataNil) { // REMOVING REF COUNTING: ASSERT(GetData()->nRefs != 0); // REMOVING REF COUNTING: if (InterlockedDecrement(&GetData()->nRefs) <= 0) delete[] (BYTE*)GetData(); Init(); } } void PASCAL CString::Release(CStringData* pData) { if (pData != afxDataNil) { // REMOVING REF COUNTING: ASSERT(pData->nRefs != 0); // REMOVING REF COUNTING: if (InterlockedDecrement(&pData->nRefs) <= 0) delete[] (BYTE*)pData; } } void CString::CopyBeforeWrite() { // REMOVING REF COUNTING: if (GetData()->nRefs > 1) // REMOVING REF COUNTING: { // REMOVING REF COUNTING: CStringData* pData = GetData(); // REMOVING REF COUNTING: Release(); // REMOVING REF COUNTING: AllocBuffer(pData->nDataLength); // REMOVING REF COUNTING: memcpy(m_pchData, pData->data(), (pData->nDataLength+1)*sizeof(TCHAR)); // REMOVING REF COUNTING: } // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1); } void CString::AllocBeforeWrite(int nLen) { // REMOVING REF COUNTING: if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) if (nLen > GetData()->nAllocLength) { Release(); AllocBuffer(nLen); } // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1); } CString::~CString() // free any attached data { if (GetData() != afxDataNil) { // REMOVING REF COUNTING: if (InterlockedDecrement(&GetData()->nRefs) <= 0) delete[] (BYTE*)GetData(); } } void CString::Empty() { if (GetData()->nDataLength == 0) return; // REMOVING REF COUNTING: if (GetData()->nRefs >= 0) Release(); // REMOVING REF COUNTING: else // REMOVING REF COUNTING: *this = &afxChNil; ASSERT(GetData()->nDataLength == 0); // REMOVING REF COUNTING: ASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0); } ////////////////////////////////////////////////////////////////////////////// // Helpers for the rest of the implementation 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 { dest.AllocBuffer(nNewLen); memcpy(dest.m_pchData, m_pchData+nCopyIndex, nCopyLen*sizeof(TCHAR)); } } ////////////////////////////////////////////////////////////////////////////// // More sophisticated construction CString::CString(LPCTSTR lpsz) { Init(); // Unlike MFC, no implicit LoadString offered - JM 1/15/99 int nLen = SafeStrlen(lpsz); if (nLen != 0) { AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } } ////////////////////////////////////////////////////////////////////////////// // 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". // void CString::AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) { AllocBeforeWrite(nSrcLen); memcpy(m_pchData, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength = nSrcLen; m_pchData[nSrcLen] = '\0'; } const CString& CString::operator=(const CString& stringSrc) { // REMOVING REF COUNTING: if (m_pchData != stringSrc.m_pchData) // REMOVING REF COUNTING: { // REMOVING REF COUNTING: if ((GetData()->nRefs < 0 && GetData() != afxDataNil) || // REMOVING REF COUNTING: stringSrc.GetData()->nRefs < 0) // REMOVING REF COUNTING: { // actual copy necessary since one of the strings is locked AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); // REMOVING REF COUNTING: } // REMOVING REF COUNTING: else // REMOVING REF COUNTING: { // REMOVING REF COUNTING: // can just copy references around // REMOVING REF COUNTING: Release(); // REMOVING REF COUNTING: ASSERT(stringSrc.GetData() != afxDataNil); // REMOVING REF COUNTING: m_pchData = stringSrc.m_pchData; // REMOVING REF COUNTING: InterlockedIncrement(&GetData()->nRefs); // REMOVING REF COUNTING: } // REMOVING REF COUNTING: } return *this; } const CString& CString::operator=(LPCTSTR lpsz) { // Suppress the following Assert from MFC - JM 1/15/99 // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); AssignCopy(SafeStrlen(lpsz), lpsz); return *this; } const CString& CString::operator=(TCHAR ch) { AssignCopy(1, &ch); return *this; } #ifdef _UNICODE const CString& CString::operator=(LPCSTR lpsz) { int nSrcLen = lpsz != NULL ? lstrlenA(lpsz) : 0; AllocBeforeWrite(nSrcLen); mbstowcs(m_pchData, lpsz, nSrcLen+1); ReleaseBuffer(); return *this; } #else //!_UNICODE const CString& CString::operator=(LPCWSTR lpsz) { int nSrcLen = lpsz != NULL ? wcslen(lpsz) : 0; AllocBeforeWrite(nSrcLen*2); wcstombs(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 // we (Saltmine) have switched away from friend because VC 6/0 doesn't like it. - JM 1/15/99 // we (Saltmine) do LPCTSTR but not TCHAR - JM 1/15/99 void CString::ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) { // -- master concatenation routine // Concatenate two sources // -- assume that 'this' is a new CString object int nNewLen = nSrc1Len + nSrc2Len; if (nNewLen != 0) { AllocBuffer(nNewLen); memcpy(m_pchData, lpszSrc1Data, nSrc1Len*sizeof(TCHAR)); memcpy(m_pchData+nSrc1Len, lpszSrc2Data, nSrc2Len*sizeof(TCHAR)); } } CString CString::operator+(const CString& string2) { CString s; s.ConcatCopy(GetData()->nDataLength, m_pchData, string2.GetData()->nDataLength, string2.m_pchData); return s; } CString CString::operator+(LPCTSTR lpsz) { // Suppress the following Assert from MFC - JM 1/15/99 // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); CString s; s.ConcatCopy(GetData()->nDataLength, m_pchData, CString::SafeStrlen(lpsz), lpsz); return s; } ////////////////////////////////////////////////////////////////////////////// // concatenate in place 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) // REMOVING REF COUNTING: if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) if (GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) { // we have to grow the buffer, use the ConcatCopy routine CStringData* pOldData = GetData(); ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData); ASSERT(pOldData != NULL); CString::Release(pOldData); } else { // fast concatenation when buffer big enough memcpy(m_pchData+GetData()->nDataLength, lpszSrcData, nSrcLen*sizeof(TCHAR)); GetData()->nDataLength += nSrcLen; ASSERT(GetData()->nDataLength <= GetData()->nAllocLength); m_pchData[GetData()->nDataLength] = '\0'; } } const CString& CString::operator+=(LPCTSTR lpsz) { // Suppress the following Assert from MFC - JM 1/15/99 // ASSERT(lpsz == NULL || AfxIsValidString(lpsz, FALSE)); ConcatInPlace(SafeStrlen(lpsz), lpsz); return *this; } const CString& CString::operator+=(const CString& string) { ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); return *this; } /////////////////////////////////////////////////////////////////////////////// // Advanced direct buffer access // CString::GetBuffer() and CString::ReleaseBuffer() calls should be matched. LPTSTR CString::GetBuffer(int nMinBufLength) { ASSERT(nMinBufLength >= 0); // REMOVING REF COUNTING: if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) if (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; AllocBuffer(nMinBufLength); memcpy(m_pchData, pOldData->data(), (nOldLen+1)*sizeof(TCHAR)); GetData()->nDataLength = nOldLen; CString::Release(pOldData); } // REMOVING REF COUNTING: ASSERT(GetData()->nRefs <= 1); // return a pointer to the character storage for this string ASSERT(m_pchData != NULL); return m_pchData; } // CString::GetBuffer() and CString::ReleaseBuffer() calls should be matched. void CString::ReleaseBuffer(int nNewLength /* = -1 */) { CopyBeforeWrite(); // just in case GetBuffer was not called if (nNewLength == -1) nNewLength = lstrlen(m_pchData); // zero terminated ASSERT(nNewLength <= GetData()->nAllocLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; } LPTSTR CString::GetBufferSetLength(int nNewLength) { ASSERT(nNewLength >= 0); GetBuffer(nNewLength); GetData()->nDataLength = nNewLength; m_pchData[nNewLength] = '\0'; return m_pchData; } /////////////////////////////////////////////////////////////////////////////// // Commonly used routines int CString::Find(TCHAR ch) const { // find first single character LPTSTR lpsz = _tcschr(m_pchData, (_TUCHAR)ch); // return -1 if not found and index otherwise return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); } void CString::MakeLower() { CopyBeforeWrite(); _tcslwr(m_pchData); } ////////////////////////////////////////////////////////////////////////////// // Very simple sub-string extraction CString CString::Mid(int nFirst) const { return Mid(nFirst, GetData()->nDataLength - nFirst); } 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; } 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; } 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; } ////////////////////////////////////////////////////////////////////////////// // Finding int CString::ReverseFind(TCHAR ch) const { // find last single character LPTSTR lpsz = _tcsrchr(m_pchData, (_TUCHAR)ch); // return -1 if not found, distance from beginning otherwise return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); } // find a sub-string (like strstr) int CString::Find(LPCTSTR lpszSub) const { // Suppress the following Assert from MFC - JM 1/15/99 // ASSERT(AfxIsValidString(lpszSub, FALSE)); // find first matching substring LPTSTR lpsz = _tcsstr(m_pchData, lpszSub); // return -1 for not found, distance from beginning otherwise return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); } // find a sub-string (like strstr). Added function - RAB19991112. int CString::Find(LPCTSTR lpszSub, int nStart) const { // Suppress the following Assert from MFC - RAB19991112. //ASSERT(AfxIsValidString(lpszSub)); int nLength = GetData()->nDataLength; if (nStart > nLength) return CString::FIND_FAILED; // find first matching substring LPTSTR lpsz = _tcsstr(m_pchData + nStart, lpszSub); // return -1 for not found, distance from beginning otherwise return (lpsz == NULL) ? CString::FIND_FAILED : (int)(lpsz - m_pchData); } ///////////////////////////////////////////////////////////////////////////// // CString formatting #ifdef _MAC #define TCHAR_ARG int #define WCHAR_ARG unsigned #define CHAR_ARG int #else #define TCHAR_ARG TCHAR #define WCHAR_ARG WCHAR #define CHAR_ARG char #endif #if defined(_68K_) || defined(_X86_) #define DOUBLE_ARG _AFX_DOUBLE #else #define DOUBLE_ARG double #endif #define FORCE_ANSI 0x10000 #define FORCE_UNICODE 0x20000 void CString::FormatV(LPCTSTR lpszFormat, va_list argList) { // Suppress the following Assert from MFC - JM 1/15/99 // ASSERT(AfxIsValidString(lpszFormat, FALSE)); 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 = _tcsinc(lpsz)) { // handle '%' character, but watch out for '%%' if (*lpsz != '%' || *(lpsz = _tcsinc(lpsz)) == '%') { nMaxLen += _tclen(lpsz); continue; } int nItemLen = 0; // handle '%' character with format int nWidth = 0; for (; *lpsz != '\0'; lpsz = _tcsinc(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' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz)) ; } ASSERT(nWidth >= 0); int nPrecision = 0; if (*lpsz == '.') { // skip past '.' separator (width.precision) lpsz = _tcsinc(lpsz); // get precision and skip it if (*lpsz == '*') { nPrecision = va_arg(argList, int); lpsz = _tcsinc(lpsz); } else { nPrecision = _ttoi(lpsz); for (; *lpsz != '\0' && _istdigit(*lpsz); lpsz = _tcsinc(lpsz)) ; } ASSERT(nPrecision >= 0); } // should be on type modifier or specifier int nModifier = 0; switch (*lpsz) { // modifiers that affect size case 'h': nModifier = FORCE_ANSI; lpsz = _tcsinc(lpsz); break; case 'l': nModifier = FORCE_UNICODE; lpsz = _tcsinc(lpsz); break; // modifiers that do not affect size case 'F': case 'N': case 'L': lpsz = _tcsinc(lpsz); break; } // now should be on specifier switch (*lpsz | nModifier) { // single characters case 'c': case 'C': nItemLen = 2; va_arg(argList, TCHAR_ARG); break; case 'c'|FORCE_ANSI: case 'C'|FORCE_ANSI: nItemLen = 2; va_arg(argList, CHAR_ARG); break; case 'c'|FORCE_UNICODE: case 'C'|FORCE_UNICODE: nItemLen = 2; va_arg(argList, WCHAR_ARG); 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 = 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; } #ifndef _MAC case 's'|FORCE_UNICODE: case 'S'|FORCE_UNICODE: { LPWSTR pstrNextArg = va_arg(argList, LPWSTR); if (pstrNextArg == NULL) nItemLen = 6; // "(null)" else { nItemLen = wcslen(pstrNextArg); nItemLen = max(1, nItemLen); } break; } #endif } // 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': va_arg(argList, int); nItemLen = 32; nItemLen = max(nItemLen, nWidth+nPrecision); break; #if 0 // We (Saltmine) are not currently supporting formatting of real numbers 1/15/99 case 'e': case 'f': case 'g': case 'G': va_arg(argList, DOUBLE_ARG); nItemLen = 128; nItemLen = max(nItemLen, nWidth+nPrecision); break; #endif case 'p': va_arg(argList, void*); nItemLen = 32; nItemLen = max(nItemLen, nWidth+nPrecision); break; // no output case 'n': va_arg(argList, int*); break; default: ASSERT(FALSE); // unknown formatting option } } // adjust nMaxLen for output nItemLen nMaxLen += nItemLen; } GetBuffer(nMaxLen); // Got rid of MFC's VERIFY in next line - JM 1/15/99 //VERIFY(_vstprintf(m_pchData, lpszFormat, argListSave) <= GetAllocLength()); _vstprintf(m_pchData, lpszFormat, argListSave); ReleaseBuffer(); va_end(argListSave); } // formatting (using wsprintf style formatting) void CString::Format(LPCTSTR lpszFormat, ...) { // Suppress the following Assert from MFC - JM 1/15/99 // ASSERT(AfxIsValidString(lpszFormat, FALSE)); va_list argList; va_start(argList, lpszFormat); FormatV(lpszFormat, argList); va_end(argList); } 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 (_istspace(*lpsz)) { if (lpszLast == NULL) lpszLast = lpsz; } else lpszLast = NULL; lpsz = _tcsinc(lpsz); } if (lpszLast != NULL) { // truncate at trailing space start *lpszLast = '\0'; GetData()->nDataLength = static_cast(lpszLast - m_pchData); } } void CString::TrimLeft() { CopyBeforeWrite(); // find first non-space character LPCTSTR lpsz = m_pchData; while (_istspace(*lpsz)) lpsz = _tcsinc(lpsz); // fix up data and length int nDataLength = GetData()->nDataLength - static_cast(lpsz - m_pchData); memmove(m_pchData, lpsz, (nDataLength+1)*sizeof(TCHAR)); GetData()->nDataLength = nDataLength; } BOOL CString::LoadString(UINT nID) { // try fixed buffer first (to avoid wasting space in the heap) TCHAR szTemp[256]; int nLen = ::AfxLoadString(nID, szTemp, _countof(szTemp)); if (_countof(szTemp) - 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 = ::AfxLoadString(nID, GetBuffer(nSize-1), nSize); } while (nSize - nLen <= CHAR_FUDGE); ReleaseBuffer(); return nLen > 0; } bool __stdcall operator ==(const CString& s1, const CString& s2) { return (s1.GetLength() == s2.GetLength() && ! _tcscmp((LPCTSTR)s1, (LPCTSTR)s2) ); } bool __stdcall operator ==(const CString& s1, LPCTSTR s2) { return (! _tcscmp((LPCTSTR)s1, s2) ); } bool __stdcall operator ==(LPCTSTR s1, const CString& s2) { return (! _tcscmp(s1, (LPCTSTR)s2) ); } bool __stdcall operator !=(const CString& s1, const CString& s2) { return (s1.GetLength() != s2.GetLength() || _tcscmp((LPCTSTR)s1, (LPCTSTR)s2) ); } bool __stdcall operator !=(const CString& s1, LPCTSTR s2) { return (_tcscmp((LPCTSTR)s1, s2) ) ? true : false; } bool __stdcall operator !=(LPCTSTR s1, const CString& s2) { return (_tcscmp(s1, (LPCTSTR)s2) ) ? true : false; } bool __stdcall operator < (const CString& s1, const CString& s2) { return (_tcscmp((LPCTSTR)s1, (LPCTSTR)s2) <0 ); } bool __stdcall operator < (const CString& s1, LPCTSTR s2) { return (_tcscmp((LPCTSTR)s1, s2) <0 ); } bool __stdcall operator < (LPCTSTR s1, const CString& s2) { return (_tcscmp(s1, (LPCTSTR)s2) <0 ); } CString operator+(LPCTSTR lpsz, const CString& string) { return CString(lpsz) + string; } void CString::Init() { m_pchData = afxDataNil->data(); } CStringData* CString::GetData() const { if(m_pchData != NULL) return ((CStringData*)m_pchData)-1; return afxDataNil; }