1 // Windows Template Library - WTL version 8.0
\r
2 // Copyright (C) Microsoft Corporation. All rights reserved.
\r
4 // This file is a part of the Windows Template Library.
\r
5 // The use and distribution terms for this software are covered by the
\r
6 // Common Public License 1.0 (http://opensource.org/osi3.0/licenses/cpl1.0.php)
\r
7 // which can be found in the file CPL.TXT at the root of this distribution.
\r
8 // By using this software in any fashion, you are agreeing to be bound by
\r
9 // the terms of this license. You must not remove this notice, or
\r
10 // any other, from this software.
\r
12 #ifndef __ATLMISC_H__
\r
13 #define __ATLMISC_H__
\r
18 #error ATL requires C++ compilation (use a .cpp suffix)
\r
21 #ifndef __ATLAPP_H__
\r
22 #error atlmisc.h requires atlapp.h to be included first
\r
26 #ifdef _ATL_TMP_NO_CSTRING
\r
27 #define _WTL_NO_CSTRING
\r
30 #if defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING)
\r
31 #error Conflicting options - both _WTL_USE_CSTRING and _WTL_NO_CSTRING are defined
\r
32 #endif // defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING)
\r
34 #if !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING)
\r
35 #define _WTL_USE_CSTRING
\r
36 #endif // !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING)
\r
38 #ifndef _WTL_NO_CSTRING
\r
39 #if defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
\r
40 #error Cannot use CString floating point formatting with _ATL_MIN_CRT defined
\r
41 #endif // defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
\r
42 #endif // !_WTL_NO_CSTRING
\r
45 ///////////////////////////////////////////////////////////////////////////////
\r
46 // Classes in this file:
\r
53 // CRecentDocumentListBase<T, t_cchItemLen, t_nFirstID, t_nLastID>
\r
54 // CRecentDocumentList
\r
57 // Global functions:
\r
58 // AtlLoadAccelerators()
\r
61 // AtlLoadSysBitmap()
\r
63 // AtlLoadSysCursor()
\r
66 // AtlLoadBitmapImage()
\r
67 // AtlLoadCursorImage()
\r
68 // AtlLoadIconImage()
\r
69 // AtlLoadSysBitmapImage()
\r
70 // AtlLoadSysCursorImage()
\r
71 // AtlLoadSysIconImage()
\r
75 // AtlGetStockBrush()
\r
76 // AtlGetStockFont()
\r
77 // AtlGetStockPalette()
\r
85 #ifndef _WTL_NO_WTYPES
\r
87 // forward declarations
\r
92 ///////////////////////////////////////////////////////////////////////////////
\r
93 // CSize - Wrapper for Windows SIZE structure.
\r
95 class CSize : public SIZE
\r
105 CSize(int initCX, int initCY)
\r
111 CSize(SIZE initSize)
\r
113 *(SIZE*)this = initSize;
\r
116 CSize(POINT initPt)
\r
118 *(POINT*)this = initPt;
\r
121 CSize(DWORD dwSize)
\r
123 cx = (short)LOWORD(dwSize);
\r
124 cy = (short)HIWORD(dwSize);
\r
128 BOOL operator ==(SIZE size) const
\r
130 return (cx == size.cx && cy == size.cy);
\r
133 BOOL operator !=(SIZE size) const
\r
135 return (cx != size.cx || cy != size.cy);
\r
138 void operator +=(SIZE size)
\r
144 void operator -=(SIZE size)
\r
150 void SetSize(int CX, int CY)
\r
156 // Operators returning CSize values
\r
157 CSize operator +(SIZE size) const
\r
159 return CSize(cx + size.cx, cy + size.cy);
\r
162 CSize operator -(SIZE size) const
\r
164 return CSize(cx - size.cx, cy - size.cy);
\r
167 CSize operator -() const
\r
169 return CSize(-cx, -cy);
\r
172 // Operators returning CPoint values
\r
173 CPoint operator +(POINT point) const;
\r
174 CPoint operator -(POINT point) const;
\r
176 // Operators returning CRect values
\r
177 CRect operator +(const RECT* lpRect) const;
\r
178 CRect operator -(const RECT* lpRect) const;
\r
182 ///////////////////////////////////////////////////////////////////////////////
\r
183 // CPoint - Wrapper for Windows POINT structure.
\r
185 class CPoint : public POINT
\r
195 CPoint(int initX, int initY)
\r
201 CPoint(POINT initPt)
\r
203 *(POINT*)this = initPt;
\r
206 CPoint(SIZE initSize)
\r
208 *(SIZE*)this = initSize;
\r
211 CPoint(DWORD dwPoint)
\r
213 x = (short)LOWORD(dwPoint);
\r
214 y = (short)HIWORD(dwPoint);
\r
218 void Offset(int xOffset, int yOffset)
\r
224 void Offset(POINT point)
\r
230 void Offset(SIZE size)
\r
236 BOOL operator ==(POINT point) const
\r
238 return (x == point.x && y == point.y);
\r
241 BOOL operator !=(POINT point) const
\r
243 return (x != point.x || y != point.y);
\r
246 void operator +=(SIZE size)
\r
252 void operator -=(SIZE size)
\r
258 void operator +=(POINT point)
\r
264 void operator -=(POINT point)
\r
270 void SetPoint(int X, int Y)
\r
276 // Operators returning CPoint values
\r
277 CPoint operator +(SIZE size) const
\r
279 return CPoint(x + size.cx, y + size.cy);
\r
282 CPoint operator -(SIZE size) const
\r
284 return CPoint(x - size.cx, y - size.cy);
\r
287 CPoint operator -() const
\r
289 return CPoint(-x, -y);
\r
292 CPoint operator +(POINT point) const
\r
294 return CPoint(x + point.x, y + point.y);
\r
297 // Operators returning CSize values
\r
298 CSize operator -(POINT point) const
\r
300 return CSize(x - point.x, y - point.y);
\r
303 // Operators returning CRect values
\r
304 CRect operator +(const RECT* lpRect) const;
\r
305 CRect operator -(const RECT* lpRect) const;
\r
309 ///////////////////////////////////////////////////////////////////////////////
\r
310 // CRect - Wrapper for Windows RECT structure.
\r
312 class CRect : public RECT
\r
324 CRect(int l, int t, int r, int b)
\r
332 CRect(const RECT& srcRect)
\r
334 ::CopyRect(this, &srcRect);
\r
337 CRect(LPCRECT lpSrcRect)
\r
339 ::CopyRect(this, lpSrcRect);
\r
342 CRect(POINT point, SIZE size)
\r
344 right = (left = point.x) + size.cx;
\r
345 bottom = (top = point.y) + size.cy;
\r
348 CRect(POINT topLeft, POINT bottomRight)
\r
352 right = bottomRight.x;
\r
353 bottom = bottomRight.y;
\r
356 // Attributes (in addition to RECT members)
\r
359 return right - left;
\r
364 return bottom - top;
\r
369 return CSize(right - left, bottom - top);
\r
374 return *((CPoint*)this);
\r
377 CPoint& BottomRight()
\r
379 return *((CPoint*)this + 1);
\r
382 const CPoint& TopLeft() const
\r
384 return *((CPoint*)this);
\r
387 const CPoint& BottomRight() const
\r
389 return *((CPoint*)this + 1);
\r
392 CPoint CenterPoint() const
\r
394 return CPoint((left + right) / 2, (top + bottom) / 2);
\r
397 // convert between CRect and LPRECT/LPCRECT (no need for &)
\r
403 operator LPCRECT() const
\r
408 BOOL IsRectEmpty() const
\r
410 return ::IsRectEmpty(this);
\r
413 BOOL IsRectNull() const
\r
415 return (left == 0 && right == 0 && top == 0 && bottom == 0);
\r
418 BOOL PtInRect(POINT point) const
\r
420 return ::PtInRect(this, point);
\r
424 void SetRect(int x1, int y1, int x2, int y2)
\r
426 ::SetRect(this, x1, y1, x2, y2);
\r
429 void SetRect(POINT topLeft, POINT bottomRight)
\r
431 ::SetRect(this, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
\r
434 void SetRectEmpty()
\r
436 ::SetRectEmpty(this);
\r
439 void CopyRect(LPCRECT lpSrcRect)
\r
441 ::CopyRect(this, lpSrcRect);
\r
444 BOOL EqualRect(LPCRECT lpRect) const
\r
446 return ::EqualRect(this, lpRect);
\r
449 void InflateRect(int x, int y)
\r
451 ::InflateRect(this, x, y);
\r
454 void InflateRect(SIZE size)
\r
456 ::InflateRect(this, size.cx, size.cy);
\r
459 void InflateRect(LPCRECT lpRect)
\r
461 left -= lpRect->left;
\r
462 top -= lpRect->top;
\r
463 right += lpRect->right;
\r
464 bottom += lpRect->bottom;
\r
467 void InflateRect(int l, int t, int r, int b)
\r
475 void DeflateRect(int x, int y)
\r
477 ::InflateRect(this, -x, -y);
\r
480 void DeflateRect(SIZE size)
\r
482 ::InflateRect(this, -size.cx, -size.cy);
\r
485 void DeflateRect(LPCRECT lpRect)
\r
487 left += lpRect->left;
\r
488 top += lpRect->top;
\r
489 right -= lpRect->right;
\r
490 bottom -= lpRect->bottom;
\r
493 void DeflateRect(int l, int t, int r, int b)
\r
501 void OffsetRect(int x, int y)
\r
503 ::OffsetRect(this, x, y);
\r
505 void OffsetRect(SIZE size)
\r
507 ::OffsetRect(this, size.cx, size.cy);
\r
510 void OffsetRect(POINT point)
\r
512 ::OffsetRect(this, point.x, point.y);
\r
515 void NormalizeRect()
\r
532 // absolute position of rectangle
\r
533 void MoveToY(int y)
\r
535 bottom = Height() + y;
\r
539 void MoveToX(int x)
\r
541 right = Width() + x;
\r
545 void MoveToXY(int x, int y)
\r
551 void MoveToXY(POINT pt)
\r
557 // operations that fill '*this' with result
\r
558 BOOL IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2)
\r
560 return ::IntersectRect(this, lpRect1, lpRect2);
\r
563 BOOL UnionRect(LPCRECT lpRect1, LPCRECT lpRect2)
\r
565 return ::UnionRect(this, lpRect1, lpRect2);
\r
568 BOOL SubtractRect(LPCRECT lpRectSrc1, LPCRECT lpRectSrc2)
\r
570 return ::SubtractRect(this, lpRectSrc1, lpRectSrc2);
\r
573 // Additional Operations
\r
574 void operator =(const RECT& srcRect)
\r
576 ::CopyRect(this, &srcRect);
\r
579 BOOL operator ==(const RECT& rect) const
\r
581 return ::EqualRect(this, &rect);
\r
584 BOOL operator !=(const RECT& rect) const
\r
586 return !::EqualRect(this, &rect);
\r
589 void operator +=(POINT point)
\r
591 ::OffsetRect(this, point.x, point.y);
\r
594 void operator +=(SIZE size)
\r
596 ::OffsetRect(this, size.cx, size.cy);
\r
599 void operator +=(LPCRECT lpRect)
\r
601 InflateRect(lpRect);
\r
604 void operator -=(POINT point)
\r
606 ::OffsetRect(this, -point.x, -point.y);
\r
609 void operator -=(SIZE size)
\r
611 ::OffsetRect(this, -size.cx, -size.cy);
\r
614 void operator -=(LPCRECT lpRect)
\r
616 DeflateRect(lpRect);
\r
619 void operator &=(const RECT& rect)
\r
621 ::IntersectRect(this, this, &rect);
\r
624 void operator |=(const RECT& rect)
\r
626 ::UnionRect(this, this, &rect);
\r
629 // Operators returning CRect values
\r
630 CRect operator +(POINT pt) const
\r
633 ::OffsetRect(&rect, pt.x, pt.y);
\r
637 CRect operator -(POINT pt) const
\r
640 ::OffsetRect(&rect, -pt.x, -pt.y);
\r
644 CRect operator +(LPCRECT lpRect) const
\r
647 rect.InflateRect(lpRect);
\r
651 CRect operator +(SIZE size) const
\r
654 ::OffsetRect(&rect, size.cx, size.cy);
\r
658 CRect operator -(SIZE size) const
\r
661 ::OffsetRect(&rect, -size.cx, -size.cy);
\r
665 CRect operator -(LPCRECT lpRect) const
\r
668 rect.DeflateRect(lpRect);
\r
672 CRect operator &(const RECT& rect2) const
\r
675 ::IntersectRect(&rect, this, &rect2);
\r
679 CRect operator |(const RECT& rect2) const
\r
682 ::UnionRect(&rect, this, &rect2);
\r
686 CRect MulDiv(int nMultiplier, int nDivisor) const
\r
689 ::MulDiv(left, nMultiplier, nDivisor),
\r
690 ::MulDiv(top, nMultiplier, nDivisor),
\r
691 ::MulDiv(right, nMultiplier, nDivisor),
\r
692 ::MulDiv(bottom, nMultiplier, nDivisor));
\r
697 // CSize implementation
\r
699 inline CPoint CSize::operator +(POINT point) const
\r
700 { return CPoint(cx + point.x, cy + point.y); }
\r
702 inline CPoint CSize::operator -(POINT point) const
\r
703 { return CPoint(cx - point.x, cy - point.y); }
\r
705 inline CRect CSize::operator +(const RECT* lpRect) const
\r
706 { return CRect(lpRect) + *this; }
\r
708 inline CRect CSize::operator -(const RECT* lpRect) const
\r
709 { return CRect(lpRect) - *this; }
\r
712 // CPoint implementation
\r
714 inline CRect CPoint::operator +(const RECT* lpRect) const
\r
715 { return CRect(lpRect) + *this; }
\r
717 inline CRect CPoint::operator -(const RECT* lpRect) const
\r
718 { return CRect(lpRect) - *this; }
\r
720 #endif // !_WTL_NO_WTYPES
\r
723 // WTL::CSize or ATL::CSize scalar operators
\r
725 #if !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__))
\r
727 template <class Num>
\r
728 inline CSize operator *(SIZE s, Num n)
\r
730 return CSize((int)(s.cx * n), (int)(s.cy * n));
\r
733 template <class Num>
\r
734 inline void operator *=(SIZE & s, Num n)
\r
739 template <class Num>
\r
740 inline CSize operator /(SIZE s, Num n)
\r
742 return CSize((int)(s.cx / n), (int)(s.cy / n));
\r
745 template <class Num>
\r
746 inline void operator /=(SIZE & s, Num n)
\r
751 #endif // !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__))
\r
754 ///////////////////////////////////////////////////////////////////////////////
\r
755 // CString - String class
\r
757 #ifndef _WTL_NO_CSTRING
\r
761 long nRefs; // reference count
\r
764 // TCHAR data[nAllocLength]
\r
767 { return (TCHAR*)(this + 1); }
\r
772 // For an empty string, m_pchData will point here
\r
773 // (note: avoids special case of checking for NULL m_pchData)
\r
774 // empty string data (and locked)
\r
775 _declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 };
\r
776 _declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData;
\r
777 _declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData));
\r
789 CString(const CString& stringSrc)
\r
791 ATLASSERT(stringSrc.GetData()->nRefs != 0);
\r
792 if (stringSrc.GetData()->nRefs >= 0)
\r
794 ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
\r
795 m_pchData = stringSrc.m_pchData;
\r
796 InterlockedIncrement(&GetData()->nRefs);
\r
801 *this = stringSrc.m_pchData;
\r
805 CString(TCHAR ch, int nRepeat = 1)
\r
807 ATLASSERT(!_istlead(ch)); // can't create a lead byte string
\r
811 if(AllocBuffer(nRepeat))
\r
814 for (int i = 0; i < nRepeat; i++)
\r
817 memset(m_pchData, ch, nRepeat);
\r
823 CString(LPCTSTR lpsz)
\r
826 if (lpsz != NULL && HIWORD(lpsz) == NULL)
\r
828 UINT nID = LOWORD((DWORD_PTR)lpsz);
\r
829 if (!LoadString(nID))
\r
830 ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID);
\r
834 int nLen = SafeStrlen(lpsz);
\r
837 if(AllocBuffer(nLen))
\r
838 SecureHelper::memcpy_x(m_pchData, (nLen + 1) * sizeof(TCHAR), lpsz, nLen * sizeof(TCHAR));
\r
844 CString(LPCSTR lpsz)
\r
847 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
\r
848 int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0;
\r
850 int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0;
\r
854 if(AllocBuffer(nSrcLen))
\r
856 _mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
\r
862 CString(LPCWSTR lpsz)
\r
865 int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0;
\r
868 if(AllocBuffer(nSrcLen * 2))
\r
870 _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
\r
875 #endif // !_UNICODE
\r
877 CString(LPCTSTR lpch, int nLength)
\r
882 if(AllocBuffer(nLength))
\r
883 SecureHelper::memcpy_x(m_pchData, (nLength + 1) * sizeof(TCHAR), lpch, nLength * sizeof(TCHAR));
\r
888 CString(LPCSTR lpsz, int nLength)
\r
893 if(AllocBuffer(nLength))
\r
895 int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength + 1);
\r
896 ReleaseBuffer((n >= 0) ? n : -1);
\r
901 CString(LPCWSTR lpsz, int nLength)
\r
906 if(((nLength * 2) > nLength) && AllocBuffer(nLength * 2))
\r
908 int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData, (nLength * 2) + 1, NULL, NULL);
\r
909 ReleaseBuffer((n >= 0) ? n : -1);
\r
913 #endif // !_UNICODE
\r
915 CString(const unsigned char* lpsz)
\r
918 *this = (LPCSTR)lpsz;
\r
921 // Attributes & Operations
\r
922 int GetLength() const // as an array of characters
\r
924 return GetData()->nDataLength;
\r
927 BOOL IsEmpty() const
\r
929 return GetData()->nDataLength == 0;
\r
932 void Empty() // free up the data
\r
934 if (GetData()->nDataLength == 0)
\r
937 if (GetData()->nRefs >= 0)
\r
942 ATLASSERT(GetData()->nDataLength == 0);
\r
943 ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
\r
946 TCHAR GetAt(int nIndex) const // 0 based
\r
948 ATLASSERT(nIndex >= 0);
\r
949 ATLASSERT(nIndex < GetData()->nDataLength);
\r
950 return m_pchData[nIndex];
\r
953 TCHAR operator [](int nIndex) const // same as GetAt
\r
956 ATLASSERT(nIndex >= 0);
\r
957 ATLASSERT(nIndex < GetData()->nDataLength);
\r
958 return m_pchData[nIndex];
\r
961 void SetAt(int nIndex, TCHAR ch)
\r
963 ATLASSERT(nIndex >= 0);
\r
964 ATLASSERT(nIndex < GetData()->nDataLength);
\r
967 m_pchData[nIndex] = ch;
\r
970 operator LPCTSTR() const // as a C string
\r
975 // overloaded assignment
\r
976 CString& operator =(const CString& stringSrc)
\r
978 if (m_pchData != stringSrc.m_pchData)
\r
980 if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0)
\r
982 // actual copy necessary since one of the strings is locked
\r
983 AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
\r
987 // can just copy references around
\r
989 ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
\r
990 m_pchData = stringSrc.m_pchData;
\r
991 InterlockedIncrement(&GetData()->nRefs);
\r
997 CString& operator =(TCHAR ch)
\r
999 ATLASSERT(!_istlead(ch)); // can't set single lead byte
\r
1000 AssignCopy(1, &ch);
\r
1005 CString& operator =(char ch)
\r
1007 *this = (TCHAR)ch;
\r
1012 CString& operator =(LPCTSTR lpsz)
\r
1014 ATLASSERT(lpsz == NULL || _IsValidString(lpsz));
\r
1015 AssignCopy(SafeStrlen(lpsz), lpsz);
\r
1020 CString& operator =(LPCSTR lpsz)
\r
1022 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
\r
1023 int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0;
\r
1025 int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0;
\r
1027 if(AllocBeforeWrite(nSrcLen))
\r
1029 _mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
\r
1034 #else // !_UNICODE
\r
1035 CString& operator =(LPCWSTR lpsz)
\r
1037 int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0;
\r
1038 if(AllocBeforeWrite(nSrcLen * 2))
\r
1040 _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
\r
1045 #endif // !_UNICODE
\r
1047 CString& operator =(const unsigned char* lpsz)
\r
1049 *this = (LPCSTR)lpsz;
\r
1053 // string concatenation
\r
1054 CString& operator +=(const CString& string)
\r
1056 ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
\r
1060 CString& operator +=(TCHAR ch)
\r
1062 ConcatInPlace(1, &ch);
\r
1067 CString& operator +=(char ch)
\r
1069 *this += (TCHAR)ch;
\r
1074 CString& operator +=(LPCTSTR lpsz)
\r
1076 ATLASSERT(lpsz == NULL || _IsValidString(lpsz));
\r
1077 ConcatInPlace(SafeStrlen(lpsz), lpsz);
\r
1081 friend CString __stdcall operator +(const CString& string1, const CString& string2);
\r
1082 friend CString __stdcall operator +(const CString& string, TCHAR ch);
\r
1083 friend CString __stdcall operator +(TCHAR ch, const CString& string);
\r
1085 friend CString __stdcall operator +(const CString& string, char ch);
\r
1086 friend CString __stdcall operator +(char ch, const CString& string);
\r
1088 friend CString __stdcall operator +(const CString& string, LPCTSTR lpsz);
\r
1089 friend CString __stdcall operator +(LPCTSTR lpsz, const CString& string);
\r
1091 // string comparison
\r
1092 int Compare(LPCTSTR lpsz) const // straight character (MBCS/Unicode aware)
\r
1094 return _cstrcmp(m_pchData, lpsz);
\r
1097 int CompareNoCase(LPCTSTR lpsz) const // ignore case (MBCS/Unicode aware)
\r
1099 return _cstrcmpi(m_pchData, lpsz);
\r
1102 #ifndef _WIN32_WCE
\r
1103 // CString::Collate is often slower than Compare but is MBSC/Unicode
\r
1104 // aware as well as locale-sensitive with respect to sort order.
\r
1105 int Collate(LPCTSTR lpsz) const // NLS aware
\r
1107 return _cstrcoll(m_pchData, lpsz);
\r
1110 int CollateNoCase(LPCTSTR lpsz) const // ignore case
\r
1112 return _cstrcolli(m_pchData, lpsz);
\r
1114 #endif // !_WIN32_WCE
\r
1116 // simple sub-string extraction
\r
1117 CString Mid(int nFirst, int nCount) const
\r
1119 // out-of-bounds requests return sensible things
\r
1125 if (nFirst + nCount > GetData()->nDataLength)
\r
1126 nCount = GetData()->nDataLength - nFirst;
\r
1127 if (nFirst > GetData()->nDataLength)
\r
1131 AllocCopy(dest, nCount, nFirst, 0);
\r
1135 CString Mid(int nFirst) const
\r
1137 return Mid(nFirst, GetData()->nDataLength - nFirst);
\r
1140 CString Left(int nCount) const
\r
1144 else if (nCount > GetData()->nDataLength)
\r
1145 nCount = GetData()->nDataLength;
\r
1148 AllocCopy(dest, nCount, 0, 0);
\r
1152 CString Right(int nCount) const
\r
1156 else if (nCount > GetData()->nDataLength)
\r
1157 nCount = GetData()->nDataLength;
\r
1160 AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
\r
1164 CString SpanIncluding(LPCTSTR lpszCharSet) const // strspn equivalent
\r
1166 ATLASSERT(_IsValidString(lpszCharSet));
\r
1167 return Left(_cstrspn(m_pchData, lpszCharSet));
\r
1170 CString SpanExcluding(LPCTSTR lpszCharSet) const // strcspn equivalent
\r
1172 ATLASSERT(_IsValidString(lpszCharSet));
\r
1173 return Left(_cstrcspn(m_pchData, lpszCharSet));
\r
1176 // upper/lower/reverse conversion
\r
1179 CopyBeforeWrite();
\r
1180 CharUpper(m_pchData);
\r
1185 CopyBeforeWrite();
\r
1186 CharLower(m_pchData);
\r
1189 void MakeReverse()
\r
1191 CopyBeforeWrite();
\r
1192 _cstrrev(m_pchData);
\r
1195 // trimming whitespace (either side)
\r
1198 CopyBeforeWrite();
\r
1200 // find beginning of trailing spaces by starting at beginning (DBCS aware)
\r
1201 LPTSTR lpsz = m_pchData;
\r
1202 LPTSTR lpszLast = NULL;
\r
1203 while (*lpsz != _T('\0'))
\r
1205 if (_cstrisspace(*lpsz))
\r
1207 if (lpszLast == NULL)
\r
1214 lpsz = ::CharNext(lpsz);
\r
1217 if (lpszLast != NULL)
\r
1219 // truncate at trailing space start
\r
1220 *lpszLast = _T('\0');
\r
1221 GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
\r
1227 CopyBeforeWrite();
\r
1229 // find first non-space character
\r
1230 LPCTSTR lpsz = m_pchData;
\r
1231 while (_cstrisspace(*lpsz))
\r
1232 lpsz = ::CharNext(lpsz);
\r
1234 // fix up data and length
\r
1235 int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
\r
1236 SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
\r
1237 GetData()->nDataLength = nDataLength;
\r
1240 // remove continuous occurrences of chTarget starting from right
\r
1241 void TrimRight(TCHAR chTarget)
\r
1243 // find beginning of trailing matches
\r
1244 // by starting at beginning (DBCS aware)
\r
1246 CopyBeforeWrite();
\r
1247 LPTSTR lpsz = m_pchData;
\r
1248 LPTSTR lpszLast = NULL;
\r
1250 while (*lpsz != _T('\0'))
\r
1252 if (*lpsz == chTarget)
\r
1254 if (lpszLast == NULL)
\r
1259 lpsz = ::CharNext(lpsz);
\r
1262 if (lpszLast != NULL)
\r
1264 // truncate at left-most matching character
\r
1265 *lpszLast = _T('\0');
\r
1266 GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
\r
1270 // remove continuous occcurrences of characters in passed string, starting from right
\r
1271 void TrimRight(LPCTSTR lpszTargetList)
\r
1273 // find beginning of trailing matches by starting at beginning (DBCS aware)
\r
1275 CopyBeforeWrite();
\r
1276 LPTSTR lpsz = m_pchData;
\r
1277 LPTSTR lpszLast = NULL;
\r
1279 while (*lpsz != _T('\0'))
\r
1281 TCHAR* pNext = ::CharNext(lpsz);
\r
1282 if(pNext > lpsz + 1)
\r
1284 if (_cstrchr_db(lpszTargetList, *lpsz, *(lpsz + 1)) != NULL)
\r
1286 if (lpszLast == NULL)
\r
1296 if (_cstrchr(lpszTargetList, *lpsz) != NULL)
\r
1298 if (lpszLast == NULL)
\r
1310 if (lpszLast != NULL)
\r
1312 // truncate at left-most matching character
\r
1313 *lpszLast = _T('\0');
\r
1314 GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
\r
1318 // remove continuous occurrences of chTarget starting from left
\r
1319 void TrimLeft(TCHAR chTarget)
\r
1321 // find first non-matching character
\r
1323 CopyBeforeWrite();
\r
1324 LPCTSTR lpsz = m_pchData;
\r
1326 while (chTarget == *lpsz)
\r
1327 lpsz = ::CharNext(lpsz);
\r
1329 if (lpsz != m_pchData)
\r
1331 // fix up data and length
\r
1332 int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
\r
1333 SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
\r
1334 GetData()->nDataLength = nDataLength;
\r
1338 // remove continuous occcurrences of characters in passed string, starting from left
\r
1339 void TrimLeft(LPCTSTR lpszTargets)
\r
1341 // if we're not trimming anything, we're not doing any work
\r
1342 if (SafeStrlen(lpszTargets) == 0)
\r
1345 CopyBeforeWrite();
\r
1346 LPCTSTR lpsz = m_pchData;
\r
1348 while (*lpsz != _T('\0'))
\r
1350 TCHAR* pNext = ::CharNext(lpsz);
\r
1351 if(pNext > lpsz + 1)
\r
1353 if (_cstrchr_db(lpszTargets, *lpsz, *(lpsz + 1)) == NULL)
\r
1358 if (_cstrchr(lpszTargets, *lpsz) == NULL)
\r
1364 if (lpsz != m_pchData)
\r
1366 // fix up data and length
\r
1367 int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
\r
1368 SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
\r
1369 GetData()->nDataLength = nDataLength;
\r
1373 // advanced manipulation
\r
1374 // replace occurrences of chOld with chNew
\r
1375 int Replace(TCHAR chOld, TCHAR chNew)
\r
1379 // short-circuit the nop case
\r
1380 if (chOld != chNew)
\r
1382 // otherwise modify each character that matches in the string
\r
1383 CopyBeforeWrite();
\r
1384 LPTSTR psz = m_pchData;
\r
1385 LPTSTR pszEnd = psz + GetData()->nDataLength;
\r
1386 while (psz < pszEnd)
\r
1388 // replace instances of the specified character only
\r
1389 if (*psz == chOld)
\r
1394 psz = ::CharNext(psz);
\r
1400 // replace occurrences of substring lpszOld with lpszNew;
\r
1401 // empty lpszNew removes instances of lpszOld
\r
1402 int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
\r
1404 // can't have empty or NULL lpszOld
\r
1406 int nSourceLen = SafeStrlen(lpszOld);
\r
1407 if (nSourceLen == 0)
\r
1409 int nReplacementLen = SafeStrlen(lpszNew);
\r
1411 // loop once to figure out the size of the result string
\r
1413 LPTSTR lpszStart = m_pchData;
\r
1414 LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
\r
1415 LPTSTR lpszTarget = NULL;
\r
1416 while (lpszStart < lpszEnd)
\r
1418 while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL)
\r
1421 lpszStart = lpszTarget + nSourceLen;
\r
1423 lpszStart += lstrlen(lpszStart) + 1;
\r
1426 // if any changes were made, make them
\r
1429 CopyBeforeWrite();
\r
1431 // if the buffer is too small, just allocate a new buffer (slow but sure)
\r
1432 int nOldLength = GetData()->nDataLength;
\r
1433 int nNewLength = nOldLength + (nReplacementLen - nSourceLen) * nCount;
\r
1434 if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
\r
1436 CStringData* pOldData = GetData();
\r
1437 LPTSTR pstr = m_pchData;
\r
1438 if(!AllocBuffer(nNewLength))
\r
1440 SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, pOldData->nDataLength * sizeof(TCHAR));
\r
1441 CString::Release(pOldData);
\r
1443 // else, we just do it in-place
\r
1444 lpszStart = m_pchData;
\r
1445 lpszEnd = m_pchData + GetData()->nDataLength;
\r
1447 // loop again to actually do the work
\r
1448 while (lpszStart < lpszEnd)
\r
1450 while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL)
\r
1452 int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen);
\r
1453 int cchBuffLen = GetData()->nAllocLength - (int)(DWORD_PTR)(lpszTarget - m_pchData);
\r
1454 SecureHelper::memmove_x(lpszTarget + nReplacementLen, (cchBuffLen - nReplacementLen + 1) * sizeof(TCHAR), lpszTarget + nSourceLen, nBalance * sizeof(TCHAR));
\r
1455 SecureHelper::memcpy_x(lpszTarget, (cchBuffLen + 1) * sizeof(TCHAR), lpszNew, nReplacementLen * sizeof(TCHAR));
\r
1456 lpszStart = lpszTarget + nReplacementLen;
\r
1457 lpszStart[nBalance] = _T('\0');
\r
1458 nOldLength += (nReplacementLen - nSourceLen);
\r
1460 lpszStart += lstrlen(lpszStart) + 1;
\r
1462 ATLASSERT(m_pchData[nNewLength] == _T('\0'));
\r
1463 GetData()->nDataLength = nNewLength;
\r
1469 // remove occurrences of chRemove
\r
1470 int Remove(TCHAR chRemove)
\r
1472 CopyBeforeWrite();
\r
1474 LPTSTR pstrSource = m_pchData;
\r
1475 LPTSTR pstrDest = m_pchData;
\r
1476 LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
\r
1478 while (pstrSource < pstrEnd)
\r
1480 if (*pstrSource != chRemove)
\r
1482 *pstrDest = *pstrSource;
\r
1483 pstrDest = ::CharNext(pstrDest);
\r
1485 pstrSource = ::CharNext(pstrSource);
\r
1487 *pstrDest = _T('\0');
\r
1488 int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest);
\r
1489 GetData()->nDataLength -= nCount;
\r
1494 // insert character at zero-based index; concatenates if index is past end of string
\r
1495 int Insert(int nIndex, TCHAR ch)
\r
1497 CopyBeforeWrite();
\r
1502 int nNewLength = GetData()->nDataLength;
\r
1503 if (nIndex > nNewLength)
\r
1504 nIndex = nNewLength;
\r
1507 if (GetData()->nAllocLength < nNewLength)
\r
1509 CStringData* pOldData = GetData();
\r
1510 LPTSTR pstr = m_pchData;
\r
1511 if(!AllocBuffer(nNewLength))
\r
1513 SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
\r
1514 CString::Release(pOldData);
\r
1517 // move existing bytes down
\r
1518 SecureHelper::memmove_x(m_pchData + nIndex + 1, (GetData()->nAllocLength - nIndex) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR));
\r
1519 m_pchData[nIndex] = ch;
\r
1520 GetData()->nDataLength = nNewLength;
\r
1522 return nNewLength;
\r
1525 // insert substring at zero-based index; concatenates if index is past end of string
\r
1526 int Insert(int nIndex, LPCTSTR pstr)
\r
1531 int nInsertLength = SafeStrlen(pstr);
\r
1532 int nNewLength = GetData()->nDataLength;
\r
1533 if (nInsertLength > 0)
\r
1535 CopyBeforeWrite();
\r
1536 if (nIndex > nNewLength)
\r
1537 nIndex = nNewLength;
\r
1538 nNewLength += nInsertLength;
\r
1540 if (GetData()->nAllocLength < nNewLength)
\r
1542 CStringData* pOldData = GetData();
\r
1543 LPTSTR pstr = m_pchData;
\r
1544 if(!AllocBuffer(nNewLength))
\r
1546 SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
\r
1547 CString::Release(pOldData);
\r
1550 // move existing bytes down
\r
1551 SecureHelper::memmove_x(m_pchData + nIndex + nInsertLength, (GetData()->nAllocLength + 1 - nIndex - nInsertLength) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR));
\r
1552 SecureHelper::memcpy_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), pstr, nInsertLength * sizeof(TCHAR));
\r
1553 GetData()->nDataLength = nNewLength;
\r
1556 return nNewLength;
\r
1559 // delete nCount characters starting at zero-based index
\r
1560 int Delete(int nIndex, int nCount = 1)
\r
1564 int nLength = GetData()->nDataLength;
\r
1565 if (nCount > 0 && nIndex < nLength)
\r
1567 if((nIndex + nCount) > nLength)
\r
1568 nCount = nLength - nIndex;
\r
1569 CopyBeforeWrite();
\r
1570 int nBytesToCopy = nLength - (nIndex + nCount) + 1;
\r
1572 SecureHelper::memmove_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
\r
1573 nLength -= nCount;
\r
1574 GetData()->nDataLength = nLength;
\r
1580 // searching (return starting index, or -1 if not found)
\r
1581 // look for a single character match
\r
1582 int Find(TCHAR ch) const // like "C" strchr
\r
1584 return Find(ch, 0);
\r
1587 int ReverseFind(TCHAR ch) const
\r
1589 // find last single character
\r
1590 LPCTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch);
\r
1592 // return -1 if not found, distance from beginning otherwise
\r
1593 return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
\r
1596 int Find(TCHAR ch, int nStart) const // starting at index
\r
1598 int nLength = GetData()->nDataLength;
\r
1599 if (nStart < 0 || nStart >= nLength)
\r
1602 // find first single character
\r
1603 LPCTSTR lpsz = _cstrchr(m_pchData + nStart, (_TUCHAR)ch);
\r
1605 // return -1 if not found and index otherwise
\r
1606 return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
\r
1609 int FindOneOf(LPCTSTR lpszCharSet) const
\r
1611 ATLASSERT(_IsValidString(lpszCharSet));
\r
1612 LPCTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet);
\r
1613 return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
\r
1616 // look for a specific sub-string
\r
1617 // find a sub-string (like strstr)
\r
1618 int Find(LPCTSTR lpszSub) const // like "C" strstr
\r
1620 return Find(lpszSub, 0);
\r
1623 int Find(LPCTSTR lpszSub, int nStart) const // starting at index
\r
1625 ATLASSERT(_IsValidString(lpszSub));
\r
1627 int nLength = GetData()->nDataLength;
\r
1628 if (nStart < 0 || nStart > nLength)
\r
1631 // find first matching substring
\r
1632 LPCTSTR lpsz = _cstrstr(m_pchData + nStart, lpszSub);
\r
1634 // return -1 for not found, distance from beginning otherwise
\r
1635 return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
\r
1638 // Concatentation for non strings
\r
1639 CString& Append(int n)
\r
1641 const int cchBuff = 12;
\r
1642 TCHAR szBuffer[cchBuff] = { 0 };
\r
1643 SecureHelper::wsprintf_x(szBuffer, cchBuff, _T("%d"), n);
\r
1644 ConcatInPlace(SafeStrlen(szBuffer), szBuffer);
\r
1648 // simple formatting
\r
1649 // formatting (using wsprintf style formatting)
\r
1650 BOOL __cdecl Format(LPCTSTR lpszFormat, ...)
\r
1652 ATLASSERT(_IsValidString(lpszFormat));
\r
1655 va_start(argList, lpszFormat);
\r
1656 BOOL bRet = FormatV(lpszFormat, argList);
\r
1661 BOOL __cdecl Format(UINT nFormatID, ...)
\r
1663 CString strFormat;
\r
1664 BOOL bRet = strFormat.LoadString(nFormatID);
\r
1665 ATLASSERT(bRet != 0);
\r
1668 va_start(argList, nFormatID);
\r
1669 bRet = FormatV(strFormat, argList);
\r
1674 BOOL FormatV(LPCTSTR lpszFormat, va_list argList)
\r
1676 ATLASSERT(_IsValidString(lpszFormat));
\r
1678 enum _FormatModifiers
\r
1680 FORCE_ANSI = 0x10000,
\r
1681 FORCE_UNICODE = 0x20000,
\r
1682 FORCE_INT64 = 0x40000
\r
1685 va_list argListSave = argList;
\r
1687 // make a guess at the maximum length of the resulting string
\r
1689 for (LPCTSTR lpsz = lpszFormat; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz))
\r
1691 // handle '%' character, but watch out for '%%'
\r
1692 if (*lpsz != _T('%') || *(lpsz = ::CharNext(lpsz)) == _T('%'))
\r
1694 nMaxLen += (int)(::CharNext(lpsz) - lpsz);
\r
1700 // handle '%' character with format
\r
1702 for (; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz))
\r
1704 // check for valid flags
\r
1705 if (*lpsz == _T('#'))
\r
1706 nMaxLen += 2; // for '0x'
\r
1707 else if (*lpsz == _T('*'))
\r
1708 nWidth = va_arg(argList, int);
\r
1709 else if (*lpsz == _T('-') || *lpsz == _T('+') || *lpsz == _T('0') || *lpsz == _T(' '))
\r
1711 else // hit non-flag character
\r
1714 // get width and skip it
\r
1717 // width indicated by
\r
1718 nWidth = _cstrtoi(lpsz);
\r
1719 for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
\r
1722 ATLASSERT(nWidth >= 0);
\r
1724 int nPrecision = 0;
\r
1725 if (*lpsz == _T('.'))
\r
1727 // skip past '.' separator (width.precision)
\r
1728 lpsz = ::CharNext(lpsz);
\r
1730 // get precision and skip it
\r
1731 if (*lpsz == _T('*'))
\r
1733 nPrecision = va_arg(argList, int);
\r
1734 lpsz = ::CharNext(lpsz);
\r
1738 nPrecision = _cstrtoi(lpsz);
\r
1739 for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
\r
1742 ATLASSERT(nPrecision >= 0);
\r
1745 // should be on type modifier or specifier
\r
1746 int nModifier = 0;
\r
1747 if(lpsz[0] == _T('I') && lpsz[1] == _T('6') && lpsz[2] == _T('4'))
\r
1750 nModifier = FORCE_INT64;
\r
1756 // modifiers that affect size
\r
1758 nModifier = FORCE_ANSI;
\r
1759 lpsz = ::CharNext(lpsz);
\r
1762 nModifier = FORCE_UNICODE;
\r
1763 lpsz = ::CharNext(lpsz);
\r
1766 // modifiers that do not affect size
\r
1770 lpsz = ::CharNext(lpsz);
\r
1775 // now should be on specifier
\r
1776 switch (*lpsz | nModifier)
\r
1778 // single characters
\r
1782 va_arg(argList, TCHAR);
\r
1784 case _T('c') | FORCE_ANSI:
\r
1785 case _T('C') | FORCE_ANSI:
\r
1787 va_arg(argList, char);
\r
1789 case _T('c') | FORCE_UNICODE:
\r
1790 case _T('C') | FORCE_UNICODE:
\r
1792 va_arg(argList, WCHAR);
\r
1798 LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
\r
1799 if (pstrNextArg == NULL)
\r
1801 nItemLen = 6; // "(null)"
\r
1805 nItemLen = lstrlen(pstrNextArg);
\r
1806 nItemLen = max(1, nItemLen);
\r
1814 LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
\r
1815 if (pstrNextArg == NULL)
\r
1817 nItemLen = 6; // "(null)"
\r
1821 nItemLen = (int)wcslen(pstrNextArg);
\r
1822 nItemLen = max(1, nItemLen);
\r
1825 LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
\r
1826 if (pstrNextArg == NULL)
\r
1828 nItemLen = 6; // "(null)"
\r
1832 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
\r
1833 nItemLen = ATL::lstrlenA(pstrNextArg);
\r
1835 nItemLen = lstrlenA(pstrNextArg);
\r
1837 nItemLen = max(1, nItemLen);
\r
1839 #endif // _UNICODE
\r
1843 case _T('s') | FORCE_ANSI:
\r
1844 case _T('S') | FORCE_ANSI:
\r
1846 LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
\r
1847 if (pstrNextArg == NULL)
\r
1849 nItemLen = 6; // "(null)"
\r
1853 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
\r
1854 nItemLen = ATL::lstrlenA(pstrNextArg);
\r
1856 nItemLen = lstrlenA(pstrNextArg);
\r
1858 nItemLen = max(1, nItemLen);
\r
1863 case _T('s') | FORCE_UNICODE:
\r
1864 case _T('S') | FORCE_UNICODE:
\r
1866 LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
\r
1867 if (pstrNextArg == NULL)
\r
1869 nItemLen = 6; // "(null)"
\r
1873 nItemLen = (int)wcslen(pstrNextArg);
\r
1874 nItemLen = max(1, nItemLen);
\r
1880 // adjust nItemLen for strings
\r
1881 if (nItemLen != 0)
\r
1883 nItemLen = max(nItemLen, nWidth);
\r
1884 if (nPrecision != 0)
\r
1885 nItemLen = min(nItemLen, nPrecision);
\r
1898 if (nModifier & FORCE_INT64)
\r
1899 va_arg(argList, __int64);
\r
1901 va_arg(argList, int);
\r
1903 nItemLen = max(nItemLen, nWidth + nPrecision);
\r
1906 #ifndef _ATL_USE_CSTRING_FLOAT
\r
1912 ATLASSERT(!"Floating point (%%e, %%E, %%f, %%g, and %%G) is not supported by the WTL::CString class.");
\r
1914 ::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class."));
\r
1915 #ifndef _WIN32_WCE
\r
1917 #else // CE specific
\r
1919 #endif // _WIN32_WCE
\r
1922 #else // _ATL_USE_CSTRING_FLOAT
\r
1927 va_arg(argList, double);
\r
1929 nItemLen = max(nItemLen, nWidth + nPrecision);
\r
1933 double f = va_arg(argList, double);
\r
1934 // 312 == strlen("-1+(309 zeroes).")
\r
1935 // 309 zeroes == max precision of a double
\r
1936 // 6 == adjustment in case precision is not specified,
\r
1937 // which means that the precision defaults to 6
\r
1938 int cchLen = max(nWidth, 312 + nPrecision + 6);
\r
1939 CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
\r
1940 LPTSTR pszTemp = buff.Allocate(cchLen);
\r
1941 if(pszTemp != NULL)
\r
1943 SecureHelper::sprintf_x(pszTemp, cchLen, _T("%*.*f"), nWidth, nPrecision + 6, f);
\r
1944 nItemLen = (int)_tcslen(pszTemp);
\r
1948 nItemLen = cchLen;
\r
1952 #endif // _ATL_USE_CSTRING_FLOAT
\r
1955 va_arg(argList, void*);
\r
1957 nItemLen = max(nItemLen, nWidth + nPrecision);
\r
1962 va_arg(argList, int*);
\r
1966 ATLASSERT(FALSE); // unknown formatting option
\r
1970 // adjust nMaxLen for output nItemLen
\r
1971 nMaxLen += nItemLen;
\r
1974 if(GetBuffer(nMaxLen) == NULL)
\r
1976 #ifndef _ATL_USE_CSTRING_FLOAT
\r
1977 int nRet = SecureHelper::wvsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave);
\r
1978 #else // _ATL_USE_CSTRING_FLOAT
\r
1979 int nRet = SecureHelper::vsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave);
\r
1980 #endif // _ATL_USE_CSTRING_FLOAT
\r
1982 ATLASSERT(nRet <= GetAllocLength());
\r
1985 va_end(argListSave);
\r
1989 // formatting for localization (uses FormatMessage API)
\r
1990 // formatting (using FormatMessage style formatting)
\r
1991 BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...)
\r
1993 // format message into temporary buffer lpszTemp
\r
1995 va_start(argList, lpszFormat);
\r
1999 if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
\r
2000 lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
\r
2003 // assign lpszTemp into the resulting string and free the temporary
\r
2005 LocalFree(lpszTemp);
\r
2010 BOOL __cdecl FormatMessage(UINT nFormatID, ...)
\r
2012 // get format string from string table
\r
2013 CString strFormat;
\r
2014 BOOL bRetTmp = strFormat.LoadString(nFormatID);
\r
2016 ATLASSERT(bRetTmp != 0);
\r
2018 // format message into temporary buffer lpszTemp
\r
2020 va_start(argList, nFormatID);
\r
2024 if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
\r
2025 strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
\r
2028 // assign lpszTemp into the resulting string and free lpszTemp
\r
2030 LocalFree(lpszTemp);
\r
2035 // Windows support
\r
2036 BOOL LoadString(UINT nID) // load from string resource (255 chars max.)
\r
2039 const int CHAR_FUDGE = 1; // one TCHAR unused is good enough
\r
2041 const int CHAR_FUDGE = 2; // two BYTES unused for case of DBC last char
\r
2044 // try fixed buffer first (to avoid wasting space in the heap)
\r
2045 TCHAR szTemp[256];
\r
2046 int nCount = sizeof(szTemp) / sizeof(szTemp[0]);
\r
2047 int nLen = _LoadString(nID, szTemp, nCount);
\r
2048 if (nCount - nLen > CHAR_FUDGE)
\r
2051 return (nLen > 0);
\r
2054 // try buffer size of 512, then larger size until entire string is retrieved
\r
2059 LPTSTR lpstr = GetBuffer(nSize - 1);
\r
2065 nLen = _LoadString(nID, lpstr, nSize);
\r
2066 } while (nSize - nLen <= CHAR_FUDGE);
\r
2069 return (nLen > 0);
\r
2073 // ANSI <-> OEM support (convert string in place)
\r
2076 CopyBeforeWrite();
\r
2077 ::AnsiToOem(m_pchData, m_pchData);
\r
2082 CopyBeforeWrite();
\r
2083 ::OemToAnsi(m_pchData, m_pchData);
\r
2087 #ifndef _ATL_NO_COM
\r
2088 // OLE BSTR support (use for OLE automation)
\r
2089 BSTR AllocSysString() const
\r
2091 #if defined(_UNICODE) || defined(OLE2ANSI)
\r
2092 BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength);
\r
2094 int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
\r
2095 GetData()->nDataLength, NULL, NULL);
\r
2096 BSTR bstr = ::SysAllocStringLen(NULL, nLen);
\r
2098 MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen);
\r
2103 BSTR SetSysString(BSTR* pbstr) const
\r
2105 #if defined(_UNICODE) || defined(OLE2ANSI)
\r
2106 ::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength);
\r
2108 int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
\r
2109 GetData()->nDataLength, NULL, NULL);
\r
2110 if(::SysReAllocStringLen(pbstr, NULL, nLen))
\r
2111 MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen);
\r
2113 ATLASSERT(*pbstr != NULL);
\r
2116 #endif // !_ATL_NO_COM
\r
2118 // Access to string implementation buffer as "C" character array
\r
2119 LPTSTR GetBuffer(int nMinBufLength)
\r
2121 ATLASSERT(nMinBufLength >= 0);
\r
2123 if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
\r
2125 // we have to grow the buffer
\r
2126 CStringData* pOldData = GetData();
\r
2127 int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
\r
2128 if (nMinBufLength < nOldLen)
\r
2129 nMinBufLength = nOldLen;
\r
2131 if(!AllocBuffer(nMinBufLength))
\r
2134 SecureHelper::memcpy_x(m_pchData, (nMinBufLength + 1) * sizeof(TCHAR), pOldData->data(), (nOldLen + 1) * sizeof(TCHAR));
\r
2135 GetData()->nDataLength = nOldLen;
\r
2136 CString::Release(pOldData);
\r
2138 ATLASSERT(GetData()->nRefs <= 1);
\r
2140 // return a pointer to the character storage for this string
\r
2141 ATLASSERT(m_pchData != NULL);
\r
2145 void ReleaseBuffer(int nNewLength = -1)
\r
2147 CopyBeforeWrite(); // just in case GetBuffer was not called
\r
2149 if (nNewLength == -1)
\r
2150 nNewLength = lstrlen(m_pchData); // zero terminated
\r
2152 ATLASSERT(nNewLength <= GetData()->nAllocLength);
\r
2153 GetData()->nDataLength = nNewLength;
\r
2154 m_pchData[nNewLength] = _T('\0');
\r
2157 LPTSTR GetBufferSetLength(int nNewLength)
\r
2159 ATLASSERT(nNewLength >= 0);
\r
2161 if(GetBuffer(nNewLength) == NULL)
\r
2164 GetData()->nDataLength = nNewLength;
\r
2165 m_pchData[nNewLength] = _T('\0');
\r
2171 ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
\r
2172 if (GetData()->nDataLength != GetData()->nAllocLength)
\r
2174 CStringData* pOldData = GetData();
\r
2175 if(AllocBuffer(GetData()->nDataLength))
\r
2177 SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pOldData->data(), pOldData->nDataLength * sizeof(TCHAR));
\r
2178 ATLASSERT(m_pchData[GetData()->nDataLength] == _T('\0'));
\r
2179 CString::Release(pOldData);
\r
2182 ATLASSERT(GetData() != NULL);
\r
2185 // Use LockBuffer/UnlockBuffer to turn refcounting off
\r
2186 LPTSTR LockBuffer()
\r
2188 LPTSTR lpsz = GetBuffer(0);
\r
2190 GetData()->nRefs = -1;
\r
2194 void UnlockBuffer()
\r
2196 ATLASSERT(GetData()->nRefs == -1);
\r
2197 if (GetData() != _atltmpDataNil)
\r
2198 GetData()->nRefs = 1;
\r
2203 ~CString() // free any attached data
\r
2205 if (GetData() != _atltmpDataNil)
\r
2207 if (InterlockedDecrement(&GetData()->nRefs) <= 0)
\r
2208 delete[] (BYTE*)GetData();
\r
2212 int GetAllocLength() const
\r
2214 return GetData()->nAllocLength;
\r
2217 static BOOL __stdcall _IsValidString(LPCTSTR lpsz, int /*nLength*/ = -1)
\r
2219 return (lpsz != NULL) ? TRUE : FALSE;
\r
2223 LPTSTR m_pchData; // pointer to ref counted string data
\r
2225 // implementation helpers
\r
2226 CStringData* GetData() const
\r
2228 ATLASSERT(m_pchData != NULL);
\r
2229 return ((CStringData*)m_pchData) - 1;
\r
2234 m_pchData = _GetEmptyString().m_pchData;
\r
2237 BOOL AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const
\r
2239 // will clone the data attached to this string
\r
2240 // allocating 'nExtraLen' characters
\r
2241 // Places results in uninitialized string 'dest'
\r
2242 // Will copy the part or all of original data to start of new string
\r
2244 BOOL bRet = FALSE;
\r
2245 int nNewLen = nCopyLen + nExtraLen;
\r
2251 else if(nNewLen >= nCopyLen)
\r
2253 if(dest.AllocBuffer(nNewLen))
\r
2255 SecureHelper::memcpy_x(dest.m_pchData, (nNewLen + 1) * sizeof(TCHAR), m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR));
\r
2263 // always allocate one extra character for '\0' termination
\r
2264 // assumes [optimistically] that data length will equal allocation length
\r
2265 BOOL AllocBuffer(int nLen)
\r
2267 ATLASSERT(nLen >= 0);
\r
2268 ATLASSERT(nLen <= INT_MAX - 1); // max size (enough room for 1 extra)
\r
2276 CStringData* pData = NULL;
\r
2277 ATLTRY(pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR)]);
\r
2282 pData->data()[nLen] = _T('\0');
\r
2283 pData->nDataLength = nLen;
\r
2284 pData->nAllocLength = nLen;
\r
2285 m_pchData = pData->data();
\r
2291 // Assignment operators
\r
2292 // All assign a new value to the string
\r
2293 // (a) first see if the buffer is big enough
\r
2294 // (b) if enough room, copy on top of old buffer, set size and type
\r
2295 // (c) otherwise free old string data, and create a new one
\r
2297 // All routines return the new string (but as a 'const CString&' so that
\r
2298 // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
\r
2300 void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
\r
2302 if(AllocBeforeWrite(nSrcLen))
\r
2304 SecureHelper::memcpy_x(m_pchData, (nSrcLen + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR));
\r
2305 GetData()->nDataLength = nSrcLen;
\r
2306 m_pchData[nSrcLen] = _T('\0');
\r
2311 // NOTE: "operator +" is done as friend functions for simplicity
\r
2312 // There are three variants:
\r
2313 // CString + CString
\r
2314 // and for ? = TCHAR, LPCTSTR
\r
2317 BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data)
\r
2319 // -- master concatenation routine
\r
2320 // Concatenate two sources
\r
2321 // -- assume that 'this' is a new CString object
\r
2324 int nNewLen = nSrc1Len + nSrc2Len;
\r
2325 if(nNewLen < nSrc1Len || nNewLen < nSrc2Len)
\r
2329 else if(nNewLen != 0)
\r
2331 bRet = AllocBuffer(nNewLen);
\r
2334 SecureHelper::memcpy_x(m_pchData, (nNewLen + 1) * sizeof(TCHAR), lpszSrc1Data, nSrc1Len * sizeof(TCHAR));
\r
2335 SecureHelper::memcpy_x(m_pchData + nSrc1Len, (nNewLen + 1 - nSrc1Len) * sizeof(TCHAR), lpszSrc2Data, nSrc2Len * sizeof(TCHAR));
\r
2341 void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
\r
2343 // -- the main routine for += operators
\r
2345 // concatenating an empty string is a no-op!
\r
2349 // if the buffer is too small, or we have a width mis-match, just
\r
2350 // allocate a new buffer (slow but sure)
\r
2351 if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
\r
2353 // we have to grow the buffer, use the ConcatCopy routine
\r
2354 CStringData* pOldData = GetData();
\r
2355 if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData))
\r
2357 ATLASSERT(pOldData != NULL);
\r
2358 CString::Release(pOldData);
\r
2363 // fast concatenation when buffer big enough
\r
2364 SecureHelper::memcpy_x(m_pchData + GetData()->nDataLength, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR));
\r
2365 GetData()->nDataLength += nSrcLen;
\r
2366 ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
\r
2367 m_pchData[GetData()->nDataLength] = _T('\0');
\r
2371 void CopyBeforeWrite()
\r
2373 if (GetData()->nRefs > 1)
\r
2375 CStringData* pData = GetData();
\r
2377 if(AllocBuffer(pData->nDataLength))
\r
2378 SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR));
\r
2380 ATLASSERT(GetData()->nRefs <= 1);
\r
2383 BOOL AllocBeforeWrite(int nLen)
\r
2386 if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
\r
2389 bRet = AllocBuffer(nLen);
\r
2391 ATLASSERT(GetData()->nRefs <= 1);
\r
2397 if (GetData() != _atltmpDataNil)
\r
2399 ATLASSERT(GetData()->nRefs != 0);
\r
2400 if (InterlockedDecrement(&GetData()->nRefs) <= 0)
\r
2401 delete[] (BYTE*)GetData();
\r
2406 static void PASCAL Release(CStringData* pData)
\r
2408 if (pData != _atltmpDataNil)
\r
2410 ATLASSERT(pData->nRefs != 0);
\r
2411 if (InterlockedDecrement(&pData->nRefs) <= 0)
\r
2412 delete[] (BYTE*)pData;
\r
2416 static int PASCAL SafeStrlen(LPCTSTR lpsz)
\r
2418 return (lpsz == NULL) ? 0 : lstrlen(lpsz);
\r
2421 static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf)
\r
2424 // LoadString without annoying warning from the Debug kernel if the
\r
2425 // segment containing the string is not present
\r
2426 if (::FindResource(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE((nID >> 4) + 1), RT_STRING) == NULL)
\r
2428 lpszBuf[0] = _T('\0');
\r
2429 return 0; // not found
\r
2433 int nLen = ::LoadString(ModuleHelper::GetResourceInstance(), nID, lpszBuf, nMaxBuf);
\r
2435 lpszBuf[0] = _T('\0');
\r
2440 static const CString& __stdcall _GetEmptyString()
\r
2442 return *(CString*)&_atltmpPchNil;
\r
2445 // CString conversion helpers
\r
2446 static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
\r
2448 if (count == 0 && mbstr != NULL)
\r
2451 int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL);
\r
2452 ATLASSERT(mbstr == NULL || result <= (int)count);
\r
2454 mbstr[result - 1] = 0;
\r
2458 static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
\r
2460 if (count == 0 && wcstr != NULL)
\r
2463 int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count);
\r
2464 ATLASSERT(wcstr == NULL || result <= (int)count);
\r
2466 wcstr[result - 1] = 0;
\r
2470 // Helpers to avoid CRT startup code
\r
2471 #ifdef _ATL_MIN_CRT
\r
2472 static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
\r
2474 // strchr for '\0' should succeed
\r
2479 p = ::CharNext(p);
\r
2481 return (*p == ch) ? p : NULL;
\r
2484 static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
\r
2486 const TCHAR* lpsz = NULL;
\r
2491 p = ::CharNext(p);
\r
2496 static TCHAR* _cstrrev(TCHAR* pStr)
\r
2498 // optimize NULL, zero-length, and single-char case
\r
2499 if ((pStr == NULL) || (pStr[0] == _T('\0')) || (pStr[1] == _T('\0')))
\r
2506 TCHAR* pNext = ::CharNext(p);
\r
2509 char p1 = *(char*)p;
\r
2510 *(char*)p = *(char*)(p + 1);
\r
2511 *(char*)(p + 1) = p1;
\r
2530 static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
\r
2532 int nLen = lstrlen(pCharSet);
\r
2534 return (TCHAR*)pStr;
\r
2536 const TCHAR* pRet = NULL;
\r
2537 const TCHAR* pCur = pStr;
\r
2538 while((pCur = _cstrchr(pCur, *pCharSet)) != NULL)
\r
2540 if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0)
\r
2545 pCur = ::CharNext(pCur);
\r
2550 static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
\r
2553 const TCHAR* p = pStr;
\r
2556 const TCHAR* pNext = ::CharNext(p);
\r
2559 if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL)
\r
2565 if(_cstrchr(pCharSet, *p) == NULL)
\r
2574 static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
\r
2577 TCHAR* p = (TCHAR*)pStr;
\r
2580 TCHAR* pNext = ::CharNext(p);
\r
2583 if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL)
\r
2589 if(_cstrchr(pCharSet, *p) != NULL)
\r
2598 static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
\r
2600 int n = _cstrcspn(p, lpszCharSet);
\r
2601 return (p[n] != 0) ? &p[n] : NULL;
\r
2604 static int _cstrisdigit(TCHAR ch)
\r
2607 GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
\r
2608 return (type & C1_DIGIT) == C1_DIGIT;
\r
2611 static int _cstrisspace(TCHAR ch)
\r
2614 GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
\r
2615 return (type & C1_SPACE) == C1_SPACE;
\r
2618 static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2620 return lstrcmp(pstrOne, pstrOther);
\r
2623 static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2625 return lstrcmpi(pstrOne, pstrOther);
\r
2628 static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2630 int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1);
\r
2631 ATLASSERT(nRet != 0);
\r
2632 return nRet - 2; // convert to strcmp convention
\r
2635 static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2637 int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1);
\r
2638 ATLASSERT(nRet != 0);
\r
2639 return nRet - 2; // convert to strcmp convention
\r
2642 static int _cstrtoi(const TCHAR* nptr)
\r
2644 int c; // current char
\r
2645 int total; // current total
\r
2646 int sign; // if '-', then negative, otherwise positive
\r
2648 while (_cstrisspace(*nptr))
\r
2651 c = (int)(_TUCHAR)*nptr++;
\r
2652 sign = c; // save sign indication
\r
2653 if (c == _T('-') || c == _T('+'))
\r
2654 c = (int)(_TUCHAR)*nptr++; // skip sign
\r
2658 while (_cstrisdigit((TCHAR)c))
\r
2660 total = 10 * total + (c - '0'); // accumulate digit
\r
2661 c = (int)(_TUCHAR)*nptr++; // get next char
\r
2667 return total; // return result, negated if necessary
\r
2669 #else // !_ATL_MIN_CRT
\r
2670 static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
\r
2672 return _tcschr(p, ch);
\r
2675 static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
\r
2677 return _tcsrchr(p, ch);
\r
2680 static TCHAR* _cstrrev(TCHAR* pStr)
\r
2682 return _tcsrev(pStr);
\r
2685 static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
\r
2687 return _tcsstr(pStr, pCharSet);
\r
2690 static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
\r
2692 return (int)_tcsspn(pStr, pCharSet);
\r
2695 static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
\r
2697 return (int)_tcscspn(pStr, pCharSet);
\r
2700 static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
\r
2702 return _tcspbrk(p, lpszCharSet);
\r
2705 static int _cstrisdigit(TCHAR ch)
\r
2707 return _istdigit(ch);
\r
2710 static int _cstrisspace(TCHAR ch)
\r
2712 return _istspace((_TUCHAR)ch);
\r
2715 static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2717 return _tcscmp(pstrOne, pstrOther);
\r
2720 static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2722 return _tcsicmp(pstrOne, pstrOther);
\r
2725 #ifndef _WIN32_WCE
\r
2726 static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2728 return _tcscoll(pstrOne, pstrOther);
\r
2731 static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
\r
2733 return _tcsicoll(pstrOne, pstrOther);
\r
2735 #endif // !_WIN32_WCE
\r
2737 static int _cstrtoi(const TCHAR* nptr)
\r
2739 return _ttoi(nptr);
\r
2741 #endif // !_ATL_MIN_CRT
\r
2743 static const TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2)
\r
2745 const TCHAR* lpsz = NULL;
\r
2748 if (*p == ch1 && *(p + 1) == ch2)
\r
2753 p = ::CharNext(p);
\r
2760 // Compare helpers
\r
2762 inline bool __stdcall operator ==(const CString& s1, const CString& s2)
\r
2763 { return s1.Compare(s2) == 0; }
\r
2765 inline bool __stdcall operator ==(const CString& s1, LPCTSTR s2)
\r
2766 { return s1.Compare(s2) == 0; }
\r
2768 inline bool __stdcall operator ==(LPCTSTR s1, const CString& s2)
\r
2769 { return s2.Compare(s1) == 0; }
\r
2771 inline bool __stdcall operator !=(const CString& s1, const CString& s2)
\r
2772 { return s1.Compare(s2) != 0; }
\r
2774 inline bool __stdcall operator !=(const CString& s1, LPCTSTR s2)
\r
2775 { return s1.Compare(s2) != 0; }
\r
2777 inline bool __stdcall operator !=(LPCTSTR s1, const CString& s2)
\r
2778 { return s2.Compare(s1) != 0; }
\r
2780 inline bool __stdcall operator <(const CString& s1, const CString& s2)
\r
2781 { return s1.Compare(s2) < 0; }
\r
2783 inline bool __stdcall operator <(const CString& s1, LPCTSTR s2)
\r
2784 { return s1.Compare(s2) < 0; }
\r
2786 inline bool __stdcall operator <(LPCTSTR s1, const CString& s2)
\r
2787 { return s2.Compare(s1) > 0; }
\r
2789 inline bool __stdcall operator >(const CString& s1, const CString& s2)
\r
2790 { return s1.Compare(s2) > 0; }
\r
2792 inline bool __stdcall operator >(const CString& s1, LPCTSTR s2)
\r
2793 { return s1.Compare(s2) > 0; }
\r
2795 inline bool __stdcall operator >(LPCTSTR s1, const CString& s2)
\r
2796 { return s2.Compare(s1) < 0; }
\r
2798 inline bool __stdcall operator <=(const CString& s1, const CString& s2)
\r
2799 { return s1.Compare(s2) <= 0; }
\r
2801 inline bool __stdcall operator <=(const CString& s1, LPCTSTR s2)
\r
2802 { return s1.Compare(s2) <= 0; }
\r
2804 inline bool __stdcall operator <=(LPCTSTR s1, const CString& s2)
\r
2805 { return s2.Compare(s1) >= 0; }
\r
2807 inline bool __stdcall operator >=(const CString& s1, const CString& s2)
\r
2808 { return s1.Compare(s2) >= 0; }
\r
2810 inline bool __stdcall operator >=(const CString& s1, LPCTSTR s2)
\r
2811 { return s1.Compare(s2) >= 0; }
\r
2813 inline bool __stdcall operator >=(LPCTSTR s1, const CString& s2)
\r
2814 { return s2.Compare(s1) <= 0; }
\r
2817 // CString "operator +" functions
\r
2819 inline CString __stdcall operator +(const CString& string1, const CString& string2)
\r
2822 s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData);
\r
2826 inline CString __stdcall operator +(const CString& string, TCHAR ch)
\r
2829 s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, 1, &ch);
\r
2833 inline CString __stdcall operator +(TCHAR ch, const CString& string)
\r
2836 s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
\r
2841 inline CString __stdcall operator +(const CString& string, char ch)
\r
2843 return string + (TCHAR)ch;
\r
2846 inline CString __stdcall operator +(char ch, const CString& string)
\r
2848 return (TCHAR)ch + string;
\r
2850 #endif // _UNICODE
\r
2852 inline CString __stdcall operator +(const CString& string, LPCTSTR lpsz)
\r
2854 ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz));
\r
2856 s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz);
\r
2860 inline CString __stdcall operator +(LPCTSTR lpsz, const CString& string)
\r
2862 ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz));
\r
2864 s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData);
\r
2868 #endif // !_WTL_NO_CSTRING
\r
2871 ///////////////////////////////////////////////////////////////////////////////
\r
2872 // CRecentDocumentList - MRU List Support
\r
2874 #ifndef _WIN32_WCE
\r
2876 #ifndef _WTL_MRUEMPTY_TEXT
\r
2877 #define _WTL_MRUEMPTY_TEXT _T("(empty)")
\r
2880 // forward declaration
\r
2881 inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen);
\r
2883 template <class T, int t_cchItemLen = MAX_PATH, int t_nFirstID = ID_FILE_MRU_FIRST, int t_nLastID = ID_FILE_MRU_LAST>
\r
2884 class CRecentDocumentListBase
\r
2890 TCHAR szDocName[t_cchItemLen];
\r
2891 bool operator ==(const _DocEntry& de) const
\r
2892 { return (lstrcmpi(szDocName, de.szDocName) == 0); }
\r
2897 m_nMaxEntries_Min = 2,
\r
2898 m_nMaxEntries_Max = t_nLastID - t_nFirstID + 1,
\r
2899 m_cchMaxItemLen_Min = 6,
\r
2900 m_cchMaxItemLen_Max = t_cchItemLen,
\r
2901 m_cchItemNameLen = 11
\r
2905 ATL::CSimpleArray<_DocEntry> m_arrDocs;
\r
2906 int m_nMaxEntries; // default is 4
\r
2909 TCHAR m_szNoEntries[t_cchItemLen];
\r
2911 int m_cchMaxItemLen;
\r
2914 CRecentDocumentListBase() : m_hMenu(NULL), m_nMaxEntries(4), m_cchMaxItemLen(-1)
\r
2916 // These ASSERTs verify values of the template arguments
\r
2917 ATLASSERT(t_cchItemLen > m_cchMaxItemLen_Min);
\r
2918 ATLASSERT(m_nMaxEntries_Max > m_nMaxEntries_Min);
\r
2922 HMENU GetMenuHandle() const
\r
2927 void SetMenuHandle(HMENU hMenu)
\r
2929 ATLASSERT(hMenu == NULL || ::IsMenu(hMenu));
\r
2931 if(m_hMenu == NULL || (::GetMenuString(m_hMenu, t_nFirstID, m_szNoEntries, t_cchItemLen, MF_BYCOMMAND) == 0))
\r
2933 T* pT = static_cast<T*>(this);
\r
2934 pT; // avoid level 4 warning
\r
2935 SecureHelper::strncpy_x(m_szNoEntries, _countof(m_szNoEntries), pT->GetMRUEmptyText(), _TRUNCATE);
\r
2939 int GetMaxEntries() const
\r
2941 return m_nMaxEntries;
\r
2944 void SetMaxEntries(int nMaxEntries)
\r
2946 ATLASSERT(nMaxEntries >= m_nMaxEntries_Min && nMaxEntries <= m_nMaxEntries_Max);
\r
2947 if(nMaxEntries < m_nMaxEntries_Min)
\r
2948 nMaxEntries = m_nMaxEntries_Min;
\r
2949 else if(nMaxEntries > m_nMaxEntries_Max)
\r
2950 nMaxEntries = m_nMaxEntries_Max;
\r
2951 m_nMaxEntries = nMaxEntries;
\r
2954 int GetMaxItemLength() const
\r
2956 return m_cchMaxItemLen;
\r
2959 void SetMaxItemLength(int cchMaxLen)
\r
2961 ATLASSERT((cchMaxLen >= m_cchMaxItemLen_Min && cchMaxLen <= m_cchMaxItemLen_Max) || cchMaxLen == -1);
\r
2962 if(cchMaxLen != -1)
\r
2964 if(cchMaxLen < m_cchMaxItemLen_Min)
\r
2965 cchMaxLen = m_cchMaxItemLen_Min;
\r
2966 else if(cchMaxLen > m_cchMaxItemLen_Max)
\r
2967 cchMaxLen = m_cchMaxItemLen_Max;
\r
2969 m_cchMaxItemLen = cchMaxLen;
\r
2970 T* pT = static_cast<T*>(this);
\r
2975 BOOL AddToList(LPCTSTR lpstrDocName)
\r
2978 errno_t nRet = SecureHelper::strncpy_x(de.szDocName, _countof(de.szDocName), lpstrDocName, _TRUNCATE);
\r
2979 if(nRet != 0 && nRet != STRUNCATE)
\r
2982 for(int i = 0; i < m_arrDocs.GetSize(); i++)
\r
2984 if(lstrcmpi(m_arrDocs[i].szDocName, lpstrDocName) == 0)
\r
2986 m_arrDocs.RemoveAt(i);
\r
2991 if(m_arrDocs.GetSize() == m_nMaxEntries)
\r
2992 m_arrDocs.RemoveAt(0);
\r
2994 BOOL bRet = m_arrDocs.Add(de);
\r
2997 T* pT = static_cast<T*>(this);
\r
2998 bRet = pT->UpdateMenu();
\r
3003 // This function is deprecated because it is not safe.
\r
3004 // Use the version below that accepts the buffer length.
\r
3005 #if (_MSC_VER >= 1300)
\r
3006 __declspec(deprecated)
\r
3008 BOOL GetFromList(int /*nItemID*/, LPTSTR /*lpstrDocName*/)
\r
3014 BOOL GetFromList(int nItemID, LPTSTR lpstrDocName, int cchLength)
\r
3016 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
\r
3017 if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
\r
3019 if(lstrlen(m_arrDocs[nIndex].szDocName) >= cchLength)
\r
3021 SecureHelper::strcpy_x(lpstrDocName, cchLength, m_arrDocs[nIndex].szDocName);
\r
3026 #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
\r
3027 BOOL GetFromList(int nItemID, _CSTRING_NS::CString& strDocName)
\r
3029 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
\r
3030 if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
\r
3032 strDocName = m_arrDocs[nIndex].szDocName;
\r
3035 #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
\r
3037 BOOL RemoveFromList(int nItemID)
\r
3039 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
\r
3040 BOOL bRet = m_arrDocs.RemoveAt(nIndex);
\r
3043 T* pT = static_cast<T*>(this);
\r
3044 bRet = pT->UpdateMenu();
\r
3049 BOOL MoveToTop(int nItemID)
\r
3051 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
\r
3052 if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
\r
3055 de = m_arrDocs[nIndex];
\r
3056 m_arrDocs.RemoveAt(nIndex);
\r
3057 BOOL bRet = m_arrDocs.Add(de);
\r
3060 T* pT = static_cast<T*>(this);
\r
3061 bRet = pT->UpdateMenu();
\r
3066 BOOL ReadFromRegistry(LPCTSTR lpstrRegKey)
\r
3068 T* pT = static_cast<T*>(this);
\r
3069 ATL::CRegKey rkParent;
\r
3072 LONG lRet = rkParent.Open(HKEY_CURRENT_USER, lpstrRegKey);
\r
3073 if(lRet != ERROR_SUCCESS)
\r
3075 lRet = rk.Open(rkParent, pT->GetRegKeyName());
\r
3076 if(lRet != ERROR_SUCCESS)
\r
3080 #if (_ATL_VER >= 0x0700)
\r
3081 lRet = rk.QueryDWORDValue(pT->GetRegCountName(), dwRet);
\r
3083 lRet = rk.QueryValue(dwRet, pT->GetRegCountName());
\r
3085 if(lRet != ERROR_SUCCESS)
\r
3087 SetMaxEntries(dwRet);
\r
3089 m_arrDocs.RemoveAll();
\r
3091 TCHAR szRetString[t_cchItemLen] = { 0 };
\r
3094 for(int nItem = m_nMaxEntries; nItem > 0; nItem--)
\r
3096 TCHAR szBuff[m_cchItemNameLen] = { 0 };
\r
3097 SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
\r
3098 #if (_ATL_VER >= 0x0700)
\r
3099 ULONG ulCount = t_cchItemLen;
\r
3100 lRet = rk.QueryStringValue(szBuff, szRetString, &ulCount);
\r
3102 DWORD dwCount = t_cchItemLen * sizeof(TCHAR);
\r
3103 lRet = rk.QueryValue(szRetString, szBuff, &dwCount);
\r
3105 if(lRet == ERROR_SUCCESS)
\r
3107 SecureHelper::strcpy_x(de.szDocName, _countof(de.szDocName), szRetString);
\r
3108 m_arrDocs.Add(de);
\r
3115 return pT->UpdateMenu();
\r
3118 BOOL WriteToRegistry(LPCTSTR lpstrRegKey)
\r
3120 T* pT = static_cast<T*>(this);
\r
3121 pT; // avoid level 4 warning
\r
3122 ATL::CRegKey rkParent;
\r
3125 LONG lRet = rkParent.Create(HKEY_CURRENT_USER, lpstrRegKey);
\r
3126 if(lRet != ERROR_SUCCESS)
\r
3128 lRet = rk.Create(rkParent, pT->GetRegKeyName());
\r
3129 if(lRet != ERROR_SUCCESS)
\r
3132 #if (_ATL_VER >= 0x0700)
\r
3133 lRet = rk.SetDWORDValue(pT->GetRegCountName(), m_nMaxEntries);
\r
3135 lRet = rk.SetValue(m_nMaxEntries, pT->GetRegCountName());
\r
3137 ATLASSERT(lRet == ERROR_SUCCESS);
\r
3141 for(nItem = m_arrDocs.GetSize(); nItem > 0; nItem--)
\r
3143 TCHAR szBuff[m_cchItemNameLen] = { 0 };
\r
3144 SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
\r
3145 TCHAR szDocName[t_cchItemLen] = { 0 };
\r
3146 GetFromList(t_nFirstID + nItem - 1, szDocName, t_cchItemLen);
\r
3147 #if (_ATL_VER >= 0x0700)
\r
3148 lRet = rk.SetStringValue(szBuff, szDocName);
\r
3150 lRet = rk.SetValue(szDocName, szBuff);
\r
3152 ATLASSERT(lRet == ERROR_SUCCESS);
\r
3155 // delete unused keys
\r
3156 for(nItem = m_arrDocs.GetSize() + 1; nItem < m_nMaxEntries_Max; nItem++)
\r
3158 TCHAR szBuff[m_cchItemNameLen] = { 0 };
\r
3159 SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
\r
3160 rk.DeleteValue(szBuff);
\r
3172 if(m_hMenu == NULL)
\r
3174 ATLASSERT(::IsMenu(m_hMenu));
\r
3176 int nItems = ::GetMenuItemCount(m_hMenu);
\r
3178 for(nInsertPoint = 0; nInsertPoint < nItems; nInsertPoint++)
\r
3181 mi.fMask = MIIM_ID;
\r
3182 ::GetMenuItemInfo(m_hMenu, nInsertPoint, TRUE, &mi);
\r
3183 if (mi.wID == t_nFirstID)
\r
3186 ATLASSERT(nInsertPoint < nItems && "You need a menu item with an ID = t_nFirstID");
\r
3189 for(nItem = t_nFirstID; nItem < t_nFirstID + m_nMaxEntries; nItem++)
\r
3191 // keep the first one as an insertion point
\r
3192 if (nItem != t_nFirstID)
\r
3193 ::DeleteMenu(m_hMenu, nItem, MF_BYCOMMAND);
\r
3196 TCHAR szItemText[t_cchItemLen + 6] = { 0 }; // add space for &, 2 digits, and a space
\r
3197 int nSize = m_arrDocs.GetSize();
\r
3201 for(nItem = 0; nItem < nSize; nItem++)
\r
3203 if(m_cchMaxItemLen == -1)
\r
3205 SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, m_arrDocs[nSize - 1 - nItem].szDocName);
\r
3209 TCHAR szBuff[t_cchItemLen] = { 0 };
\r
3210 T* pT = static_cast<T*>(this);
\r
3211 pT; // avoid level 4 warning
\r
3212 bool bRet = pT->CompactDocumentName(szBuff, m_arrDocs[nSize - 1 - nItem].szDocName, m_cchMaxItemLen);
\r
3213 bRet; // avoid level 4 warning
\r
3215 SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, szBuff);
\r
3217 ::InsertMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION | MF_STRING, t_nFirstID + nItem, szItemText);
\r
3222 ::InsertMenu(m_hMenu, nInsertPoint, MF_BYPOSITION | MF_STRING, t_nFirstID, m_szNoEntries);
\r
3223 ::EnableMenuItem(m_hMenu, t_nFirstID, MF_GRAYED);
\r
3226 ::DeleteMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION);
\r
3232 // override to provide a different method of compacting document names
\r
3233 static bool CompactDocumentName(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
\r
3235 return AtlCompactPath(lpstrOut, lpstrIn, cchLen);
\r
3238 static LPCTSTR GetRegKeyName()
\r
3240 return _T("Recent Document List");
\r
3243 static LPCTSTR GetRegCountName()
\r
3245 return _T("DocumentCount");
\r
3248 static LPCTSTR GetRegItemName()
\r
3250 // Note: This string is a format string used with wsprintf().
\r
3251 // Resulting formatted string must be m_cchItemNameLen or less
\r
3252 // characters long, including the terminating null character.
\r
3253 return _T("Document%i");
\r
3256 static LPCTSTR GetMRUEmptyText()
\r
3258 return _WTL_MRUEMPTY_TEXT;
\r
3262 class CRecentDocumentList : public CRecentDocumentListBase<CRecentDocumentList>
\r
3268 #endif // _WIN32_WCE
\r
3271 ///////////////////////////////////////////////////////////////////////////////
\r
3272 // CFindFile - file search helper class
\r
3278 WIN32_FIND_DATA m_fd;
\r
3279 TCHAR m_lpszRoot[MAX_PATH];
\r
3280 TCHAR m_chDirSeparator;
\r
3284 // Constructor/destructor
\r
3285 CFindFile() : m_hFind(NULL), m_chDirSeparator(_T('\\')), m_bFound(FALSE)
\r
3294 ULONGLONG GetFileSize() const
\r
3296 ATLASSERT(m_hFind != NULL);
\r
3298 ULARGE_INTEGER nFileSize = { 0 };
\r
3302 nFileSize.LowPart = m_fd.nFileSizeLow;
\r
3303 nFileSize.HighPart = m_fd.nFileSizeHigh;
\r
3307 nFileSize.QuadPart = 0;
\r
3310 return nFileSize.QuadPart;
\r
3313 BOOL GetFileName(LPTSTR lpstrFileName, int cchLength) const
\r
3315 ATLASSERT(m_hFind != NULL);
\r
3316 if(lstrlen(m_fd.cFileName) >= cchLength)
\r
3320 SecureHelper::strcpy_x(lpstrFileName, cchLength, m_fd.cFileName);
\r
3325 BOOL GetFilePath(LPTSTR lpstrFilePath, int cchLength) const
\r
3327 ATLASSERT(m_hFind != NULL);
\r
3329 int nLen = lstrlen(m_lpszRoot);
\r
3330 #ifndef _WIN32_WCE
\r
3331 ATLASSERT(nLen > 0);
\r
3335 bool bAddSep = (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/'));
\r
3336 #else // CE specific
\r
3337 // allow diskless devices (nLen == 0)
\r
3338 bool bAddSep = ((nLen == 0) || (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/')));
\r
3339 #endif // _WIN32_WCE
\r
3341 if((lstrlen(m_lpszRoot) + (bAddSep ? 1 : 0)) >= cchLength)
\r
3344 SecureHelper::strcpy_x(lpstrFilePath, cchLength, m_lpszRoot);
\r
3348 TCHAR szSeparator[2] = { m_chDirSeparator, 0 };
\r
3349 SecureHelper::strcat_x(lpstrFilePath, cchLength, szSeparator);
\r
3352 SecureHelper::strcat_x(lpstrFilePath, cchLength, m_fd.cFileName);
\r
3357 #ifndef _WIN32_WCE
\r
3358 BOOL GetFileTitle(LPTSTR lpstrFileTitle, int cchLength) const
\r
3360 ATLASSERT(m_hFind != NULL);
\r
3362 TCHAR szBuff[MAX_PATH] = { 0 };
\r
3363 if(!GetFileName(szBuff, MAX_PATH))
\r
3366 if(lstrlen(szBuff) >= cchLength || cchLength < 1)
\r
3369 // find the last dot
\r
3370 LPTSTR pstrDot = (LPTSTR)_cstrrchr(szBuff, _T('.'));
\r
3371 if(pstrDot != NULL)
\r
3374 SecureHelper::strcpy_x(lpstrFileTitle, cchLength, szBuff);
\r
3378 #endif // !_WIN32_WCE
\r
3380 BOOL GetFileURL(LPTSTR lpstrFileURL, int cchLength) const
\r
3382 ATLASSERT(m_hFind != NULL);
\r
3384 TCHAR szBuff[MAX_PATH] = { 0 };
\r
3385 if(!GetFilePath(szBuff, MAX_PATH))
\r
3387 LPCTSTR lpstrFileURLPrefix = _T("file://");
\r
3388 if(lstrlen(szBuff) + lstrlen(lpstrFileURLPrefix) >= cchLength)
\r
3390 SecureHelper::strcpy_x(lpstrFileURL, cchLength, lpstrFileURLPrefix);
\r
3391 SecureHelper::strcat_x(lpstrFileURL, cchLength, szBuff);
\r
3396 BOOL GetRoot(LPTSTR lpstrRoot, int cchLength) const
\r
3398 ATLASSERT(m_hFind != NULL);
\r
3399 if(lstrlen(m_lpszRoot) >= cchLength)
\r
3402 SecureHelper::strcpy_x(lpstrRoot, cchLength, m_lpszRoot);
\r
3407 #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
\r
3408 _CSTRING_NS::CString GetFileName() const
\r
3410 ATLASSERT(m_hFind != NULL);
\r
3412 _CSTRING_NS::CString ret;
\r
3415 ret = m_fd.cFileName;
\r
3419 _CSTRING_NS::CString GetFilePath() const
\r
3421 ATLASSERT(m_hFind != NULL);
\r
3423 _CSTRING_NS::CString strResult = m_lpszRoot;
\r
3424 int nLen = strResult.GetLength();
\r
3425 #ifndef _WIN32_WCE
\r
3426 ATLASSERT(nLen > 0);
\r
3430 if((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/')))
\r
3431 #else // CE specific
\r
3432 // allow diskless devices (nLen == 0)
\r
3433 if((nLen == 0) || ((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/'))))
\r
3434 #endif // _WIN32_WCE
\r
3435 strResult += m_chDirSeparator;
\r
3436 strResult += GetFileName();
\r
3440 #ifndef _WIN32_WCE
\r
3441 _CSTRING_NS::CString GetFileTitle() const
\r
3443 ATLASSERT(m_hFind != NULL);
\r
3445 _CSTRING_NS::CString strResult;
\r
3446 GetFileTitle(strResult.GetBuffer(MAX_PATH), MAX_PATH);
\r
3447 strResult.ReleaseBuffer();
\r
3451 #endif // !_WIN32_WCE
\r
3453 _CSTRING_NS::CString GetFileURL() const
\r
3455 ATLASSERT(m_hFind != NULL);
\r
3457 _CSTRING_NS::CString strResult("file://");
\r
3458 strResult += GetFilePath();
\r
3462 _CSTRING_NS::CString GetRoot() const
\r
3464 ATLASSERT(m_hFind != NULL);
\r
3466 _CSTRING_NS::CString str = m_lpszRoot;
\r
3469 #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
\r
3471 BOOL GetLastWriteTime(FILETIME* pTimeStamp) const
\r
3473 ATLASSERT(m_hFind != NULL);
\r
3474 ATLASSERT(pTimeStamp != NULL);
\r
3476 if(m_bFound && pTimeStamp != NULL)
\r
3478 *pTimeStamp = m_fd.ftLastWriteTime;
\r
3485 BOOL GetLastAccessTime(FILETIME* pTimeStamp) const
\r
3487 ATLASSERT(m_hFind != NULL);
\r
3488 ATLASSERT(pTimeStamp != NULL);
\r
3490 if(m_bFound && pTimeStamp != NULL)
\r
3492 *pTimeStamp = m_fd.ftLastAccessTime;
\r
3499 BOOL GetCreationTime(FILETIME* pTimeStamp) const
\r
3501 ATLASSERT(m_hFind != NULL);
\r
3503 if(m_bFound && pTimeStamp != NULL)
\r
3505 *pTimeStamp = m_fd.ftCreationTime;
\r
3512 BOOL MatchesMask(DWORD dwMask) const
\r
3514 ATLASSERT(m_hFind != NULL);
\r
3517 return ((m_fd.dwFileAttributes & dwMask) != 0);
\r
3522 BOOL IsDots() const
\r
3524 ATLASSERT(m_hFind != NULL);
\r
3526 // return TRUE if the file name is "." or ".." and
\r
3527 // the file is a directory
\r
3529 BOOL bResult = FALSE;
\r
3530 if(m_bFound && IsDirectory())
\r
3532 if(m_fd.cFileName[0] == _T('.') && (m_fd.cFileName[1] == _T('\0') || (m_fd.cFileName[1] == _T('.') && m_fd.cFileName[2] == _T('\0'))))
\r
3539 BOOL IsReadOnly() const
\r
3541 return MatchesMask(FILE_ATTRIBUTE_READONLY);
\r
3544 BOOL IsDirectory() const
\r
3546 return MatchesMask(FILE_ATTRIBUTE_DIRECTORY);
\r
3549 BOOL IsCompressed() const
\r
3551 return MatchesMask(FILE_ATTRIBUTE_COMPRESSED);
\r
3554 BOOL IsSystem() const
\r
3556 return MatchesMask(FILE_ATTRIBUTE_SYSTEM);
\r
3559 BOOL IsHidden() const
\r
3561 return MatchesMask(FILE_ATTRIBUTE_HIDDEN);
\r
3564 BOOL IsTemporary() const
\r
3566 return MatchesMask(FILE_ATTRIBUTE_TEMPORARY);
\r
3569 BOOL IsNormal() const
\r
3571 return MatchesMask(FILE_ATTRIBUTE_NORMAL);
\r
3574 BOOL IsArchived() const
\r
3576 return MatchesMask(FILE_ATTRIBUTE_ARCHIVE);
\r
3580 BOOL FindFile(LPCTSTR pstrName = NULL)
\r
3584 if(pstrName == NULL)
\r
3586 pstrName = _T("*.*");
\r
3588 else if(lstrlen(pstrName) >= MAX_PATH)
\r
3594 SecureHelper::strcpy_x(m_fd.cFileName, _countof(m_fd.cFileName), pstrName);
\r
3596 m_hFind = ::FindFirstFile(pstrName, &m_fd);
\r
3598 if(m_hFind == INVALID_HANDLE_VALUE)
\r
3601 #ifndef _WIN32_WCE
\r
3602 bool bFullPath = (::GetFullPathName(pstrName, MAX_PATH, m_lpszRoot, NULL) != 0);
\r
3603 #else // CE specific
\r
3604 errno_t nRet = SecureHelper::strncpy_x(m_lpszRoot, _countof(m_lpszRoot), pstrName, _TRUNCATE);
\r
3605 bool bFullPath = (nRet == 0 || nRet == STRUNCATE);
\r
3606 #endif // _WIN32_WCE
\r
3608 // passed name isn't a valid path but was found by the API
\r
3609 ATLASSERT(bFullPath);
\r
3613 ::SetLastError(ERROR_INVALID_NAME);
\r
3618 // find the last forward or backward whack
\r
3619 LPTSTR pstrBack = (LPTSTR)_cstrrchr(m_lpszRoot, _T('\\'));
\r
3620 LPTSTR pstrFront = (LPTSTR)_cstrrchr(m_lpszRoot, _T('/'));
\r
3622 if(pstrFront != NULL || pstrBack != NULL)
\r
3624 if(pstrFront == NULL)
\r
3625 pstrFront = m_lpszRoot;
\r
3626 if(pstrBack == NULL)
\r
3627 pstrBack = m_lpszRoot;
\r
3629 // from the start to the last whack is the root
\r
3631 if(pstrFront >= pstrBack)
\r
3632 *pstrFront = _T('\0');
\r
3634 *pstrBack = _T('\0');
\r
3643 BOOL FindNextFile()
\r
3645 ATLASSERT(m_hFind != NULL);
\r
3647 if(m_hFind == NULL)
\r
3653 m_bFound = ::FindNextFile(m_hFind, &m_fd);
\r
3662 if(m_hFind != NULL && m_hFind != INVALID_HANDLE_VALUE)
\r
3664 ::FindClose(m_hFind);
\r
3670 static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
\r
3672 #ifdef _ATL_MIN_CRT
\r
3673 const TCHAR* lpsz = NULL;
\r
3678 p = ::CharNext(p);
\r
3681 #else // !_ATL_MIN_CRT
\r
3682 return _tcsrchr(p, ch);
\r
3683 #endif // !_ATL_MIN_CRT
\r
3688 ///////////////////////////////////////////////////////////////////////////////
\r
3689 // Global functions for loading resources
\r
3691 inline HACCEL AtlLoadAccelerators(ATL::_U_STRINGorID table)
\r
3693 return ::LoadAccelerators(ModuleHelper::GetResourceInstance(), table.m_lpstr);
\r
3696 inline HMENU AtlLoadMenu(ATL::_U_STRINGorID menu)
\r
3698 return ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr);
\r
3701 inline HBITMAP AtlLoadBitmap(ATL::_U_STRINGorID bitmap)
\r
3703 return ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr);
\r
3706 #ifdef OEMRESOURCE
\r
3707 inline HBITMAP AtlLoadSysBitmap(ATL::_U_STRINGorID bitmap)
\r
3710 WORD wID = (WORD)bitmap.m_lpstr;
\r
3711 ATLASSERT(wID >= 32734 && wID <= 32767);
\r
3713 return ::LoadBitmap(NULL, bitmap.m_lpstr);
\r
3715 #endif // OEMRESOURCE
\r
3717 inline HCURSOR AtlLoadCursor(ATL::_U_STRINGorID cursor)
\r
3719 return ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr);
\r
3722 inline HCURSOR AtlLoadSysCursor(LPCTSTR lpCursorName)
\r
3724 #if (WINVER >= 0x0500)
\r
3725 ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT ||
\r
3726 lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE ||
\r
3727 lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW ||
\r
3728 lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL ||
\r
3729 lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP ||
\r
3730 lpCursorName == IDC_HAND);
\r
3731 #else // !(WINVER >= 0x0500)
\r
3732 ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT ||
\r
3733 lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE ||
\r
3734 lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW ||
\r
3735 lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL ||
\r
3736 lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP);
\r
3737 #endif // !(WINVER >= 0x0500)
\r
3738 return ::LoadCursor(NULL, lpCursorName);
\r
3741 inline HICON AtlLoadIcon(ATL::_U_STRINGorID icon)
\r
3743 return ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr);
\r
3746 #ifndef _WIN32_WCE
\r
3747 inline HICON AtlLoadSysIcon(LPCTSTR lpIconName)
\r
3749 #if (WINVER >= 0x0600)
\r
3750 ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION ||
\r
3751 lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO ||
\r
3752 lpIconName == IDI_SHIELD);
\r
3753 #else // !(WINVER >= 0x0600)
\r
3754 ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION ||
\r
3755 lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO);
\r
3756 #endif // !(WINVER >= 0x0600)
\r
3757 return ::LoadIcon(NULL, lpIconName);
\r
3759 #endif // !_WIN32_WCE
\r
3761 inline HBITMAP AtlLoadBitmapImage(ATL::_U_STRINGorID bitmap, UINT fuLoad = LR_DEFAULTCOLOR)
\r
3763 return (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, IMAGE_BITMAP, 0, 0, fuLoad);
\r
3766 inline HCURSOR AtlLoadCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
\r
3768 return (HCURSOR)::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
\r
3771 inline HICON AtlLoadIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
\r
3773 return (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
\r
3776 #ifdef OEMRESOURCE
\r
3777 inline HBITMAP AtlLoadSysBitmapImage(WORD wBitmapID, UINT fuLoad = LR_DEFAULTCOLOR)
\r
3779 ATLASSERT(wBitmapID >= 32734 && wBitmapID <= 32767);
\r
3780 ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file
\r
3781 return (HBITMAP)::LoadImage(NULL, MAKEINTRESOURCE(wBitmapID), IMAGE_BITMAP, 0, 0, fuLoad);
\r
3783 #endif // OEMRESOURCE
\r
3785 inline HCURSOR AtlLoadSysCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
\r
3788 WORD wID = (WORD)cursor.m_lpstr;
\r
3789 ATLASSERT((wID >= 32512 && wID <= 32516) || (wID >= 32640 && wID <= 32648) || (wID == 32650) || (wID == 32651));
\r
3790 ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file
\r
3792 return (HCURSOR)::LoadImage(NULL, cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
\r
3795 inline HICON AtlLoadSysIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
\r
3798 WORD wID = (WORD)icon.m_lpstr;
\r
3799 ATLASSERT(wID >= 32512 && wID <= 32517);
\r
3800 ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0); // this one doesn't load from a file
\r
3802 return (HICON)::LoadImage(NULL, icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
\r
3805 #if (_ATL_VER < 0x0700)
\r
3806 inline int AtlLoadString(UINT uID, LPTSTR lpBuffer, int nBufferMax)
\r
3808 return ::LoadString(_Module.GetResourceInstance(), uID, lpBuffer, nBufferMax);
\r
3810 #endif // (_ATL_VER < 0x0700)
\r
3812 #ifdef _WIN32_WCE // CE only direct access to the resource
\r
3813 inline LPCTSTR AtlLoadString(UINT uID)
\r
3815 LPCTSTR s = (LPCTSTR)::LoadString(ModuleHelper::GetResourceInstance(), uID, NULL, 0);
\r
3816 #ifdef DEBUG // Check for null-termination
\r
3818 // Note: RC -n <file.rc> compiles null-terminated resource strings
\r
3819 ATLASSERT(s[*((WORD*)s -1) - 1] == L'\0');
\r
3823 #endif // _WIN32_WCE
\r
3825 inline bool AtlLoadString(UINT uID, BSTR& bstrText)
\r
3828 ATLASSERT(bstrText == NULL);
\r
3830 LPTSTR lpstrText = NULL;
\r
3832 for(int nLen = 256; ; nLen *= 2)
\r
3834 ATLTRY(lpstrText = new TCHAR[nLen]);
\r
3835 if(lpstrText == NULL)
\r
3837 nRes = ::LoadString(ModuleHelper::GetResourceInstance(), uID, lpstrText, nLen);
\r
3838 if(nRes < nLen - 1)
\r
3840 delete [] lpstrText;
\r
3844 if(lpstrText != NULL)
\r
3847 bstrText = ::SysAllocString(T2OLE(lpstrText));
\r
3848 delete [] lpstrText;
\r
3851 return (bstrText != NULL) ? true : false;
\r
3855 ///////////////////////////////////////////////////////////////////////////////
\r
3856 // Global functions for stock GDI objects
\r
3858 inline HPEN AtlGetStockPen(int nPen)
\r
3860 #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE)
\r
3861 ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN || nPen == DC_PEN);
\r
3863 ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN);
\r
3865 return (HPEN)::GetStockObject(nPen);
\r
3868 inline HBRUSH AtlGetStockBrush(int nBrush)
\r
3870 #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE)
\r
3871 ATLASSERT((nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH) || nBrush == DC_BRUSH);
\r
3873 ATLASSERT(nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH);
\r
3875 return (HBRUSH)::GetStockObject(nBrush);
\r
3878 inline HFONT AtlGetStockFont(int nFont)
\r
3880 #ifndef _WIN32_WCE
\r
3881 ATLASSERT((nFont >= OEM_FIXED_FONT && nFont <= SYSTEM_FIXED_FONT) || nFont == DEFAULT_GUI_FONT);
\r
3882 #else // CE specific
\r
3883 ATLASSERT(nFont == SYSTEM_FONT);
\r
3884 #endif // _WIN32_WCE
\r
3885 return (HFONT)::GetStockObject(nFont);
\r
3888 inline HPALETTE AtlGetStockPalette(int nPalette)
\r
3890 ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported
\r
3891 return (HPALETTE)::GetStockObject(nPalette);
\r
3895 ///////////////////////////////////////////////////////////////////////////////
\r
3896 // Global function for compacting a path by replacing parts with ellipsis
\r
3898 // helper for multi-byte character sets
\r
3899 inline bool _IsDBCSTrailByte(LPCTSTR lpstr, int nChar)
\r
3903 for( ; i > 0; i--)
\r
3905 if(!::IsDBCSLeadByte(lpstr[i - 1]))
\r
3908 return ((nChar > 0) && (((nChar - i) & 1) != 0));
\r
3912 #endif // _UNICODE
\r
3915 inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
\r
3917 ATLASSERT(lpstrOut != NULL);
\r
3918 ATLASSERT(lpstrIn != NULL);
\r
3919 ATLASSERT(cchLen > 0);
\r
3921 LPCTSTR szEllipsis = _T("...");
\r
3922 const int cchEndEllipsis = 3;
\r
3923 const int cchMidEllipsis = 4;
\r
3925 if(lstrlen(lpstrIn) < cchLen)
\r
3927 SecureHelper::strcpy_x(lpstrOut, cchLen, lpstrIn);
\r
3933 // check if the separator is a slash or a backslash
\r
3934 TCHAR chSlash = _T('\\');
\r
3935 for(LPTSTR lpstr = (LPTSTR)lpstrIn; *lpstr != 0; lpstr = ::CharNext(lpstr))
\r
3937 if((*lpstr == _T('/')) || (*lpstr == _T('\\')))
\r
3941 // find the filename portion of the path
\r
3942 LPCTSTR lpstrFileName = lpstrIn;
\r
3943 for(LPCTSTR pPath = lpstrIn; *pPath; pPath = ::CharNext(pPath))
\r
3945 if((pPath[0] == _T('\\') || pPath[0] == _T(':') || pPath[0] == _T('/'))
\r
3946 && pPath[1] && pPath[1] != _T('\\') && pPath[1] != _T('/'))
\r
3947 lpstrFileName = pPath + 1;
\r
3949 int cchFileName = lstrlen(lpstrFileName);
\r
3951 // handle just the filename without a path
\r
3952 if(lpstrFileName == lpstrIn && cchLen > cchEndEllipsis)
\r
3954 bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchLen - cchEndEllipsis - 1) == 0);
\r
3958 if(_IsDBCSTrailByte(lpstrIn, cchLen - cchEndEllipsis))
\r
3959 lpstrOut[cchLen - cchEndEllipsis - 1] = 0;
\r
3960 #endif // _UNICODE
\r
3961 SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
\r
3966 // handle just ellipsis
\r
3967 if((cchLen < (cchMidEllipsis + cchEndEllipsis)))
\r
3969 for(int i = 0; i < cchLen - 1; i++)
\r
3970 lpstrOut[i] = ((i + 1) == cchMidEllipsis) ? chSlash : _T('.');
\r
3971 lpstrOut[cchLen - 1] = 0;
\r
3975 // calc how much we have to copy
\r
3976 int cchToCopy = cchLen - (cchMidEllipsis + cchFileName) - 1;
\r
3982 if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrIn, cchToCopy))
\r
3984 #endif // _UNICODE
\r
3986 bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchToCopy) == 0);
\r
3991 SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
\r
3994 TCHAR szSlash[2] = { chSlash, 0 };
\r
3995 SecureHelper::strcat_x(lpstrOut, cchLen, szSlash);
\r
3999 // add filename (and ellipsis, if needed)
\r
4000 if(cchLen > (cchMidEllipsis + cchFileName))
\r
4002 SecureHelper::strcat_x(lpstrOut, cchLen, lpstrFileName);
\r
4006 cchToCopy = cchLen - cchMidEllipsis - cchEndEllipsis - 1;
\r
4008 if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrFileName, cchToCopy))
\r
4010 #endif // _UNICODE
\r
4011 bRet = (SecureHelper::strncpy_x(&lpstrOut[cchMidEllipsis], cchLen - cchMidEllipsis, lpstrFileName, cchToCopy) == 0);
\r
4013 SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
\r
4019 }; // namespace WTL
\r
4021 #endif // __ATLMISC_H__
\r