crashrpt2
atlmisc.h
1 // Windows Template Library - WTL version 9.10
2 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
3 //
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Public License (http://opensource.org/licenses/MS-PL)
7 // which can be found in the file MS-PL.txt at the root folder.
8 
9 #ifndef __ATLMISC_H__
10 #define __ATLMISC_H__
11 
12 #pragma once
13 
14 #ifndef __ATLAPP_H__
15  #error atlmisc.h requires atlapp.h to be included first
16 #endif
17 
18 
19 #ifdef _ATL_TMP_NO_CSTRING
20  #define _WTL_NO_CSTRING
21 #endif
22 
23 #if defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING)
24  #error Conflicting options - both _WTL_USE_CSTRING and _WTL_NO_CSTRING are defined
25 #endif // defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING)
26 
27 #if !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING)
28  #define _WTL_USE_CSTRING
29 #endif // !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING)
30 
31 #ifndef _WTL_NO_CSTRING
32  #if defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
33  #error Cannot use CString floating point formatting with _ATL_MIN_CRT defined
34  #endif // defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
35 #endif // !_WTL_NO_CSTRING
36 
37 
39 // Classes in this file:
40 //
41 // CSize
42 // CPoint
43 // CRect
44 // CString
45 //
46 // CRecentDocumentListBase<T, t_cchItemLen, t_nFirstID, t_nLastID>
47 // CRecentDocumentList
48 // CFindFile
49 //
50 // Global functions:
51 // AtlGetStockPen()
52 // AtlGetStockBrush()
53 // AtlGetStockFont()
54 // AtlGetStockPalette()
55 //
56 // AtlCompactPath()
57 
58 
59 namespace WTL
60 {
61 
62 #ifndef _WTL_NO_WTYPES
63 
64 // forward declarations
65 class CSize;
66 class CPoint;
67 class CRect;
68 
70 // CSize - Wrapper for Windows SIZE structure.
71 
72 class CSize : public SIZE
73 {
74 public:
75 // Constructors
76  CSize()
77  {
78  cx = 0;
79  cy = 0;
80  }
81 
82  CSize(int initCX, int initCY)
83  {
84  cx = initCX;
85  cy = initCY;
86  }
87 
88  CSize(SIZE initSize)
89  {
90  *(SIZE*)this = initSize;
91  }
92 
93  CSize(POINT initPt)
94  {
95  *(POINT*)this = initPt;
96  }
97 
98  CSize(DWORD dwSize)
99  {
100  cx = (short)LOWORD(dwSize);
101  cy = (short)HIWORD(dwSize);
102  }
103 
104 // Operations
105  BOOL operator ==(SIZE size) const
106  {
107  return (cx == size.cx && cy == size.cy);
108  }
109 
110  BOOL operator !=(SIZE size) const
111  {
112  return (cx != size.cx || cy != size.cy);
113  }
114 
115  void operator +=(SIZE size)
116  {
117  cx += size.cx;
118  cy += size.cy;
119  }
120 
121  void operator -=(SIZE size)
122  {
123  cx -= size.cx;
124  cy -= size.cy;
125  }
126 
127  void SetSize(int CX, int CY)
128  {
129  cx = CX;
130  cy = CY;
131  }
132 
133 // Operators returning CSize values
134  CSize operator +(SIZE size) const
135  {
136  return CSize(cx + size.cx, cy + size.cy);
137  }
138 
139  CSize operator -(SIZE size) const
140  {
141  return CSize(cx - size.cx, cy - size.cy);
142  }
143 
144  CSize operator -() const
145  {
146  return CSize(-cx, -cy);
147  }
148 
149 // Operators returning CPoint values
150  CPoint operator +(POINT point) const;
151  CPoint operator -(POINT point) const;
152 
153 // Operators returning CRect values
154  CRect operator +(const RECT* lpRect) const;
155  CRect operator -(const RECT* lpRect) const;
156 };
157 
158 
160 // CPoint - Wrapper for Windows POINT structure.
161 
162 class CPoint : public POINT
163 {
164 public:
165 // Constructors
166  CPoint()
167  {
168  x = 0;
169  y = 0;
170  }
171 
172  CPoint(int initX, int initY)
173  {
174  x = initX;
175  y = initY;
176  }
177 
178  CPoint(POINT initPt)
179  {
180  *(POINT*)this = initPt;
181  }
182 
183  CPoint(SIZE initSize)
184  {
185  *(SIZE*)this = initSize;
186  }
187 
188  CPoint(DWORD dwPoint)
189  {
190  x = (short)LOWORD(dwPoint);
191  y = (short)HIWORD(dwPoint);
192  }
193 
194 // Operations
195  void Offset(int xOffset, int yOffset)
196  {
197  x += xOffset;
198  y += yOffset;
199  }
200 
201  void Offset(POINT point)
202  {
203  x += point.x;
204  y += point.y;
205  }
206 
207  void Offset(SIZE size)
208  {
209  x += size.cx;
210  y += size.cy;
211  }
212 
213  BOOL operator ==(POINT point) const
214  {
215  return (x == point.x && y == point.y);
216  }
217 
218  BOOL operator !=(POINT point) const
219  {
220  return (x != point.x || y != point.y);
221  }
222 
223  void operator +=(SIZE size)
224  {
225  x += size.cx;
226  y += size.cy;
227  }
228 
229  void operator -=(SIZE size)
230  {
231  x -= size.cx;
232  y -= size.cy;
233  }
234 
235  void operator +=(POINT point)
236  {
237  x += point.x;
238  y += point.y;
239  }
240 
241  void operator -=(POINT point)
242  {
243  x -= point.x;
244  y -= point.y;
245  }
246 
247  void SetPoint(int X, int Y)
248  {
249  x = X;
250  y = Y;
251  }
252 
253 // Operators returning CPoint values
254  CPoint operator +(SIZE size) const
255  {
256  return CPoint(x + size.cx, y + size.cy);
257  }
258 
259  CPoint operator -(SIZE size) const
260  {
261  return CPoint(x - size.cx, y - size.cy);
262  }
263 
264  CPoint operator -() const
265  {
266  return CPoint(-x, -y);
267  }
268 
269  CPoint operator +(POINT point) const
270  {
271  return CPoint(x + point.x, y + point.y);
272  }
273 
274 // Operators returning CSize values
275  CSize operator -(POINT point) const
276  {
277  return CSize(x - point.x, y - point.y);
278  }
279 
280 // Operators returning CRect values
281  CRect operator +(const RECT* lpRect) const;
282  CRect operator -(const RECT* lpRect) const;
283 };
284 
285 
287 // CRect - Wrapper for Windows RECT structure.
288 
289 class CRect : public RECT
290 {
291 public:
292 // Constructors
293  CRect()
294  {
295  left = 0;
296  top = 0;
297  right = 0;
298  bottom = 0;
299  }
300 
301  CRect(int l, int t, int r, int b)
302  {
303  left = l;
304  top = t;
305  right = r;
306  bottom = b;
307  }
308 
309  CRect(const RECT& srcRect)
310  {
311  ::CopyRect(this, &srcRect);
312  }
313 
314  CRect(LPCRECT lpSrcRect)
315  {
316  ::CopyRect(this, lpSrcRect);
317  }
318 
319  CRect(POINT point, SIZE size)
320  {
321  right = (left = point.x) + size.cx;
322  bottom = (top = point.y) + size.cy;
323  }
324 
325  CRect(POINT topLeft, POINT bottomRight)
326  {
327  left = topLeft.x;
328  top = topLeft.y;
329  right = bottomRight.x;
330  bottom = bottomRight.y;
331  }
332 
333 // Attributes (in addition to RECT members)
334  int Width() const
335  {
336  return right - left;
337  }
338 
339  int Height() const
340  {
341  return bottom - top;
342  }
343 
344  CSize Size() const
345  {
346  return CSize(right - left, bottom - top);
347  }
348 
349  CPoint& TopLeft()
350  {
351  return *((CPoint*)this);
352  }
353 
354  CPoint& BottomRight()
355  {
356  return *((CPoint*)this + 1);
357  }
358 
359  const CPoint& TopLeft() const
360  {
361  return *((CPoint*)this);
362  }
363 
364  const CPoint& BottomRight() const
365  {
366  return *((CPoint*)this + 1);
367  }
368 
369  CPoint CenterPoint() const
370  {
371  return CPoint((left + right) / 2, (top + bottom) / 2);
372  }
373 
374  // convert between CRect and LPRECT/LPCRECT (no need for &)
375  operator LPRECT()
376  {
377  return this;
378  }
379 
380  operator LPCRECT() const
381  {
382  return this;
383  }
384 
385  BOOL IsRectEmpty() const
386  {
387  return ::IsRectEmpty(this);
388  }
389 
390  BOOL IsRectNull() const
391  {
392  return (left == 0 && right == 0 && top == 0 && bottom == 0);
393  }
394 
395  BOOL PtInRect(POINT point) const
396  {
397  return ::PtInRect(this, point);
398  }
399 
400 // Operations
401  void SetRect(int x1, int y1, int x2, int y2)
402  {
403  ::SetRect(this, x1, y1, x2, y2);
404  }
405 
406  void SetRect(POINT topLeft, POINT bottomRight)
407  {
408  ::SetRect(this, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
409  }
410 
411  void SetRectEmpty()
412  {
413  ::SetRectEmpty(this);
414  }
415 
416  void CopyRect(LPCRECT lpSrcRect)
417  {
418  ::CopyRect(this, lpSrcRect);
419  }
420 
421  BOOL EqualRect(LPCRECT lpRect) const
422  {
423  return ::EqualRect(this, lpRect);
424  }
425 
426  void InflateRect(int x, int y)
427  {
428  ::InflateRect(this, x, y);
429  }
430 
431  void InflateRect(SIZE size)
432  {
433  ::InflateRect(this, size.cx, size.cy);
434  }
435 
436  void InflateRect(LPCRECT lpRect)
437  {
438  left -= lpRect->left;
439  top -= lpRect->top;
440  right += lpRect->right;
441  bottom += lpRect->bottom;
442  }
443 
444  void InflateRect(int l, int t, int r, int b)
445  {
446  left -= l;
447  top -= t;
448  right += r;
449  bottom += b;
450  }
451 
452  void DeflateRect(int x, int y)
453  {
454  ::InflateRect(this, -x, -y);
455  }
456 
457  void DeflateRect(SIZE size)
458  {
459  ::InflateRect(this, -size.cx, -size.cy);
460  }
461 
462  void DeflateRect(LPCRECT lpRect)
463  {
464  left += lpRect->left;
465  top += lpRect->top;
466  right -= lpRect->right;
467  bottom -= lpRect->bottom;
468  }
469 
470  void DeflateRect(int l, int t, int r, int b)
471  {
472  left += l;
473  top += t;
474  right -= r;
475  bottom -= b;
476  }
477 
478  void OffsetRect(int x, int y)
479  {
480  ::OffsetRect(this, x, y);
481  }
482  void OffsetRect(SIZE size)
483  {
484  ::OffsetRect(this, size.cx, size.cy);
485  }
486 
487  void OffsetRect(POINT point)
488  {
489  ::OffsetRect(this, point.x, point.y);
490  }
491 
492  void NormalizeRect()
493  {
494  int nTemp;
495  if (left > right)
496  {
497  nTemp = left;
498  left = right;
499  right = nTemp;
500  }
501  if (top > bottom)
502  {
503  nTemp = top;
504  top = bottom;
505  bottom = nTemp;
506  }
507  }
508 
509  // absolute position of rectangle
510  void MoveToY(int y)
511  {
512  bottom = Height() + y;
513  top = y;
514  }
515 
516  void MoveToX(int x)
517  {
518  right = Width() + x;
519  left = x;
520  }
521 
522  void MoveToXY(int x, int y)
523  {
524  MoveToX(x);
525  MoveToY(y);
526  }
527 
528  void MoveToXY(POINT pt)
529  {
530  MoveToX(pt.x);
531  MoveToY(pt.y);
532  }
533 
534  // operations that fill '*this' with result
535  BOOL IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2)
536  {
537  return ::IntersectRect(this, lpRect1, lpRect2);
538  }
539 
540  BOOL UnionRect(LPCRECT lpRect1, LPCRECT lpRect2)
541  {
542  return ::UnionRect(this, lpRect1, lpRect2);
543  }
544 
545  BOOL SubtractRect(LPCRECT lpRectSrc1, LPCRECT lpRectSrc2)
546  {
547  return ::SubtractRect(this, lpRectSrc1, lpRectSrc2);
548  }
549 
550 // Additional Operations
551  void operator =(const RECT& srcRect)
552  {
553  ::CopyRect(this, &srcRect);
554  }
555 
556  BOOL operator ==(const RECT& rect) const
557  {
558  return ::EqualRect(this, &rect);
559  }
560 
561  BOOL operator !=(const RECT& rect) const
562  {
563  return !::EqualRect(this, &rect);
564  }
565 
566  void operator +=(POINT point)
567  {
568  ::OffsetRect(this, point.x, point.y);
569  }
570 
571  void operator +=(SIZE size)
572  {
573  ::OffsetRect(this, size.cx, size.cy);
574  }
575 
576  void operator +=(LPCRECT lpRect)
577  {
578  InflateRect(lpRect);
579  }
580 
581  void operator -=(POINT point)
582  {
583  ::OffsetRect(this, -point.x, -point.y);
584  }
585 
586  void operator -=(SIZE size)
587  {
588  ::OffsetRect(this, -size.cx, -size.cy);
589  }
590 
591  void operator -=(LPCRECT lpRect)
592  {
593  DeflateRect(lpRect);
594  }
595 
596  void operator &=(const RECT& rect)
597  {
598  ::IntersectRect(this, this, &rect);
599  }
600 
601  void operator |=(const RECT& rect)
602  {
603  ::UnionRect(this, this, &rect);
604  }
605 
606 // Operators returning CRect values
607  CRect operator +(POINT pt) const
608  {
609  CRect rect(*this);
610  ::OffsetRect(&rect, pt.x, pt.y);
611  return rect;
612  }
613 
614  CRect operator -(POINT pt) const
615  {
616  CRect rect(*this);
617  ::OffsetRect(&rect, -pt.x, -pt.y);
618  return rect;
619  }
620 
621  CRect operator +(LPCRECT lpRect) const
622  {
623  CRect rect(this);
624  rect.InflateRect(lpRect);
625  return rect;
626  }
627 
628  CRect operator +(SIZE size) const
629  {
630  CRect rect(*this);
631  ::OffsetRect(&rect, size.cx, size.cy);
632  return rect;
633  }
634 
635  CRect operator -(SIZE size) const
636  {
637  CRect rect(*this);
638  ::OffsetRect(&rect, -size.cx, -size.cy);
639  return rect;
640  }
641 
642  CRect operator -(LPCRECT lpRect) const
643  {
644  CRect rect(this);
645  rect.DeflateRect(lpRect);
646  return rect;
647  }
648 
649  CRect operator &(const RECT& rect2) const
650  {
651  CRect rect;
652  ::IntersectRect(&rect, this, &rect2);
653  return rect;
654  }
655 
656  CRect operator |(const RECT& rect2) const
657  {
658  CRect rect;
659  ::UnionRect(&rect, this, &rect2);
660  return rect;
661  }
662 
663  CRect MulDiv(int nMultiplier, int nDivisor) const
664  {
665  return CRect(
666  ::MulDiv(left, nMultiplier, nDivisor),
667  ::MulDiv(top, nMultiplier, nDivisor),
668  ::MulDiv(right, nMultiplier, nDivisor),
669  ::MulDiv(bottom, nMultiplier, nDivisor));
670  }
671 };
672 
673 
674 // CSize implementation
675 
676 inline CPoint CSize::operator +(POINT point) const
677 { return CPoint(cx + point.x, cy + point.y); }
678 
679 inline CPoint CSize::operator -(POINT point) const
680 { return CPoint(cx - point.x, cy - point.y); }
681 
682 inline CRect CSize::operator +(const RECT* lpRect) const
683 { return CRect(lpRect) + *this; }
684 
685 inline CRect CSize::operator -(const RECT* lpRect) const
686 { return CRect(lpRect) - *this; }
687 
688 
689 // CPoint implementation
690 
691 inline CRect CPoint::operator +(const RECT* lpRect) const
692 { return CRect(lpRect) + *this; }
693 
694 inline CRect CPoint::operator -(const RECT* lpRect) const
695 { return CRect(lpRect) - *this; }
696 
697 #endif // !_WTL_NO_WTYPES
698 
699 
700 // WTL::CSize or ATL::CSize scalar operators
701 
702 #if !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__))
703 
704 template <class Num>
705 inline CSize operator *(SIZE s, Num n)
706 {
707  return CSize((int)(s.cx * n), (int)(s.cy * n));
708 };
709 
710 template <class Num>
711 inline void operator *=(SIZE & s, Num n)
712 {
713  s = s * n;
714 };
715 
716 template <class Num>
717 inline CSize operator /(SIZE s, Num n)
718 {
719  return CSize((int)(s.cx / n), (int)(s.cy / n));
720 };
721 
722 template <class Num>
723 inline void operator /=(SIZE & s, Num n)
724 {
725  s = s / n;
726 };
727 
728 #endif // !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__))
729 
730 
732 // CString - String class
733 
734 #ifndef _WTL_NO_CSTRING
735 
737 {
738  long nRefs; // reference count
739  int nDataLength;
740  int nAllocLength;
741  // TCHAR data[nAllocLength]
742 
743  TCHAR* data()
744  { return (TCHAR*)(this + 1); }
745 };
746 
747 // Globals
748 
749 // For an empty string, m_pchData will point here
750 // (note: avoids special case of checking for NULL m_pchData)
751 // empty string data (and locked)
752 _declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 };
753 _declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData;
754 _declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData));
755 
756 
757 class CString
758 {
759 public:
760 // Constructors
761  CString()
762  {
763  Init();
764  }
765 
766  CString(const CString& stringSrc)
767  {
768  ATLASSERT(stringSrc.GetData()->nRefs != 0);
769  if (stringSrc.GetData()->nRefs >= 0)
770  {
771  ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
772  m_pchData = stringSrc.m_pchData;
773  InterlockedIncrement(&GetData()->nRefs);
774  }
775  else
776  {
777  Init();
778  *this = stringSrc.m_pchData;
779  }
780  }
781 
782  CString(TCHAR ch, int nRepeat = 1)
783  {
784  ATLASSERT(!_istlead(ch)); // can't create a lead byte string
785  Init();
786  if (nRepeat >= 1)
787  {
788  if(AllocBuffer(nRepeat))
789  {
790 #ifdef _UNICODE
791  for (int i = 0; i < nRepeat; i++)
792  m_pchData[i] = ch;
793 #else
794  memset(m_pchData, ch, nRepeat);
795 #endif
796  }
797  }
798  }
799 
800  CString(LPCTSTR lpsz)
801  {
802  Init();
803  if (lpsz != NULL && HIWORD(lpsz) == NULL)
804  {
805  UINT nID = LOWORD((DWORD_PTR)lpsz);
806  if (!LoadString(nID))
807  ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID);
808  }
809  else
810  {
811  int nLen = SafeStrlen(lpsz);
812  if (nLen != 0)
813  {
814  if(AllocBuffer(nLen))
815  SecureHelper::memcpy_x(m_pchData, (nLen + 1) * sizeof(TCHAR), lpsz, nLen * sizeof(TCHAR));
816  }
817  }
818  }
819 
820 #ifdef _UNICODE
821  CString(LPCSTR lpsz)
822  {
823  Init();
824 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
825  int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0;
826 #else
827  int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0;
828 #endif
829  if (nSrcLen != 0)
830  {
831  if(AllocBuffer(nSrcLen))
832  {
833  _mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
834  ReleaseBuffer();
835  }
836  }
837  }
838 #else // !_UNICODE
839  CString(LPCWSTR lpsz)
840  {
841  Init();
842  int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0;
843  if (nSrcLen != 0)
844  {
845  if(AllocBuffer(nSrcLen * 2))
846  {
847  _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
848  ReleaseBuffer();
849  }
850  }
851  }
852 #endif // !_UNICODE
853 
854  CString(LPCTSTR lpch, int nLength)
855  {
856  Init();
857  if (nLength != 0)
858  {
859  if(AllocBuffer(nLength))
860  SecureHelper::memcpy_x(m_pchData, (nLength + 1) * sizeof(TCHAR), lpch, nLength * sizeof(TCHAR));
861  }
862  }
863 
864 #ifdef _UNICODE
865  CString(LPCSTR lpsz, int nLength)
866  {
867  Init();
868  if (nLength != 0)
869  {
870  if(AllocBuffer(nLength))
871  {
872  int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength + 1);
873  ReleaseBuffer((n >= 0) ? n : -1);
874  }
875  }
876  }
877 #else // !_UNICODE
878  CString(LPCWSTR lpsz, int nLength)
879  {
880  Init();
881  if (nLength != 0)
882  {
883  if(((nLength * 2) > nLength) && AllocBuffer(nLength * 2))
884  {
885  int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData, (nLength * 2) + 1, NULL, NULL);
886  ReleaseBuffer((n >= 0) ? n : -1);
887  }
888  }
889  }
890 #endif // !_UNICODE
891 
892  CString(const unsigned char* lpsz)
893  {
894  Init();
895  *this = (LPCSTR)lpsz;
896  }
897 
898 // Attributes & Operations
899  int GetLength() const // as an array of characters
900  {
901  return GetData()->nDataLength;
902  }
903 
904  BOOL IsEmpty() const
905  {
906  return GetData()->nDataLength == 0;
907  }
908 
909  void Empty() // free up the data
910  {
911  if (GetData()->nDataLength == 0)
912  return;
913 
914  if (GetData()->nRefs >= 0)
915  Release();
916  else
917  *this = _T("");
918 
919  ATLASSERT(GetData()->nDataLength == 0);
920  ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
921  }
922 
923  TCHAR GetAt(int nIndex) const // 0 based
924  {
925  ATLASSERT(nIndex >= 0);
926  ATLASSERT(nIndex < GetData()->nDataLength);
927  return m_pchData[nIndex];
928  }
929 
930  TCHAR operator [](int nIndex) const // same as GetAt
931  {
932  // same as GetAt
933  ATLASSERT(nIndex >= 0);
934  ATLASSERT(nIndex < GetData()->nDataLength);
935  return m_pchData[nIndex];
936  }
937 
938  void SetAt(int nIndex, TCHAR ch)
939  {
940  ATLASSERT(nIndex >= 0);
941  ATLASSERT(nIndex < GetData()->nDataLength);
942 
943  CopyBeforeWrite();
944  m_pchData[nIndex] = ch;
945  }
946 
947  operator LPCTSTR() const // as a C string
948  {
949  return m_pchData;
950  }
951 
952  // overloaded assignment
953  CString& operator =(const CString& stringSrc)
954  {
955  if (m_pchData != stringSrc.m_pchData)
956  {
957  if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0)
958  {
959  // actual copy necessary since one of the strings is locked
960  AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
961  }
962  else
963  {
964  // can just copy references around
965  Release();
966  ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
967  m_pchData = stringSrc.m_pchData;
968  InterlockedIncrement(&GetData()->nRefs);
969  }
970  }
971  return *this;
972  }
973 
974  CString& operator =(TCHAR ch)
975  {
976  ATLASSERT(!_istlead(ch)); // can't set single lead byte
977  AssignCopy(1, &ch);
978  return *this;
979  }
980 
981 #ifdef _UNICODE
982  CString& operator =(char ch)
983  {
984  *this = (TCHAR)ch;
985  return *this;
986  }
987 #endif
988 
989  CString& operator =(LPCTSTR lpsz)
990  {
991  ATLASSERT(lpsz == NULL || _IsValidString(lpsz));
992  AssignCopy(SafeStrlen(lpsz), lpsz);
993  return *this;
994  }
995 
996 #ifdef _UNICODE
997  CString& operator =(LPCSTR lpsz)
998  {
999 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
1000  int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0;
1001 #else
1002  int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0;
1003 #endif
1004  if(AllocBeforeWrite(nSrcLen))
1005  {
1006  _mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
1007  ReleaseBuffer();
1008  }
1009  return *this;
1010  }
1011 #else // !_UNICODE
1012  CString& operator =(LPCWSTR lpsz)
1013  {
1014  int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0;
1015  if(AllocBeforeWrite(nSrcLen * 2))
1016  {
1017  _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
1018  ReleaseBuffer();
1019  }
1020  return *this;
1021  }
1022 #endif // !_UNICODE
1023 
1024  CString& operator =(const unsigned char* lpsz)
1025  {
1026  *this = (LPCSTR)lpsz;
1027  return *this;
1028  }
1029 
1030  // string concatenation
1031  CString& operator +=(const CString& string)
1032  {
1033  ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
1034  return *this;
1035  }
1036 
1037  CString& operator +=(TCHAR ch)
1038  {
1039  ConcatInPlace(1, &ch);
1040  return *this;
1041  }
1042 
1043 #ifdef _UNICODE
1044  CString& operator +=(char ch)
1045  {
1046  *this += (TCHAR)ch;
1047  return *this;
1048  }
1049 #endif
1050 
1051  CString& operator +=(LPCTSTR lpsz)
1052  {
1053  ATLASSERT(lpsz == NULL || _IsValidString(lpsz));
1054  ConcatInPlace(SafeStrlen(lpsz), lpsz);
1055  return *this;
1056  }
1057 
1058  friend CString __stdcall operator +(const CString& string1, const CString& string2);
1059  friend CString __stdcall operator +(const CString& string, TCHAR ch);
1060  friend CString __stdcall operator +(TCHAR ch, const CString& string);
1061 #ifdef _UNICODE
1062  friend CString __stdcall operator +(const CString& string, char ch);
1063  friend CString __stdcall operator +(char ch, const CString& string);
1064 #endif
1065  friend CString __stdcall operator +(const CString& string, LPCTSTR lpsz);
1066  friend CString __stdcall operator +(LPCTSTR lpsz, const CString& string);
1067 
1068  // string comparison
1069  int Compare(LPCTSTR lpsz) const // straight character (MBCS/Unicode aware)
1070  {
1071  return _cstrcmp(m_pchData, lpsz);
1072  }
1073 
1074  int CompareNoCase(LPCTSTR lpsz) const // ignore case (MBCS/Unicode aware)
1075  {
1076  return _cstrcmpi(m_pchData, lpsz);
1077  }
1078 
1079 #ifndef _WIN32_WCE
1080  // CString::Collate is often slower than Compare but is MBSC/Unicode
1081  // aware as well as locale-sensitive with respect to sort order.
1082  int Collate(LPCTSTR lpsz) const // NLS aware
1083  {
1084  return _cstrcoll(m_pchData, lpsz);
1085  }
1086 
1087  int CollateNoCase(LPCTSTR lpsz) const // ignore case
1088  {
1089  return _cstrcolli(m_pchData, lpsz);
1090  }
1091 #endif // !_WIN32_WCE
1092 
1093  // simple sub-string extraction
1094  CString Mid(int nFirst, int nCount) const
1095  {
1096  // out-of-bounds requests return sensible things
1097  if (nFirst < 0)
1098  nFirst = 0;
1099  if (nCount < 0)
1100  nCount = 0;
1101 
1102  if (nFirst + nCount > GetData()->nDataLength)
1103  nCount = GetData()->nDataLength - nFirst;
1104  if (nFirst > GetData()->nDataLength)
1105  nCount = 0;
1106 
1107  CString dest;
1108  AllocCopy(dest, nCount, nFirst, 0);
1109  return dest;
1110  }
1111 
1112  CString Mid(int nFirst) const
1113  {
1114  return Mid(nFirst, GetData()->nDataLength - nFirst);
1115  }
1116 
1117  CString Left(int nCount) const
1118  {
1119  if (nCount < 0)
1120  nCount = 0;
1121  else if (nCount > GetData()->nDataLength)
1122  nCount = GetData()->nDataLength;
1123 
1124  CString dest;
1125  AllocCopy(dest, nCount, 0, 0);
1126  return dest;
1127  }
1128 
1129  CString Right(int nCount) const
1130  {
1131  if (nCount < 0)
1132  nCount = 0;
1133  else if (nCount > GetData()->nDataLength)
1134  nCount = GetData()->nDataLength;
1135 
1136  CString dest;
1137  AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
1138  return dest;
1139  }
1140 
1141  CString SpanIncluding(LPCTSTR lpszCharSet) const // strspn equivalent
1142  {
1143  ATLASSERT(_IsValidString(lpszCharSet));
1144  return Left(_cstrspn(m_pchData, lpszCharSet));
1145  }
1146 
1147  CString SpanExcluding(LPCTSTR lpszCharSet) const // strcspn equivalent
1148  {
1149  ATLASSERT(_IsValidString(lpszCharSet));
1150  return Left(_cstrcspn(m_pchData, lpszCharSet));
1151  }
1152 
1153  // upper/lower/reverse conversion
1154  void MakeUpper()
1155  {
1156  CopyBeforeWrite();
1157  CharUpper(m_pchData);
1158  }
1159 
1160  void MakeLower()
1161  {
1162  CopyBeforeWrite();
1163  CharLower(m_pchData);
1164  }
1165 
1166  void MakeReverse()
1167  {
1168  CopyBeforeWrite();
1169  _cstrrev(m_pchData);
1170  }
1171 
1172  // trimming whitespace (either side)
1173  void TrimRight()
1174  {
1175  CopyBeforeWrite();
1176 
1177  // find beginning of trailing spaces by starting at beginning (DBCS aware)
1178  LPTSTR lpsz = m_pchData;
1179  LPTSTR lpszLast = NULL;
1180  while (*lpsz != _T('\0'))
1181  {
1182  if (_cstrisspace(*lpsz))
1183  {
1184  if (lpszLast == NULL)
1185  lpszLast = lpsz;
1186  }
1187  else
1188  {
1189  lpszLast = NULL;
1190  }
1191  lpsz = ::CharNext(lpsz);
1192  }
1193 
1194  if (lpszLast != NULL)
1195  {
1196  // truncate at trailing space start
1197  *lpszLast = _T('\0');
1198  GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
1199  }
1200  }
1201 
1202  void TrimLeft()
1203  {
1204  CopyBeforeWrite();
1205 
1206  // find first non-space character
1207  LPCTSTR lpsz = m_pchData;
1208  while (_cstrisspace(*lpsz))
1209  lpsz = ::CharNext(lpsz);
1210 
1211  // fix up data and length
1212  int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
1213  SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
1214  GetData()->nDataLength = nDataLength;
1215  }
1216 
1217  // remove continuous occurrences of chTarget starting from right
1218  void TrimRight(TCHAR chTarget)
1219  {
1220  // find beginning of trailing matches
1221  // by starting at beginning (DBCS aware)
1222 
1223  CopyBeforeWrite();
1224  LPTSTR lpsz = m_pchData;
1225  LPTSTR lpszLast = NULL;
1226 
1227  while (*lpsz != _T('\0'))
1228  {
1229  if (*lpsz == chTarget)
1230  {
1231  if (lpszLast == NULL)
1232  lpszLast = lpsz;
1233  }
1234  else
1235  lpszLast = NULL;
1236  lpsz = ::CharNext(lpsz);
1237  }
1238 
1239  if (lpszLast != NULL)
1240  {
1241  // truncate at left-most matching character
1242  *lpszLast = _T('\0');
1243  GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
1244  }
1245  }
1246 
1247  // remove continuous occcurrences of characters in passed string, starting from right
1248  void TrimRight(LPCTSTR lpszTargetList)
1249  {
1250  // find beginning of trailing matches by starting at beginning (DBCS aware)
1251 
1252  CopyBeforeWrite();
1253  LPTSTR lpsz = m_pchData;
1254  LPTSTR lpszLast = NULL;
1255 
1256  while (*lpsz != _T('\0'))
1257  {
1258  TCHAR* pNext = ::CharNext(lpsz);
1259  if(pNext > lpsz + 1)
1260  {
1261  if (_cstrchr_db(lpszTargetList, *lpsz, *(lpsz + 1)) != NULL)
1262  {
1263  if (lpszLast == NULL)
1264  lpszLast = lpsz;
1265  }
1266  else
1267  {
1268  lpszLast = NULL;
1269  }
1270  }
1271  else
1272  {
1273  if (_cstrchr(lpszTargetList, *lpsz) != NULL)
1274  {
1275  if (lpszLast == NULL)
1276  lpszLast = lpsz;
1277  }
1278  else
1279  {
1280  lpszLast = NULL;
1281  }
1282  }
1283 
1284  lpsz = pNext;
1285  }
1286 
1287  if (lpszLast != NULL)
1288  {
1289  // truncate at left-most matching character
1290  *lpszLast = _T('\0');
1291  GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
1292  }
1293  }
1294 
1295  // remove continuous occurrences of chTarget starting from left
1296  void TrimLeft(TCHAR chTarget)
1297  {
1298  // find first non-matching character
1299 
1300  CopyBeforeWrite();
1301  LPCTSTR lpsz = m_pchData;
1302 
1303  while (chTarget == *lpsz)
1304  lpsz = ::CharNext(lpsz);
1305 
1306  if (lpsz != m_pchData)
1307  {
1308  // fix up data and length
1309  int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
1310  SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
1311  GetData()->nDataLength = nDataLength;
1312  }
1313  }
1314 
1315  // remove continuous occcurrences of characters in passed string, starting from left
1316  void TrimLeft(LPCTSTR lpszTargets)
1317  {
1318  // if we're not trimming anything, we're not doing any work
1319  if (SafeStrlen(lpszTargets) == 0)
1320  return;
1321 
1322  CopyBeforeWrite();
1323  LPCTSTR lpsz = m_pchData;
1324 
1325  while (*lpsz != _T('\0'))
1326  {
1327  TCHAR* pNext = ::CharNext(lpsz);
1328  if(pNext > lpsz + 1)
1329  {
1330  if (_cstrchr_db(lpszTargets, *lpsz, *(lpsz + 1)) == NULL)
1331  break;
1332  }
1333  else
1334  {
1335  if (_cstrchr(lpszTargets, *lpsz) == NULL)
1336  break;
1337  }
1338  lpsz = pNext;
1339  }
1340 
1341  if (lpsz != m_pchData)
1342  {
1343  // fix up data and length
1344  int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
1345  SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
1346  GetData()->nDataLength = nDataLength;
1347  }
1348  }
1349 
1350  // advanced manipulation
1351  // replace occurrences of chOld with chNew
1352  int Replace(TCHAR chOld, TCHAR chNew)
1353  {
1354  int nCount = 0;
1355 
1356  // short-circuit the nop case
1357  if (chOld != chNew)
1358  {
1359  // otherwise modify each character that matches in the string
1360  CopyBeforeWrite();
1361  LPTSTR psz = m_pchData;
1362  LPTSTR pszEnd = psz + GetData()->nDataLength;
1363  while (psz < pszEnd)
1364  {
1365  // replace instances of the specified character only
1366  if (*psz == chOld)
1367  {
1368  *psz = chNew;
1369  nCount++;
1370  }
1371  psz = ::CharNext(psz);
1372  }
1373  }
1374  return nCount;
1375  }
1376 
1377  // replace occurrences of substring lpszOld with lpszNew;
1378  // empty lpszNew removes instances of lpszOld
1379  int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
1380  {
1381  // can't have empty or NULL lpszOld
1382 
1383  int nSourceLen = SafeStrlen(lpszOld);
1384  if (nSourceLen == 0)
1385  return 0;
1386  int nReplacementLen = SafeStrlen(lpszNew);
1387 
1388  // loop once to figure out the size of the result string
1389  int nCount = 0;
1390  LPTSTR lpszStart = m_pchData;
1391  LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
1392  LPTSTR lpszTarget = NULL;
1393  while (lpszStart < lpszEnd)
1394  {
1395  while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL)
1396  {
1397  nCount++;
1398  lpszStart = lpszTarget + nSourceLen;
1399  }
1400  lpszStart += lstrlen(lpszStart) + 1;
1401  }
1402 
1403  // if any changes were made, make them
1404  if (nCount > 0)
1405  {
1406  CopyBeforeWrite();
1407 
1408  // if the buffer is too small, just allocate a new buffer (slow but sure)
1409  int nOldLength = GetData()->nDataLength;
1410  int nNewLength = nOldLength + (nReplacementLen - nSourceLen) * nCount;
1411  if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
1412  {
1413  CStringData* pOldData = GetData();
1414  LPTSTR pstr = m_pchData;
1415  if(!AllocBuffer(nNewLength))
1416  return -1;
1417  SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, pOldData->nDataLength * sizeof(TCHAR));
1418  CString::Release(pOldData);
1419  }
1420  // else, we just do it in-place
1421  lpszStart = m_pchData;
1422  lpszEnd = m_pchData + GetData()->nDataLength;
1423 
1424  // loop again to actually do the work
1425  while (lpszStart < lpszEnd)
1426  {
1427  while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL)
1428  {
1429  int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen);
1430  int cchBuffLen = GetData()->nAllocLength - (int)(DWORD_PTR)(lpszTarget - m_pchData);
1431  SecureHelper::memmove_x(lpszTarget + nReplacementLen, (cchBuffLen - nReplacementLen + 1) * sizeof(TCHAR), lpszTarget + nSourceLen, nBalance * sizeof(TCHAR));
1432  SecureHelper::memcpy_x(lpszTarget, (cchBuffLen + 1) * sizeof(TCHAR), lpszNew, nReplacementLen * sizeof(TCHAR));
1433  lpszStart = lpszTarget + nReplacementLen;
1434  lpszStart[nBalance] = _T('\0');
1435  nOldLength += (nReplacementLen - nSourceLen);
1436  }
1437  lpszStart += lstrlen(lpszStart) + 1;
1438  }
1439  ATLASSERT(m_pchData[nNewLength] == _T('\0'));
1440  GetData()->nDataLength = nNewLength;
1441  }
1442 
1443  return nCount;
1444  }
1445 
1446  // remove occurrences of chRemove
1447  int Remove(TCHAR chRemove)
1448  {
1449  CopyBeforeWrite();
1450 
1451  LPTSTR pstrSource = m_pchData;
1452  LPTSTR pstrDest = m_pchData;
1453  LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;
1454 
1455  while (pstrSource < pstrEnd)
1456  {
1457  if (*pstrSource != chRemove)
1458  {
1459  *pstrDest = *pstrSource;
1460  pstrDest = ::CharNext(pstrDest);
1461  }
1462  pstrSource = ::CharNext(pstrSource);
1463  }
1464  *pstrDest = _T('\0');
1465  int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest);
1466  GetData()->nDataLength -= nCount;
1467 
1468  return nCount;
1469  }
1470 
1471  // insert character at zero-based index; concatenates if index is past end of string
1472  int Insert(int nIndex, TCHAR ch)
1473  {
1474  CopyBeforeWrite();
1475 
1476  if (nIndex < 0)
1477  nIndex = 0;
1478 
1479  int nNewLength = GetData()->nDataLength;
1480  if (nIndex > nNewLength)
1481  nIndex = nNewLength;
1482  nNewLength++;
1483 
1484  if (GetData()->nAllocLength < nNewLength)
1485  {
1486  CStringData* pOldData = GetData();
1487  LPTSTR pstr = m_pchData;
1488  if(!AllocBuffer(nNewLength))
1489  return -1;
1490  SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
1491  CString::Release(pOldData);
1492  }
1493 
1494  // move existing bytes down
1495  SecureHelper::memmove_x(m_pchData + nIndex + 1, (GetData()->nAllocLength - nIndex) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR));
1496  m_pchData[nIndex] = ch;
1497  GetData()->nDataLength = nNewLength;
1498 
1499  return nNewLength;
1500  }
1501 
1502  // insert substring at zero-based index; concatenates if index is past end of string
1503  int Insert(int nIndex, LPCTSTR pstr)
1504  {
1505  if (nIndex < 0)
1506  nIndex = 0;
1507 
1508  int nInsertLength = SafeStrlen(pstr);
1509  int nNewLength = GetData()->nDataLength;
1510  if (nInsertLength > 0)
1511  {
1512  CopyBeforeWrite();
1513  if (nIndex > nNewLength)
1514  nIndex = nNewLength;
1515  nNewLength += nInsertLength;
1516 
1517  if (GetData()->nAllocLength < nNewLength)
1518  {
1519  CStringData* pOldData = GetData();
1520  LPTSTR pstrTmp = m_pchData;
1521  if(!AllocBuffer(nNewLength))
1522  return -1;
1523  SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstrTmp, (pOldData->nDataLength + 1) * sizeof(TCHAR));
1524  CString::Release(pOldData);
1525  }
1526 
1527  // move existing bytes down
1528  SecureHelper::memmove_x(m_pchData + nIndex + nInsertLength, (GetData()->nAllocLength + 1 - nIndex - nInsertLength) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR));
1529  SecureHelper::memcpy_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), pstr, nInsertLength * sizeof(TCHAR));
1530  GetData()->nDataLength = nNewLength;
1531  }
1532 
1533  return nNewLength;
1534  }
1535 
1536  // delete nCount characters starting at zero-based index
1537  int Delete(int nIndex, int nCount = 1)
1538  {
1539  if (nIndex < 0)
1540  nIndex = 0;
1541  int nLength = GetData()->nDataLength;
1542  if (nCount > 0 && nIndex < nLength)
1543  {
1544  if((nIndex + nCount) > nLength)
1545  nCount = nLength - nIndex;
1546  CopyBeforeWrite();
1547  int nBytesToCopy = nLength - (nIndex + nCount) + 1;
1548 
1549  SecureHelper::memmove_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
1550  nLength -= nCount;
1551  GetData()->nDataLength = nLength;
1552  }
1553 
1554  return nLength;
1555  }
1556 
1557  // searching (return starting index, or -1 if not found)
1558  // look for a single character match
1559  int Find(TCHAR ch) const // like "C" strchr
1560  {
1561  return Find(ch, 0);
1562  }
1563 
1564  int ReverseFind(TCHAR ch) const
1565  {
1566  // find last single character
1567  LPCTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch);
1568 
1569  // return -1 if not found, distance from beginning otherwise
1570  return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
1571  }
1572 
1573  int Find(TCHAR ch, int nStart) const // starting at index
1574  {
1575  int nLength = GetData()->nDataLength;
1576  if (nStart < 0 || nStart >= nLength)
1577  return -1;
1578 
1579  // find first single character
1580  LPCTSTR lpsz = _cstrchr(m_pchData + nStart, (_TUCHAR)ch);
1581 
1582  // return -1 if not found and index otherwise
1583  return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
1584  }
1585 
1586  int FindOneOf(LPCTSTR lpszCharSet) const
1587  {
1588  ATLASSERT(_IsValidString(lpszCharSet));
1589  LPCTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet);
1590  return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
1591  }
1592 
1593  // look for a specific sub-string
1594  // find a sub-string (like strstr)
1595  int Find(LPCTSTR lpszSub) const // like "C" strstr
1596  {
1597  return Find(lpszSub, 0);
1598  }
1599 
1600  int Find(LPCTSTR lpszSub, int nStart) const // starting at index
1601  {
1602  ATLASSERT(_IsValidString(lpszSub));
1603 
1604  int nLength = GetData()->nDataLength;
1605  if (nStart < 0 || nStart > nLength)
1606  return -1;
1607 
1608  // find first matching substring
1609  LPCTSTR lpsz = _cstrstr(m_pchData + nStart, lpszSub);
1610 
1611  // return -1 for not found, distance from beginning otherwise
1612  return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
1613  }
1614 
1615  // Concatentation for non strings
1616  CString& Append(int n)
1617  {
1618  const int cchBuff = 12;
1619  TCHAR szBuffer[cchBuff] = { 0 };
1620  SecureHelper::wsprintf_x(szBuffer, cchBuff, _T("%d"), n);
1621  ConcatInPlace(SafeStrlen(szBuffer), szBuffer);
1622  return *this;
1623  }
1624 
1625  // simple formatting
1626  // formatting (using wsprintf style formatting)
1627  BOOL __cdecl Format(LPCTSTR lpszFormat, ...)
1628  {
1629  ATLASSERT(_IsValidString(lpszFormat));
1630 
1631  va_list argList;
1632  va_start(argList, lpszFormat);
1633  BOOL bRet = FormatV(lpszFormat, argList);
1634  va_end(argList);
1635  return bRet;
1636  }
1637 
1638  BOOL __cdecl Format(UINT nFormatID, ...)
1639  {
1640  CString strFormat;
1641  BOOL bRet = strFormat.LoadString(nFormatID);
1642  ATLASSERT(bRet != 0);
1643 
1644  va_list argList;
1645  va_start(argList, nFormatID);
1646  bRet = FormatV(strFormat, argList);
1647  va_end(argList);
1648  return bRet;
1649  }
1650 
1651  BOOL FormatV(LPCTSTR lpszFormat, va_list argList)
1652  {
1653  ATLASSERT(_IsValidString(lpszFormat));
1654 
1655  enum _FormatModifiers
1656  {
1657  FORCE_ANSI = 0x10000,
1658  FORCE_UNICODE = 0x20000,
1659  FORCE_INT64 = 0x40000
1660  };
1661 
1662  va_list argListSave = argList;
1663 
1664  // make a guess at the maximum length of the resulting string
1665  int nMaxLen = 0;
1666  for (LPCTSTR lpsz = lpszFormat; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz))
1667  {
1668  // handle '%' character, but watch out for '%%'
1669  if (*lpsz != _T('%') || *(lpsz = ::CharNext(lpsz)) == _T('%'))
1670  {
1671  nMaxLen += (int)(::CharNext(lpsz) - lpsz);
1672  continue;
1673  }
1674 
1675  int nItemLen = 0;
1676 
1677  // handle '%' character with format
1678  int nWidth = 0;
1679  for (; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz))
1680  {
1681  // check for valid flags
1682  if (*lpsz == _T('#'))
1683  nMaxLen += 2; // for '0x'
1684  else if (*lpsz == _T('*'))
1685  nWidth = va_arg(argList, int);
1686  else if (*lpsz == _T('-') || *lpsz == _T('+') || *lpsz == _T('0') || *lpsz == _T(' '))
1687  ;
1688  else // hit non-flag character
1689  break;
1690  }
1691  // get width and skip it
1692  if (nWidth == 0)
1693  {
1694  // width indicated by
1695  nWidth = _cstrtoi(lpsz);
1696  for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
1697  ;
1698  }
1699  ATLASSERT(nWidth >= 0);
1700 
1701  int nPrecision = 0;
1702  if (*lpsz == _T('.'))
1703  {
1704  // skip past '.' separator (width.precision)
1705  lpsz = ::CharNext(lpsz);
1706 
1707  // get precision and skip it
1708  if (*lpsz == _T('*'))
1709  {
1710  nPrecision = va_arg(argList, int);
1711  lpsz = ::CharNext(lpsz);
1712  }
1713  else
1714  {
1715  nPrecision = _cstrtoi(lpsz);
1716  for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
1717  ;
1718  }
1719  ATLASSERT(nPrecision >= 0);
1720  }
1721 
1722  // should be on type modifier or specifier
1723  int nModifier = 0;
1724  if(lpsz[0] == _T('I'))
1725  {
1726  if((lpsz[1] == _T('6')) && (lpsz[2] == _T('4')))
1727  {
1728  lpsz += 3;
1729  nModifier = FORCE_INT64;
1730  }
1731  else if((lpsz[1] == _T('3')) && (lpsz[2] == _T('2')))
1732  {
1733  lpsz += 3;
1734  }
1735  else
1736  {
1737  lpsz++;
1738  if(sizeof(size_t) == 8)
1739  nModifier = FORCE_INT64;
1740  }
1741  }
1742  else
1743  {
1744  switch (*lpsz)
1745  {
1746  // modifiers that affect size
1747  case _T('h'):
1748  nModifier = FORCE_ANSI;
1749  lpsz = ::CharNext(lpsz);
1750  break;
1751  case _T('l'):
1752  nModifier = FORCE_UNICODE;
1753  lpsz = ::CharNext(lpsz);
1754  break;
1755 
1756  // modifiers that do not affect size
1757  case _T('F'):
1758  case _T('N'):
1759  case _T('L'):
1760  lpsz = ::CharNext(lpsz);
1761  break;
1762  }
1763  }
1764 
1765  // now should be on specifier
1766  switch (*lpsz | nModifier)
1767  {
1768  // single characters
1769  case _T('c'):
1770  case _T('C'):
1771  nItemLen = 2;
1772  va_arg(argList, TCHAR);
1773  break;
1774  case _T('c') | FORCE_ANSI:
1775  case _T('C') | FORCE_ANSI:
1776  nItemLen = 2;
1777  va_arg(argList, char);
1778  break;
1779  case _T('c') | FORCE_UNICODE:
1780  case _T('C') | FORCE_UNICODE:
1781  nItemLen = 2;
1782  va_arg(argList, WCHAR);
1783  break;
1784 
1785  // strings
1786  case _T('s'):
1787  {
1788  LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
1789  if (pstrNextArg == NULL)
1790  {
1791  nItemLen = 6; // "(null)"
1792  }
1793  else
1794  {
1795  nItemLen = lstrlen(pstrNextArg);
1796  nItemLen = __max(1, nItemLen);
1797  }
1798  break;
1799  }
1800 
1801  case _T('S'):
1802  {
1803 #ifndef _UNICODE
1804  LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
1805  if (pstrNextArg == NULL)
1806  {
1807  nItemLen = 6; // "(null)"
1808  }
1809  else
1810  {
1811  nItemLen = (int)wcslen(pstrNextArg);
1812  nItemLen = __max(1, nItemLen);
1813  }
1814 #else // _UNICODE
1815  LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
1816  if (pstrNextArg == NULL)
1817  {
1818  nItemLen = 6; // "(null)"
1819  }
1820  else
1821  {
1822 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
1823  nItemLen = ATL::lstrlenA(pstrNextArg);
1824 #else
1825  nItemLen = lstrlenA(pstrNextArg);
1826 #endif
1827  nItemLen = __max(1, nItemLen);
1828  }
1829 #endif // _UNICODE
1830  break;
1831  }
1832 
1833  case _T('s') | FORCE_ANSI:
1834  case _T('S') | FORCE_ANSI:
1835  {
1836  LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
1837  if (pstrNextArg == NULL)
1838  {
1839  nItemLen = 6; // "(null)"
1840  }
1841  else
1842  {
1843 #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
1844  nItemLen = ATL::lstrlenA(pstrNextArg);
1845 #else
1846  nItemLen = lstrlenA(pstrNextArg);
1847 #endif
1848  nItemLen = __max(1, nItemLen);
1849  }
1850  break;
1851  }
1852 
1853  case _T('s') | FORCE_UNICODE:
1854  case _T('S') | FORCE_UNICODE:
1855  {
1856  LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
1857  if (pstrNextArg == NULL)
1858  {
1859  nItemLen = 6; // "(null)"
1860  }
1861  else
1862  {
1863  nItemLen = (int)wcslen(pstrNextArg);
1864  nItemLen = __max(1, nItemLen);
1865  }
1866  break;
1867  }
1868  }
1869 
1870  // adjust nItemLen for strings
1871  if (nItemLen != 0)
1872  {
1873  nItemLen = __max(nItemLen, nWidth);
1874  if (nPrecision != 0)
1875  nItemLen = __min(nItemLen, nPrecision);
1876  }
1877  else
1878  {
1879  switch (*lpsz)
1880  {
1881  // integers
1882  case _T('d'):
1883  case _T('i'):
1884  case _T('u'):
1885  case _T('x'):
1886  case _T('X'):
1887  case _T('o'):
1888  if (nModifier & FORCE_INT64)
1889  va_arg(argList, __int64);
1890  else
1891  va_arg(argList, int);
1892  nItemLen = 32;
1893  nItemLen = __max(nItemLen, nWidth + nPrecision);
1894  break;
1895 
1896 #ifndef _ATL_USE_CSTRING_FLOAT
1897  case _T('e'):
1898  case _T('E'):
1899  case _T('f'):
1900  case _T('g'):
1901  case _T('G'):
1902  ATLASSERT(!"Floating point (%%e, %%E, %%f, %%g, and %%G) is not supported by the WTL::CString class.");
1903 #ifndef _DEBUG
1904  ::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class."));
1905 #ifndef _WIN32_WCE
1906  ::DebugBreak();
1907 #else // CE specific
1908  DebugBreak();
1909 #endif // _WIN32_WCE
1910 #endif // !_DEBUG
1911  break;
1912 #else // _ATL_USE_CSTRING_FLOAT
1913  case _T('e'):
1914  case _T('E'):
1915  case _T('g'):
1916  case _T('G'):
1917  va_arg(argList, double);
1918  nItemLen = 128;
1919  nItemLen = __max(nItemLen, nWidth + nPrecision);
1920  break;
1921  case _T('f'):
1922  {
1923  double f = va_arg(argList, double);
1924  // 312 == strlen("-1+(309 zeroes).")
1925  // 309 zeroes == max precision of a double
1926  // 6 == adjustment in case precision is not specified,
1927  // which means that the precision defaults to 6
1928  int cchLen = __max(nWidth, 312 + nPrecision + 6);
1930  LPTSTR pszTemp = buff.Allocate(cchLen);
1931  if(pszTemp != NULL)
1932  {
1933  SecureHelper::sprintf_x(pszTemp, cchLen, _T("%*.*f"), nWidth, nPrecision + 6, f);
1934  nItemLen = (int)_tcslen(pszTemp);
1935  }
1936  else
1937  {
1938  nItemLen = cchLen;
1939  }
1940  }
1941  break;
1942 #endif // _ATL_USE_CSTRING_FLOAT
1943 
1944  case _T('p'):
1945  va_arg(argList, void*);
1946  nItemLen = 32;
1947  nItemLen = __max(nItemLen, nWidth + nPrecision);
1948  break;
1949 
1950  // no output
1951  case _T('n'):
1952  va_arg(argList, int*);
1953  break;
1954 
1955  default:
1956  ATLASSERT(FALSE); // unknown formatting option
1957  }
1958  }
1959 
1960  // adjust nMaxLen for output nItemLen
1961  nMaxLen += nItemLen;
1962  }
1963 
1964  if(GetBuffer(nMaxLen) == NULL)
1965  return FALSE;
1966 #ifndef _ATL_USE_CSTRING_FLOAT
1967  int nRet = SecureHelper::wvsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave);
1968 #else // _ATL_USE_CSTRING_FLOAT
1969  int nRet = SecureHelper::vsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave);
1970 #endif // _ATL_USE_CSTRING_FLOAT
1971  nRet; // ref
1972  ATLASSERT(nRet <= GetAllocLength());
1973  ReleaseBuffer();
1974 
1975  va_end(argListSave);
1976  return TRUE;
1977  }
1978 
1979  // formatting for localization (uses FormatMessage API)
1980  // formatting (using FormatMessage style formatting)
1981  BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...)
1982  {
1983  // format message into temporary buffer lpszTemp
1984  va_list argList;
1985  va_start(argList, lpszFormat);
1986  LPTSTR lpszTemp = NULL;
1987  BOOL bRet = TRUE;
1988 
1989  if ((::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
1990  lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0) || (lpszTemp == NULL))
1991  bRet = FALSE;
1992 
1993  // assign lpszTemp into the resulting string and free the temporary
1994  *this = lpszTemp;
1995  LocalFree(lpszTemp);
1996  va_end(argList);
1997  return bRet;
1998  }
1999 
2000  BOOL __cdecl FormatMessage(UINT nFormatID, ...)
2001  {
2002  // get format string from string table
2003  CString strFormat;
2004  BOOL bRetTmp = strFormat.LoadString(nFormatID);
2005  bRetTmp; // ref
2006  ATLASSERT(bRetTmp != 0);
2007 
2008  // format message into temporary buffer lpszTemp
2009  va_list argList;
2010  va_start(argList, nFormatID);
2011  LPTSTR lpszTemp = NULL;
2012  BOOL bRet = TRUE;
2013 
2014  if ((::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
2015  strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0) || (lpszTemp == NULL))
2016  bRet = FALSE;
2017 
2018  // assign lpszTemp into the resulting string and free lpszTemp
2019  *this = lpszTemp;
2020  LocalFree(lpszTemp);
2021  va_end(argList);
2022  return bRet;
2023  }
2024 
2025  // Windows support
2026  BOOL LoadString(UINT nID) // load from string resource (255 chars max.)
2027  {
2028 #ifdef _UNICODE
2029  const int CHAR_FUDGE = 1; // one TCHAR unused is good enough
2030 #else
2031  const int CHAR_FUDGE = 2; // two BYTES unused for case of DBC last char
2032 #endif
2033 
2034  // try fixed buffer first (to avoid wasting space in the heap)
2035  TCHAR szTemp[256] = { 0 };
2036  int nCount = sizeof(szTemp) / sizeof(szTemp[0]);
2037  int nLen = _LoadString(nID, szTemp, nCount);
2038  if (nCount - nLen > CHAR_FUDGE)
2039  {
2040  *this = szTemp;
2041  return (nLen > 0);
2042  }
2043 
2044  // try buffer size of 512, then larger size until entire string is retrieved
2045  int nSize = 256;
2046  do
2047  {
2048  nSize += 256;
2049  LPTSTR lpstr = GetBuffer(nSize - 1);
2050  if(lpstr == NULL)
2051  {
2052  nLen = 0;
2053  break;
2054  }
2055  nLen = _LoadString(nID, lpstr, nSize);
2056  } while (nSize - nLen <= CHAR_FUDGE);
2057  ReleaseBuffer();
2058 
2059  return (nLen > 0);
2060  }
2061 
2062 #ifndef _UNICODE
2063  // ANSI <-> OEM support (convert string in place)
2064  void AnsiToOem()
2065  {
2066  CopyBeforeWrite();
2067  ::AnsiToOem(m_pchData, m_pchData);
2068  }
2069 
2070  void OemToAnsi()
2071  {
2072  CopyBeforeWrite();
2073  ::OemToAnsi(m_pchData, m_pchData);
2074  }
2075 #endif
2076 
2077 #ifndef _ATL_NO_COM
2078  // OLE BSTR support (use for OLE automation)
2079  BSTR AllocSysString() const
2080  {
2081 #if defined(_UNICODE) || defined(OLE2ANSI)
2082  BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength);
2083 #else
2084  int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
2085  GetData()->nDataLength, NULL, NULL);
2086  BSTR bstr = ::SysAllocStringLen(NULL, nLen);
2087  if(bstr != NULL)
2088  MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen);
2089 #endif
2090  return bstr;
2091  }
2092 
2093  BSTR SetSysString(BSTR* pbstr) const
2094  {
2095 #if defined(_UNICODE) || defined(OLE2ANSI)
2096  ::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength);
2097 #else
2098  int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
2099  GetData()->nDataLength, NULL, NULL);
2100  if(::SysReAllocStringLen(pbstr, NULL, nLen))
2101  MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen);
2102 #endif
2103  ATLASSERT(*pbstr != NULL);
2104  return *pbstr;
2105  }
2106 #endif // !_ATL_NO_COM
2107 
2108  // Access to string implementation buffer as "C" character array
2109  LPTSTR GetBuffer(int nMinBufLength)
2110  {
2111  ATLASSERT(nMinBufLength >= 0);
2112 
2113  if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
2114  {
2115  // we have to grow the buffer
2116  CStringData* pOldData = GetData();
2117  int nOldLen = GetData()->nDataLength; // AllocBuffer will tromp it
2118  if (nMinBufLength < nOldLen)
2119  nMinBufLength = nOldLen;
2120 
2121  if(!AllocBuffer(nMinBufLength))
2122  return NULL;
2123 
2124  SecureHelper::memcpy_x(m_pchData, (nMinBufLength + 1) * sizeof(TCHAR), pOldData->data(), (nOldLen + 1) * sizeof(TCHAR));
2125  GetData()->nDataLength = nOldLen;
2126  CString::Release(pOldData);
2127  }
2128  ATLASSERT(GetData()->nRefs <= 1);
2129 
2130  // return a pointer to the character storage for this string
2131  ATLASSERT(m_pchData != NULL);
2132  return m_pchData;
2133  }
2134 
2135  void ReleaseBuffer(int nNewLength = -1)
2136  {
2137  CopyBeforeWrite(); // just in case GetBuffer was not called
2138 
2139  if (nNewLength == -1)
2140  nNewLength = lstrlen(m_pchData); // zero terminated
2141 
2142  ATLASSERT(nNewLength <= GetData()->nAllocLength);
2143  GetData()->nDataLength = nNewLength;
2144  m_pchData[nNewLength] = _T('\0');
2145  }
2146 
2147  LPTSTR GetBufferSetLength(int nNewLength)
2148  {
2149  ATLASSERT(nNewLength >= 0);
2150 
2151  if(GetBuffer(nNewLength) == NULL)
2152  return NULL;
2153 
2154  GetData()->nDataLength = nNewLength;
2155  m_pchData[nNewLength] = _T('\0');
2156  return m_pchData;
2157  }
2158 
2159  void FreeExtra()
2160  {
2161  ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
2162  if (GetData()->nDataLength != GetData()->nAllocLength)
2163  {
2164  CStringData* pOldData = GetData();
2165  if(AllocBuffer(GetData()->nDataLength))
2166  {
2167  SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pOldData->data(), pOldData->nDataLength * sizeof(TCHAR));
2168  ATLASSERT(m_pchData[GetData()->nDataLength] == _T('\0'));
2169  CString::Release(pOldData);
2170  }
2171  }
2172  ATLASSERT(GetData() != NULL);
2173  }
2174 
2175  // Use LockBuffer/UnlockBuffer to turn refcounting off
2176  LPTSTR LockBuffer()
2177  {
2178  LPTSTR lpsz = GetBuffer(0);
2179  if(lpsz != NULL)
2180  GetData()->nRefs = -1;
2181  return lpsz;
2182  }
2183 
2184  void UnlockBuffer()
2185  {
2186  ATLASSERT(GetData()->nRefs == -1);
2187  if (GetData() != _atltmpDataNil)
2188  GetData()->nRefs = 1;
2189  }
2190 
2191 // Implementation
2192 public:
2193  ~CString() // free any attached data
2194  {
2195  if (GetData() != _atltmpDataNil)
2196  {
2197  if (InterlockedDecrement(&GetData()->nRefs) <= 0)
2198  delete[] (BYTE*)GetData();
2199  }
2200  }
2201 
2202  int GetAllocLength() const
2203  {
2204  return GetData()->nAllocLength;
2205  }
2206 
2207  static BOOL __stdcall _IsValidString(LPCTSTR lpsz, int /*nLength*/ = -1)
2208  {
2209  return (lpsz != NULL) ? TRUE : FALSE;
2210  }
2211 
2212 protected:
2213  LPTSTR m_pchData; // pointer to ref counted string data
2214 
2215  // implementation helpers
2216  CStringData* GetData() const
2217  {
2218  ATLASSERT(m_pchData != NULL);
2219  return ((CStringData*)m_pchData) - 1;
2220  }
2221 
2222  void Init()
2223  {
2224  m_pchData = _GetEmptyString().m_pchData;
2225  }
2226 
2227  BOOL AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const
2228  {
2229  // will clone the data attached to this string
2230  // allocating 'nExtraLen' characters
2231  // Places results in uninitialized string 'dest'
2232  // Will copy the part or all of original data to start of new string
2233 
2234  BOOL bRet = FALSE;
2235  int nNewLen = nCopyLen + nExtraLen;
2236  if (nNewLen == 0)
2237  {
2238  dest.Init();
2239  bRet = TRUE;
2240  }
2241  else if(nNewLen >= nCopyLen)
2242  {
2243  if(dest.AllocBuffer(nNewLen))
2244  {
2245  SecureHelper::memcpy_x(dest.m_pchData, (nNewLen + 1) * sizeof(TCHAR), m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR));
2246  bRet = TRUE;
2247  }
2248  }
2249 
2250  return bRet;
2251  }
2252 
2253  // always allocate one extra character for '\0' termination
2254  // assumes [optimistically] that data length will equal allocation length
2255  BOOL AllocBuffer(int nLen)
2256  {
2257  ATLASSERT(nLen >= 0);
2258  ATLASSERT(nLen <= INT_MAX - 1); // max size (enough room for 1 extra)
2259 
2260  if (nLen == 0)
2261  {
2262  Init();
2263  }
2264  else
2265  {
2266  CStringData* pData = NULL;
2267  ATLTRY(pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR)]);
2268  if(pData == NULL)
2269  return FALSE;
2270 
2271  pData->nRefs = 1;
2272  pData->data()[nLen] = _T('\0');
2273  pData->nDataLength = nLen;
2274  pData->nAllocLength = nLen;
2275  m_pchData = pData->data();
2276  }
2277 
2278  return TRUE;
2279  }
2280 
2281  // Assignment operators
2282  // All assign a new value to the string
2283  // (a) first see if the buffer is big enough
2284  // (b) if enough room, copy on top of old buffer, set size and type
2285  // (c) otherwise free old string data, and create a new one
2286  //
2287  // All routines return the new string (but as a 'const CString&' so that
2288  // assigning it again will cause a copy, eg: s1 = s2 = "hi there".
2289  //
2290  void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
2291  {
2292  if(AllocBeforeWrite(nSrcLen))
2293  {
2294  SecureHelper::memcpy_x(m_pchData, (nSrcLen + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR));
2295  GetData()->nDataLength = nSrcLen;
2296  m_pchData[nSrcLen] = _T('\0');
2297  }
2298  }
2299 
2300  // Concatenation
2301  // NOTE: "operator +" is done as friend functions for simplicity
2302  // There are three variants:
2303  // CString + CString
2304  // and for ? = TCHAR, LPCTSTR
2305  // CString + ?
2306  // ? + CString
2307  BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data)
2308  {
2309  // -- master concatenation routine
2310  // Concatenate two sources
2311  // -- assume that 'this' is a new CString object
2312 
2313  BOOL bRet = TRUE;
2314  int nNewLen = nSrc1Len + nSrc2Len;
2315  if(nNewLen < nSrc1Len || nNewLen < nSrc2Len)
2316  {
2317  bRet = FALSE;
2318  }
2319  else if(nNewLen != 0)
2320  {
2321  bRet = AllocBuffer(nNewLen);
2322  if (bRet)
2323  {
2324  SecureHelper::memcpy_x(m_pchData, (nNewLen + 1) * sizeof(TCHAR), lpszSrc1Data, nSrc1Len * sizeof(TCHAR));
2325  SecureHelper::memcpy_x(m_pchData + nSrc1Len, (nNewLen + 1 - nSrc1Len) * sizeof(TCHAR), lpszSrc2Data, nSrc2Len * sizeof(TCHAR));
2326  }
2327  }
2328  return bRet;
2329  }
2330 
2331  void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
2332  {
2333  // -- the main routine for += operators
2334 
2335  // concatenating an empty string is a no-op!
2336  if (nSrcLen == 0)
2337  return;
2338 
2339  // if the buffer is too small, or we have a width mis-match, just
2340  // allocate a new buffer (slow but sure)
2341  if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
2342  {
2343  // we have to grow the buffer, use the ConcatCopy routine
2344  CStringData* pOldData = GetData();
2345  if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData))
2346  {
2347  ATLASSERT(pOldData != NULL);
2348  CString::Release(pOldData);
2349  }
2350  }
2351  else
2352  {
2353  // fast concatenation when buffer big enough
2354  SecureHelper::memcpy_x(m_pchData + GetData()->nDataLength, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR));
2355  GetData()->nDataLength += nSrcLen;
2356  ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
2357  m_pchData[GetData()->nDataLength] = _T('\0');
2358  }
2359  }
2360 
2361  void CopyBeforeWrite()
2362  {
2363  if (GetData()->nRefs > 1)
2364  {
2365  CStringData* pData = GetData();
2366  Release();
2367  if(AllocBuffer(pData->nDataLength))
2368  SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR));
2369  }
2370  ATLASSERT(GetData()->nRefs <= 1);
2371  }
2372 
2373  BOOL AllocBeforeWrite(int nLen)
2374  {
2375  BOOL bRet = TRUE;
2376  if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
2377  {
2378  Release();
2379  bRet = AllocBuffer(nLen);
2380  }
2381  ATLASSERT(GetData()->nRefs <= 1);
2382  return bRet;
2383  }
2384 
2385  void Release()
2386  {
2387  if (GetData() != _atltmpDataNil)
2388  {
2389  ATLASSERT(GetData()->nRefs != 0);
2390  if (InterlockedDecrement(&GetData()->nRefs) <= 0)
2391  delete[] (BYTE*)GetData();
2392  Init();
2393  }
2394  }
2395 
2396  static void PASCAL Release(CStringData* pData)
2397  {
2398  if (pData != _atltmpDataNil)
2399  {
2400  ATLASSERT(pData->nRefs != 0);
2401  if (InterlockedDecrement(&pData->nRefs) <= 0)
2402  delete[] (BYTE*)pData;
2403  }
2404  }
2405 
2406  static int PASCAL SafeStrlen(LPCTSTR lpsz)
2407  {
2408  return (lpsz == NULL) ? 0 : lstrlen(lpsz);
2409  }
2410 
2411  static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf)
2412  {
2413 #ifdef _DEBUG
2414  // LoadString without annoying warning from the Debug kernel if the
2415  // segment containing the string is not present
2416  if (::FindResource(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE((nID >> 4) + 1), RT_STRING) == NULL)
2417  {
2418  lpszBuf[0] = _T('\0');
2419  return 0; // not found
2420  }
2421 #endif // _DEBUG
2422 
2423  int nLen = ::LoadString(ModuleHelper::GetResourceInstance(), nID, lpszBuf, nMaxBuf);
2424  if (nLen == 0)
2425  lpszBuf[0] = _T('\0');
2426 
2427  return nLen;
2428  }
2429 
2430  static const CString& __stdcall _GetEmptyString()
2431  {
2432  return *(CString*)&_atltmpPchNil;
2433  }
2434 
2435 // CString conversion helpers
2436  static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
2437  {
2438  if (count == 0 && mbstr != NULL)
2439  return 0;
2440 
2441  int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL);
2442  ATLASSERT(mbstr == NULL || result <= (int)count);
2443  if ((mbstr != NULL) && (result > 0))
2444  mbstr[result - 1] = 0;
2445  return result;
2446  }
2447 
2448  static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
2449  {
2450  if (count == 0 && wcstr != NULL)
2451  return 0;
2452 
2453  int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count);
2454  ATLASSERT(wcstr == NULL || result <= (int)count);
2455  if ((wcstr != NULL) && (result > 0))
2456  wcstr[result - 1] = 0;
2457  return result;
2458  }
2459 
2460 // Helpers to avoid CRT startup code
2461 #ifdef _ATL_MIN_CRT
2462  static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
2463  {
2464  // strchr for '\0' should succeed
2465  while (*p != 0)
2466  {
2467  if (*p == ch)
2468  break;
2469  p = ::CharNext(p);
2470  }
2471  return (*p == ch) ? p : NULL;
2472  }
2473 
2474  static TCHAR* _cstrrev(TCHAR* pStr)
2475  {
2476  // optimize NULL, zero-length, and single-char case
2477  if ((pStr == NULL) || (pStr[0] == _T('\0')) || (pStr[1] == _T('\0')))
2478  return pStr;
2479 
2480  TCHAR* p = pStr;
2481 
2482  while (*p != 0)
2483  {
2484  TCHAR* pNext = ::CharNext(p);
2485  if(pNext > p + 1)
2486  {
2487  char p1 = *(char*)p;
2488  *(char*)p = *(char*)(p + 1);
2489  *(char*)(p + 1) = p1;
2490  }
2491  p = pNext;
2492  }
2493 
2494  p--;
2495  TCHAR* q = pStr;
2496 
2497  while (q < p)
2498  {
2499  TCHAR t = *q;
2500  *q = *p;
2501  *p = t;
2502  q++;
2503  p--;
2504  }
2505  return pStr;
2506  }
2507 
2508  static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
2509  {
2510  int nLen = lstrlen(pCharSet);
2511  if (nLen == 0)
2512  return (TCHAR*)pStr;
2513 
2514  const TCHAR* pRet = NULL;
2515  const TCHAR* pCur = pStr;
2516  while((pCur = _cstrchr(pCur, *pCharSet)) != NULL)
2517  {
2518  if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0)
2519  {
2520  pRet = pCur;
2521  break;
2522  }
2523  pCur = ::CharNext(pCur);
2524  }
2525  return pRet;
2526  }
2527 
2528  static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
2529  {
2530  int nRet = 0;
2531  const TCHAR* p = pStr;
2532  while (*p != 0)
2533  {
2534  const TCHAR* pNext = ::CharNext(p);
2535  if(pNext > p + 1)
2536  {
2537  if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL)
2538  break;
2539  nRet += 2;
2540  }
2541  else
2542  {
2543  if(_cstrchr(pCharSet, *p) == NULL)
2544  break;
2545  nRet++;
2546  }
2547  p = pNext;
2548  }
2549  return nRet;
2550  }
2551 
2552  static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
2553  {
2554  int nRet = 0;
2555  TCHAR* p = (TCHAR*)pStr;
2556  while (*p != 0)
2557  {
2558  TCHAR* pNext = ::CharNext(p);
2559  if(pNext > p + 1)
2560  {
2561  if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL)
2562  break;
2563  nRet += 2;
2564  }
2565  else
2566  {
2567  if(_cstrchr(pCharSet, *p) != NULL)
2568  break;
2569  nRet++;
2570  }
2571  p = pNext;
2572  }
2573  return nRet;
2574  }
2575 
2576  static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
2577  {
2578  int n = _cstrcspn(p, lpszCharSet);
2579  return (p[n] != 0) ? &p[n] : NULL;
2580  }
2581 
2582  static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
2583  {
2584  return lstrcmp(pstrOne, pstrOther);
2585  }
2586 
2587  static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
2588  {
2589  return lstrcmpi(pstrOne, pstrOther);
2590  }
2591 
2592  static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
2593  {
2594  int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1);
2595  ATLASSERT(nRet != 0);
2596  return nRet - 2; // convert to strcmp convention
2597  }
2598 
2599  static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
2600  {
2601  int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1);
2602  ATLASSERT(nRet != 0);
2603  return nRet - 2; // convert to strcmp convention
2604  }
2605 #else // !_ATL_MIN_CRT
2606  static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
2607  {
2608  return _tcschr(p, ch);
2609  }
2610 
2611  static TCHAR* _cstrrev(TCHAR* pStr)
2612  {
2613  return _tcsrev(pStr);
2614  }
2615 
2616  static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
2617  {
2618  return _tcsstr(pStr, pCharSet);
2619  }
2620 
2621  static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
2622  {
2623  return (int)_tcsspn(pStr, pCharSet);
2624  }
2625 
2626  static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
2627  {
2628  return (int)_tcscspn(pStr, pCharSet);
2629  }
2630 
2631  static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
2632  {
2633  return _tcspbrk(p, lpszCharSet);
2634  }
2635 
2636  static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
2637  {
2638  return _tcscmp(pstrOne, pstrOther);
2639  }
2640 
2641  static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
2642  {
2643  return _tcsicmp(pstrOne, pstrOther);
2644  }
2645 
2646 #ifndef _WIN32_WCE
2647  static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
2648  {
2649  return _tcscoll(pstrOne, pstrOther);
2650  }
2651 
2652  static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
2653  {
2654  return _tcsicoll(pstrOne, pstrOther);
2655  }
2656 #endif // !_WIN32_WCE
2657 #endif // !_ATL_MIN_CRT
2658 
2659  static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
2660  {
2661  return MinCrtHelper::_strrchr(p, ch);
2662  }
2663 
2664  static int _cstrisdigit(TCHAR ch)
2665  {
2666  return MinCrtHelper::_isdigit(ch);
2667  }
2668 
2669  static int _cstrisspace(TCHAR ch)
2670  {
2671  return MinCrtHelper::_isspace(ch);
2672  }
2673 
2674  static int _cstrtoi(const TCHAR* nptr)
2675  {
2676  return MinCrtHelper::_atoi(nptr);
2677  }
2678 
2679  static const TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2)
2680  {
2681  const TCHAR* lpsz = NULL;
2682  while (*p != 0)
2683  {
2684  if (*p == ch1 && *(p + 1) == ch2)
2685  {
2686  lpsz = p;
2687  break;
2688  }
2689  p = ::CharNext(p);
2690  }
2691  return lpsz;
2692  }
2693 };
2694 
2695 
2696 // Compare helpers
2697 
2698 inline bool __stdcall operator ==(const CString& s1, const CString& s2)
2699 { return s1.Compare(s2) == 0; }
2700 
2701 inline bool __stdcall operator ==(const CString& s1, LPCTSTR s2)
2702 { return s1.Compare(s2) == 0; }
2703 
2704 inline bool __stdcall operator ==(LPCTSTR s1, const CString& s2)
2705 { return s2.Compare(s1) == 0; }
2706 
2707 inline bool __stdcall operator !=(const CString& s1, const CString& s2)
2708 { return s1.Compare(s2) != 0; }
2709 
2710 inline bool __stdcall operator !=(const CString& s1, LPCTSTR s2)
2711 { return s1.Compare(s2) != 0; }
2712 
2713 inline bool __stdcall operator !=(LPCTSTR s1, const CString& s2)
2714 { return s2.Compare(s1) != 0; }
2715 
2716 inline bool __stdcall operator <(const CString& s1, const CString& s2)
2717 { return s1.Compare(s2) < 0; }
2718 
2719 inline bool __stdcall operator <(const CString& s1, LPCTSTR s2)
2720 { return s1.Compare(s2) < 0; }
2721 
2722 inline bool __stdcall operator <(LPCTSTR s1, const CString& s2)
2723 { return s2.Compare(s1) > 0; }
2724 
2725 inline bool __stdcall operator >(const CString& s1, const CString& s2)
2726 { return s1.Compare(s2) > 0; }
2727 
2728 inline bool __stdcall operator >(const CString& s1, LPCTSTR s2)
2729 { return s1.Compare(s2) > 0; }
2730 
2731 inline bool __stdcall operator >(LPCTSTR s1, const CString& s2)
2732 { return s2.Compare(s1) < 0; }
2733 
2734 inline bool __stdcall operator <=(const CString& s1, const CString& s2)
2735 { return s1.Compare(s2) <= 0; }
2736 
2737 inline bool __stdcall operator <=(const CString& s1, LPCTSTR s2)
2738 { return s1.Compare(s2) <= 0; }
2739 
2740 inline bool __stdcall operator <=(LPCTSTR s1, const CString& s2)
2741 { return s2.Compare(s1) >= 0; }
2742 
2743 inline bool __stdcall operator >=(const CString& s1, const CString& s2)
2744 { return s1.Compare(s2) >= 0; }
2745 
2746 inline bool __stdcall operator >=(const CString& s1, LPCTSTR s2)
2747 { return s1.Compare(s2) >= 0; }
2748 
2749 inline bool __stdcall operator >=(LPCTSTR s1, const CString& s2)
2750 { return s2.Compare(s1) <= 0; }
2751 
2752 
2753 // CString "operator +" functions
2754 
2755 inline CString __stdcall operator +(const CString& string1, const CString& string2)
2756 {
2757  CString s;
2758  s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData);
2759  return s;
2760 }
2761 
2762 inline CString __stdcall operator +(const CString& string, TCHAR ch)
2763 {
2764  CString s;
2765  s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, 1, &ch);
2766  return s;
2767 }
2768 
2769 inline CString __stdcall operator +(TCHAR ch, const CString& string)
2770 {
2771  CString s;
2772  s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
2773  return s;
2774 }
2775 
2776 #ifdef _UNICODE
2777 inline CString __stdcall operator +(const CString& string, char ch)
2778 {
2779  return string + (TCHAR)ch;
2780 }
2781 
2782 inline CString __stdcall operator +(char ch, const CString& string)
2783 {
2784  return (TCHAR)ch + string;
2785 }
2786 #endif // _UNICODE
2787 
2788 inline CString __stdcall operator +(const CString& string, LPCTSTR lpsz)
2789 {
2790  ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz));
2791  CString s;
2792  s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz);
2793  return s;
2794 }
2795 
2796 inline CString __stdcall operator +(LPCTSTR lpsz, const CString& string)
2797 {
2798  ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz));
2799  CString s;
2800  s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData);
2801  return s;
2802 }
2803 
2804 #endif // !_WTL_NO_CSTRING
2805 
2806 
2808 // CRecentDocumentList - MRU List Support
2809 
2810 #ifndef _WIN32_WCE
2811 
2812 #ifndef _WTL_MRUEMPTY_TEXT
2813  #define _WTL_MRUEMPTY_TEXT _T("(empty)")
2814 #endif
2815 
2816 // forward declaration
2817 inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen);
2818 
2819 template <class T, int t_cchItemLen = MAX_PATH, int t_nFirstID = ID_FILE_MRU_FIRST, int t_nLastID = ID_FILE_MRU_LAST>
2821 {
2822 public:
2823 // Declarations
2824  struct _DocEntry
2825  {
2826  TCHAR szDocName[t_cchItemLen];
2827  bool operator ==(const _DocEntry& de) const
2828  { return (lstrcmpi(szDocName, de.szDocName) == 0); }
2829  };
2830 
2831  enum
2832  {
2833  m_nMaxEntries_Min = 2,
2834  m_nMaxEntries_Max = t_nLastID - t_nFirstID + 1,
2835  m_cchMaxItemLen_Min = 6,
2836  m_cchMaxItemLen_Max = t_cchItemLen,
2837  m_cchItemNameLen = 11
2838  };
2839 
2840 // Data members
2841  ATL::CSimpleArray<_DocEntry> m_arrDocs;
2842  int m_nMaxEntries; // default is 4
2843  HMENU m_hMenu;
2844 
2845  TCHAR m_szNoEntries[t_cchItemLen];
2846 
2847  int m_cchMaxItemLen;
2848 
2849 // Constructor
2850  CRecentDocumentListBase() : m_hMenu(NULL), m_nMaxEntries(4), m_cchMaxItemLen(-1)
2851  {
2852  // These ASSERTs verify values of the template arguments
2853  ATLASSERT(t_cchItemLen > m_cchMaxItemLen_Min);
2854  ATLASSERT(m_nMaxEntries_Max > m_nMaxEntries_Min);
2855  }
2856 
2857 // Attributes
2858  HMENU GetMenuHandle() const
2859  {
2860  return m_hMenu;
2861  }
2862 
2863  void SetMenuHandle(HMENU hMenu)
2864  {
2865  ATLASSERT(hMenu == NULL || ::IsMenu(hMenu));
2866  m_hMenu = hMenu;
2867  if(m_hMenu == NULL || (::GetMenuString(m_hMenu, t_nFirstID, m_szNoEntries, t_cchItemLen, MF_BYCOMMAND) == 0))
2868  {
2869  T* pT = static_cast<T*>(this);
2870  pT; // avoid level 4 warning
2871  SecureHelper::strncpy_x(m_szNoEntries, _countof(m_szNoEntries), pT->GetMRUEmptyText(), _TRUNCATE);
2872  }
2873  }
2874 
2875  int GetMaxEntries() const
2876  {
2877  return m_nMaxEntries;
2878  }
2879 
2880  void SetMaxEntries(int nMaxEntries)
2881  {
2882  ATLASSERT(nMaxEntries >= m_nMaxEntries_Min && nMaxEntries <= m_nMaxEntries_Max);
2883  if(nMaxEntries < m_nMaxEntries_Min)
2884  nMaxEntries = m_nMaxEntries_Min;
2885  else if(nMaxEntries > m_nMaxEntries_Max)
2886  nMaxEntries = m_nMaxEntries_Max;
2887  m_nMaxEntries = nMaxEntries;
2888  }
2889 
2890  int GetMaxItemLength() const
2891  {
2892  return m_cchMaxItemLen;
2893  }
2894 
2895  void SetMaxItemLength(int cchMaxLen)
2896  {
2897  ATLASSERT((cchMaxLen >= m_cchMaxItemLen_Min && cchMaxLen <= m_cchMaxItemLen_Max) || cchMaxLen == -1);
2898  if(cchMaxLen != -1)
2899  {
2900  if(cchMaxLen < m_cchMaxItemLen_Min)
2901  cchMaxLen = m_cchMaxItemLen_Min;
2902  else if(cchMaxLen > m_cchMaxItemLen_Max)
2903  cchMaxLen = m_cchMaxItemLen_Max;
2904  }
2905  m_cchMaxItemLen = cchMaxLen;
2906  T* pT = static_cast<T*>(this);
2907  pT->UpdateMenu();
2908  }
2909 
2910 // Operations
2911  BOOL AddToList(LPCTSTR lpstrDocName)
2912  {
2913  _DocEntry de;
2914  errno_t nRet = SecureHelper::strncpy_x(de.szDocName, _countof(de.szDocName), lpstrDocName, _TRUNCATE);
2915  if(nRet != 0 && nRet != STRUNCATE)
2916  return FALSE;
2917 
2918  for(int i = 0; i < m_arrDocs.GetSize(); i++)
2919  {
2920  if(lstrcmpi(m_arrDocs[i].szDocName, lpstrDocName) == 0)
2921  {
2922  m_arrDocs.RemoveAt(i);
2923  break;
2924  }
2925  }
2926 
2927  if(m_arrDocs.GetSize() == m_nMaxEntries)
2928  m_arrDocs.RemoveAt(0);
2929 
2930  BOOL bRet = m_arrDocs.Add(de);
2931  if(bRet)
2932  {
2933  T* pT = static_cast<T*>(this);
2934  bRet = pT->UpdateMenu();
2935  }
2936  return bRet;
2937  }
2938 
2939  // This function is deprecated because it is not safe.
2940  // Use the version below that accepts the buffer length.
2941 #if (_MSC_VER >= 1300)
2942  __declspec(deprecated)
2943 #endif
2944  BOOL GetFromList(int /*nItemID*/, LPTSTR /*lpstrDocName*/)
2945  {
2946  ATLASSERT(FALSE);
2947  return FALSE;
2948  }
2949 
2950  BOOL GetFromList(int nItemID, LPTSTR lpstrDocName, int cchLength)
2951  {
2952  int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
2953  if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
2954  return FALSE;
2955  if(lstrlen(m_arrDocs[nIndex].szDocName) >= cchLength)
2956  return FALSE;
2957  SecureHelper::strcpy_x(lpstrDocName, cchLength, m_arrDocs[nIndex].szDocName);
2958 
2959  return TRUE;
2960  }
2961 
2962 #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
2963  BOOL GetFromList(int nItemID, _CSTRING_NS::CString& strDocName)
2964  {
2965  int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
2966  if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
2967  return FALSE;
2968  strDocName = m_arrDocs[nIndex].szDocName;
2969  return TRUE;
2970  }
2971 #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
2972 
2973  BOOL RemoveFromList(int nItemID)
2974  {
2975  int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
2976  BOOL bRet = m_arrDocs.RemoveAt(nIndex);
2977  if(bRet)
2978  {
2979  T* pT = static_cast<T*>(this);
2980  bRet = pT->UpdateMenu();
2981  }
2982  return bRet;
2983  }
2984 
2985  BOOL MoveToTop(int nItemID)
2986  {
2987  int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
2988  if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
2989  return FALSE;
2990  _DocEntry de;
2991  de = m_arrDocs[nIndex];
2992  m_arrDocs.RemoveAt(nIndex);
2993  BOOL bRet = m_arrDocs.Add(de);
2994  if(bRet)
2995  {
2996  T* pT = static_cast<T*>(this);
2997  bRet = pT->UpdateMenu();
2998  }
2999  return bRet;
3000  }
3001 
3002  BOOL ReadFromRegistry(LPCTSTR lpstrRegKey)
3003  {
3004  T* pT = static_cast<T*>(this);
3005  CRegKeyEx rkParent;
3006  CRegKeyEx rk;
3007 
3008  LONG lRet = rkParent.Open(HKEY_CURRENT_USER, lpstrRegKey);
3009  if(lRet != ERROR_SUCCESS)
3010  return FALSE;
3011  lRet = rk.Open(rkParent, pT->GetRegKeyName());
3012  if(lRet != ERROR_SUCCESS)
3013  return FALSE;
3014 
3015  DWORD dwRet = 0;
3016  lRet = rk.QueryDWORDValue(pT->GetRegCountName(), dwRet);
3017  if(lRet != ERROR_SUCCESS)
3018  return FALSE;
3019  SetMaxEntries(dwRet);
3020 
3021  m_arrDocs.RemoveAll();
3022 
3023  TCHAR szRetString[t_cchItemLen] = { 0 };
3024  _DocEntry de;
3025 
3026  for(int nItem = m_nMaxEntries; nItem > 0; nItem--)
3027  {
3028  TCHAR szBuff[m_cchItemNameLen] = { 0 };
3029  SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
3030  ULONG ulCount = t_cchItemLen;
3031  lRet = rk.QueryStringValue(szBuff, szRetString, &ulCount);
3032  if(lRet == ERROR_SUCCESS)
3033  {
3034  SecureHelper::strcpy_x(de.szDocName, _countof(de.szDocName), szRetString);
3035  m_arrDocs.Add(de);
3036  }
3037  }
3038 
3039  rk.Close();
3040  rkParent.Close();
3041 
3042  return pT->UpdateMenu();
3043  }
3044 
3045  BOOL WriteToRegistry(LPCTSTR lpstrRegKey)
3046  {
3047  T* pT = static_cast<T*>(this);
3048  pT; // avoid level 4 warning
3049  CRegKeyEx rkParent;
3050  CRegKeyEx rk;
3051 
3052  LONG lRet = rkParent.Create(HKEY_CURRENT_USER, lpstrRegKey);
3053  if(lRet != ERROR_SUCCESS)
3054  return FALSE;
3055  lRet = rk.Create(rkParent, pT->GetRegKeyName());
3056  if(lRet != ERROR_SUCCESS)
3057  return FALSE;
3058 
3059  lRet = rk.SetDWORDValue(pT->GetRegCountName(), m_nMaxEntries);
3060  ATLASSERT(lRet == ERROR_SUCCESS);
3061 
3062  // set new values
3063  int nItem;
3064  for(nItem = m_arrDocs.GetSize(); nItem > 0; nItem--)
3065  {
3066  TCHAR szBuff[m_cchItemNameLen] = { 0 };
3067  SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
3068  TCHAR szDocName[t_cchItemLen] = { 0 };
3069  GetFromList(t_nFirstID + nItem - 1, szDocName, t_cchItemLen);
3070  lRet = rk.SetStringValue(szBuff, szDocName);
3071  ATLASSERT(lRet == ERROR_SUCCESS);
3072  }
3073 
3074  // delete unused keys
3075  for(nItem = m_arrDocs.GetSize() + 1; nItem <= m_nMaxEntries_Max; nItem++)
3076  {
3077  TCHAR szBuff[m_cchItemNameLen] = { 0 };
3078  SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
3079  rk.DeleteValue(szBuff);
3080  }
3081 
3082  rk.Close();
3083  rkParent.Close();
3084 
3085  return TRUE;
3086  }
3087 
3088 // Implementation
3089  BOOL UpdateMenu()
3090  {
3091  if(m_hMenu == NULL)
3092  return FALSE;
3093  ATLASSERT(::IsMenu(m_hMenu));
3094 
3095  int nItems = ::GetMenuItemCount(m_hMenu);
3096  int nInsertPoint = 0;
3097  for(int i = 0; i < nItems; i++)
3098  {
3099  CMenuItemInfo mi;
3100  mi.fMask = MIIM_ID;
3101  ::GetMenuItemInfo(m_hMenu, i, TRUE, &mi);
3102  if (mi.wID == t_nFirstID)
3103  {
3104  nInsertPoint = i;
3105  break;
3106  }
3107  }
3108 
3109  ATLASSERT(nInsertPoint < nItems && "You need a menu item with an ID = t_nFirstID");
3110 
3111  for(int j = t_nFirstID; j < (t_nFirstID + m_nMaxEntries); j++)
3112  {
3113  // keep the first one as an insertion point
3114  if (j != t_nFirstID)
3115  ::DeleteMenu(m_hMenu, j, MF_BYCOMMAND);
3116  }
3117 
3118  TCHAR szItemText[t_cchItemLen + 6] = { 0 }; // add space for &, 2 digits, and a space
3119  int nSize = m_arrDocs.GetSize();
3120  int nItem = 0;
3121  if(nSize > 0)
3122  {
3123  for(nItem = 0; nItem < nSize; nItem++)
3124  {
3125  if(m_cchMaxItemLen == -1)
3126  {
3127  SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, m_arrDocs[nSize - 1 - nItem].szDocName);
3128  }
3129  else
3130  {
3131  TCHAR szBuff[t_cchItemLen] = { 0 };
3132  T* pT = static_cast<T*>(this);
3133  pT; // avoid level 4 warning
3134  bool bRet = pT->CompactDocumentName(szBuff, m_arrDocs[nSize - 1 - nItem].szDocName, m_cchMaxItemLen);
3135  bRet; // avoid level 4 warning
3136  ATLASSERT(bRet);
3137  SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, szBuff);
3138  }
3139 
3140  ::InsertMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION | MF_STRING, t_nFirstID + nItem, szItemText);
3141  }
3142  }
3143  else // empty
3144  {
3145  ::InsertMenu(m_hMenu, nInsertPoint, MF_BYPOSITION | MF_STRING, t_nFirstID, m_szNoEntries);
3146  ::EnableMenuItem(m_hMenu, t_nFirstID, MF_GRAYED);
3147  nItem++;
3148  }
3149  ::DeleteMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION);
3150 
3151  return TRUE;
3152  }
3153 
3154 // Overrideables
3155  // override to provide a different method of compacting document names
3156  static bool CompactDocumentName(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
3157  {
3158  return AtlCompactPath(lpstrOut, lpstrIn, cchLen);
3159  }
3160 
3161  static LPCTSTR GetRegKeyName()
3162  {
3163  return _T("Recent Document List");
3164  }
3165 
3166  static LPCTSTR GetRegCountName()
3167  {
3168  return _T("DocumentCount");
3169  }
3170 
3171  static LPCTSTR GetRegItemName()
3172  {
3173  // Note: This string is a format string used with wsprintf().
3174  // Resulting formatted string must be m_cchItemNameLen or less
3175  // characters long, including the terminating null character.
3176  return _T("Document%i");
3177  }
3178 
3179  static LPCTSTR GetMRUEmptyText()
3180  {
3181  return _WTL_MRUEMPTY_TEXT;
3182  }
3183 };
3184 
3185 class CRecentDocumentList : public CRecentDocumentListBase<CRecentDocumentList>
3186 {
3187 public:
3188 // nothing here
3189 };
3190 
3191 #endif // _WIN32_WCE
3192 
3193 
3195 // CFindFile - file search helper class
3196 
3198 {
3199 public:
3200 // Data members
3201  WIN32_FIND_DATA m_fd;
3202  TCHAR m_lpszRoot[MAX_PATH];
3203  TCHAR m_chDirSeparator;
3204  HANDLE m_hFind;
3205  BOOL m_bFound;
3206 
3207 // Constructor/destructor
3208  CFindFile() : m_hFind(NULL), m_chDirSeparator(_T('\\')), m_bFound(FALSE)
3209  { }
3210 
3211  ~CFindFile()
3212  {
3213  Close();
3214  }
3215 
3216 // Attributes
3217  ULONGLONG GetFileSize() const
3218  {
3219  ATLASSERT(m_hFind != NULL);
3220 
3221  ULARGE_INTEGER nFileSize = { 0 };
3222 
3223  if(m_bFound)
3224  {
3225  nFileSize.LowPart = m_fd.nFileSizeLow;
3226  nFileSize.HighPart = m_fd.nFileSizeHigh;
3227  }
3228  else
3229  {
3230  nFileSize.QuadPart = 0;
3231  }
3232 
3233  return nFileSize.QuadPart;
3234  }
3235 
3236  BOOL GetFileName(LPTSTR lpstrFileName, int cchLength) const
3237  {
3238  ATLASSERT(m_hFind != NULL);
3239  if(lstrlen(m_fd.cFileName) >= cchLength)
3240  return FALSE;
3241 
3242  if(m_bFound)
3243  SecureHelper::strcpy_x(lpstrFileName, cchLength, m_fd.cFileName);
3244 
3245  return m_bFound;
3246  }
3247 
3248  BOOL GetFilePath(LPTSTR lpstrFilePath, int cchLength) const
3249  {
3250  ATLASSERT(m_hFind != NULL);
3251 
3252  int nLen = lstrlen(m_lpszRoot);
3253 #ifndef _WIN32_WCE
3254  ATLASSERT(nLen > 0);
3255  if(nLen == 0)
3256  return FALSE;
3257 
3258  bool bAddSep = (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/'));
3259 #else // CE specific
3260  // allow diskless devices (nLen == 0)
3261  bool bAddSep = ((nLen == 0) || (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/')));
3262 #endif // _WIN32_WCE
3263 
3264  if((lstrlen(m_lpszRoot) + (bAddSep ? 1 : 0)) >= cchLength)
3265  return FALSE;
3266 
3267  SecureHelper::strcpy_x(lpstrFilePath, cchLength, m_lpszRoot);
3268 
3269  if(bAddSep)
3270  {
3271  TCHAR szSeparator[2] = { m_chDirSeparator, 0 };
3272  SecureHelper::strcat_x(lpstrFilePath, cchLength, szSeparator);
3273  }
3274 
3275  SecureHelper::strcat_x(lpstrFilePath, cchLength, m_fd.cFileName);
3276 
3277  return TRUE;
3278  }
3279 
3280 #ifndef _WIN32_WCE
3281  BOOL GetFileTitle(LPTSTR lpstrFileTitle, int cchLength) const
3282  {
3283  ATLASSERT(m_hFind != NULL);
3284 
3285  TCHAR szBuff[MAX_PATH] = { 0 };
3286  if(!GetFileName(szBuff, MAX_PATH))
3287  return FALSE;
3288 
3289  if(lstrlen(szBuff) >= cchLength || cchLength < 1)
3290  return FALSE;
3291 
3292  // find the last dot
3293  LPTSTR pstrDot = MinCrtHelper::_strrchr(szBuff, _T('.'));
3294  if(pstrDot != NULL)
3295  *pstrDot = 0;
3296 
3297  SecureHelper::strcpy_x(lpstrFileTitle, cchLength, szBuff);
3298 
3299  return TRUE;
3300  }
3301 #endif // !_WIN32_WCE
3302 
3303  BOOL GetFileURL(LPTSTR lpstrFileURL, int cchLength) const
3304  {
3305  ATLASSERT(m_hFind != NULL);
3306 
3307  TCHAR szBuff[MAX_PATH] = { 0 };
3308  if(!GetFilePath(szBuff, MAX_PATH))
3309  return FALSE;
3310  LPCTSTR lpstrFileURLPrefix = _T("file://");
3311  if(lstrlen(szBuff) + lstrlen(lpstrFileURLPrefix) >= cchLength)
3312  return FALSE;
3313  SecureHelper::strcpy_x(lpstrFileURL, cchLength, lpstrFileURLPrefix);
3314  SecureHelper::strcat_x(lpstrFileURL, cchLength, szBuff);
3315 
3316  return TRUE;
3317  }
3318 
3319  BOOL GetRoot(LPTSTR lpstrRoot, int cchLength) const
3320  {
3321  ATLASSERT(m_hFind != NULL);
3322  if(lstrlen(m_lpszRoot) >= cchLength)
3323  return FALSE;
3324 
3325  SecureHelper::strcpy_x(lpstrRoot, cchLength, m_lpszRoot);
3326 
3327  return TRUE;
3328  }
3329 
3330 #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
3331  _CSTRING_NS::CString GetFileName() const
3332  {
3333  ATLASSERT(m_hFind != NULL);
3334 
3335  _CSTRING_NS::CString ret;
3336 
3337  if(m_bFound)
3338  ret = m_fd.cFileName;
3339  return ret;
3340  }
3341 
3342  _CSTRING_NS::CString GetFilePath() const
3343  {
3344  ATLASSERT(m_hFind != NULL);
3345 
3346  _CSTRING_NS::CString strResult = m_lpszRoot;
3347  int nLen = strResult.GetLength();
3348 #ifndef _WIN32_WCE
3349  ATLASSERT(nLen > 0);
3350  if(nLen == 0)
3351  return strResult;
3352 
3353  if((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/')))
3354 #else // CE specific
3355  // allow diskless devices (nLen == 0)
3356  if((nLen == 0) || ((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/'))))
3357 #endif // _WIN32_WCE
3358  strResult += m_chDirSeparator;
3359  strResult += GetFileName();
3360  return strResult;
3361  }
3362 
3363 #ifndef _WIN32_WCE
3364  _CSTRING_NS::CString GetFileTitle() const
3365  {
3366  ATLASSERT(m_hFind != NULL);
3367 
3368  _CSTRING_NS::CString strResult;
3369  GetFileTitle(strResult.GetBuffer(MAX_PATH), MAX_PATH);
3370  strResult.ReleaseBuffer();
3371 
3372  return strResult;
3373  }
3374 #endif // !_WIN32_WCE
3375 
3376  _CSTRING_NS::CString GetFileURL() const
3377  {
3378  ATLASSERT(m_hFind != NULL);
3379 
3380  _CSTRING_NS::CString strResult("file://");
3381  strResult += GetFilePath();
3382  return strResult;
3383  }
3384 
3385  _CSTRING_NS::CString GetRoot() const
3386  {
3387  ATLASSERT(m_hFind != NULL);
3388 
3389  _CSTRING_NS::CString str = m_lpszRoot;
3390  return str;
3391  }
3392 #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
3393 
3394  BOOL GetLastWriteTime(FILETIME* pTimeStamp) const
3395  {
3396  ATLASSERT(m_hFind != NULL);
3397  ATLASSERT(pTimeStamp != NULL);
3398 
3399  if(m_bFound && pTimeStamp != NULL)
3400  {
3401  *pTimeStamp = m_fd.ftLastWriteTime;
3402  return TRUE;
3403  }
3404 
3405  return FALSE;
3406  }
3407 
3408  BOOL GetLastAccessTime(FILETIME* pTimeStamp) const
3409  {
3410  ATLASSERT(m_hFind != NULL);
3411  ATLASSERT(pTimeStamp != NULL);
3412 
3413  if(m_bFound && pTimeStamp != NULL)
3414  {
3415  *pTimeStamp = m_fd.ftLastAccessTime;
3416  return TRUE;
3417  }
3418 
3419  return FALSE;
3420  }
3421 
3422  BOOL GetCreationTime(FILETIME* pTimeStamp) const
3423  {
3424  ATLASSERT(m_hFind != NULL);
3425 
3426  if(m_bFound && pTimeStamp != NULL)
3427  {
3428  *pTimeStamp = m_fd.ftCreationTime;
3429  return TRUE;
3430  }
3431 
3432  return FALSE;
3433  }
3434 
3435  BOOL MatchesMask(DWORD dwMask) const
3436  {
3437  ATLASSERT(m_hFind != NULL);
3438 
3439  if(m_bFound)
3440  return ((m_fd.dwFileAttributes & dwMask) != 0);
3441 
3442  return FALSE;
3443  }
3444 
3445  BOOL IsDots() const
3446  {
3447  ATLASSERT(m_hFind != NULL);
3448 
3449  // return TRUE if the file name is "." or ".." and
3450  // the file is a directory
3451 
3452  BOOL bResult = FALSE;
3453  if(m_bFound && IsDirectory())
3454  {
3455  if(m_fd.cFileName[0] == _T('.') && (m_fd.cFileName[1] == _T('\0') || (m_fd.cFileName[1] == _T('.') && m_fd.cFileName[2] == _T('\0'))))
3456  bResult = TRUE;
3457  }
3458 
3459  return bResult;
3460  }
3461 
3462  BOOL IsReadOnly() const
3463  {
3464  return MatchesMask(FILE_ATTRIBUTE_READONLY);
3465  }
3466 
3467  BOOL IsDirectory() const
3468  {
3469  return MatchesMask(FILE_ATTRIBUTE_DIRECTORY);
3470  }
3471 
3472  BOOL IsCompressed() const
3473  {
3474  return MatchesMask(FILE_ATTRIBUTE_COMPRESSED);
3475  }
3476 
3477  BOOL IsSystem() const
3478  {
3479  return MatchesMask(FILE_ATTRIBUTE_SYSTEM);
3480  }
3481 
3482  BOOL IsHidden() const
3483  {
3484  return MatchesMask(FILE_ATTRIBUTE_HIDDEN);
3485  }
3486 
3487  BOOL IsTemporary() const
3488  {
3489  return MatchesMask(FILE_ATTRIBUTE_TEMPORARY);
3490  }
3491 
3492  BOOL IsNormal() const
3493  {
3494  return MatchesMask(FILE_ATTRIBUTE_NORMAL);
3495  }
3496 
3497  BOOL IsArchived() const
3498  {
3499  return MatchesMask(FILE_ATTRIBUTE_ARCHIVE);
3500  }
3501 
3502 // Operations
3503  BOOL FindFile(LPCTSTR pstrName = NULL)
3504  {
3505  Close();
3506 
3507  if(pstrName == NULL)
3508  {
3509  pstrName = _T("*.*");
3510  }
3511  else if(lstrlen(pstrName) >= MAX_PATH)
3512  {
3513  ATLASSERT(FALSE);
3514  return FALSE;
3515  }
3516 
3517  SecureHelper::strcpy_x(m_fd.cFileName, _countof(m_fd.cFileName), pstrName);
3518 
3519  m_hFind = ::FindFirstFile(pstrName, &m_fd);
3520 
3521  if(m_hFind == INVALID_HANDLE_VALUE)
3522  return FALSE;
3523 
3524 #ifndef _WIN32_WCE
3525  bool bFullPath = (::GetFullPathName(pstrName, MAX_PATH, m_lpszRoot, NULL) != 0);
3526 #else // CE specific
3527  errno_t nRet = SecureHelper::strncpy_x(m_lpszRoot, _countof(m_lpszRoot), pstrName, _TRUNCATE);
3528  bool bFullPath = (nRet == 0 || nRet == STRUNCATE);
3529 #endif // _WIN32_WCE
3530 
3531  // passed name isn't a valid path but was found by the API
3532  ATLASSERT(bFullPath);
3533  if(!bFullPath)
3534  {
3535  Close();
3536  ::SetLastError(ERROR_INVALID_NAME);
3537  return FALSE;
3538  }
3539  else
3540  {
3541  // find the last forward or backward whack
3542  LPTSTR pstrBack = MinCrtHelper::_strrchr(m_lpszRoot, _T('\\'));
3543  LPTSTR pstrFront = MinCrtHelper::_strrchr(m_lpszRoot, _T('/'));
3544 
3545  if(pstrFront != NULL || pstrBack != NULL)
3546  {
3547  if(pstrFront == NULL)
3548  pstrFront = m_lpszRoot;
3549  if(pstrBack == NULL)
3550  pstrBack = m_lpszRoot;
3551 
3552  // from the start to the last whack is the root
3553 
3554  if(pstrFront >= pstrBack)
3555  *pstrFront = _T('\0');
3556  else
3557  *pstrBack = _T('\0');
3558  }
3559  }
3560 
3561  m_bFound = TRUE;
3562 
3563  return TRUE;
3564  }
3565 
3566  BOOL FindNextFile()
3567  {
3568  ATLASSERT(m_hFind != NULL);
3569 
3570  if(m_hFind == NULL)
3571  return FALSE;
3572 
3573  if(!m_bFound)
3574  return FALSE;
3575 
3576  m_bFound = ::FindNextFile(m_hFind, &m_fd);
3577 
3578  return m_bFound;
3579  }
3580 
3581  void Close()
3582  {
3583  m_bFound = FALSE;
3584 
3585  if(m_hFind != NULL && m_hFind != INVALID_HANDLE_VALUE)
3586  {
3587  ::FindClose(m_hFind);
3588  m_hFind = NULL;
3589  }
3590  }
3591 };
3592 
3593 
3595 // Global functions for stock GDI objects
3596 
3597 inline HPEN AtlGetStockPen(int nPen)
3598 {
3599 #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE)
3600  ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN || nPen == DC_PEN);
3601 #else
3602  ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN);
3603 #endif
3604  return (HPEN)::GetStockObject(nPen);
3605 }
3606 
3607 inline HBRUSH AtlGetStockBrush(int nBrush)
3608 {
3609 #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE)
3610  ATLASSERT((nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH) || nBrush == DC_BRUSH);
3611 #else
3612  ATLASSERT(nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH);
3613 #endif
3614  return (HBRUSH)::GetStockObject(nBrush);
3615 }
3616 
3617 inline HFONT AtlGetStockFont(int nFont)
3618 {
3619 #ifndef _WIN32_WCE
3620  ATLASSERT((nFont >= OEM_FIXED_FONT && nFont <= SYSTEM_FIXED_FONT) || nFont == DEFAULT_GUI_FONT);
3621 #else // CE specific
3622  ATLASSERT(nFont == SYSTEM_FONT);
3623 #endif // _WIN32_WCE
3624  return (HFONT)::GetStockObject(nFont);
3625 }
3626 
3627 inline HPALETTE AtlGetStockPalette(int nPalette)
3628 {
3629  ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported
3630  return (HPALETTE)::GetStockObject(nPalette);
3631 }
3632 
3633 
3635 // Global function for compacting a path by replacing parts with ellipsis
3636 
3637 // helper for multi-byte character sets
3638 inline bool _IsDBCSTrailByte(LPCTSTR lpstr, int nChar)
3639 {
3640 #ifndef _UNICODE
3641  int i = nChar;
3642  for( ; i > 0; i--)
3643  {
3644  if(!::IsDBCSLeadByte(lpstr[i - 1]))
3645  break;
3646  }
3647  return ((nChar > 0) && (((nChar - i) & 1) != 0));
3648 #else // _UNICODE
3649  lpstr; nChar;
3650  return false;
3651 #endif // _UNICODE
3652 }
3653 
3654 inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
3655 {
3656  ATLASSERT(lpstrOut != NULL);
3657  ATLASSERT(lpstrIn != NULL);
3658  ATLASSERT(cchLen > 0);
3659 
3660  LPCTSTR szEllipsis = _T("...");
3661  const int cchEndEllipsis = 3;
3662  const int cchMidEllipsis = 4;
3663 
3664  if(lstrlen(lpstrIn) < cchLen)
3665  {
3666  SecureHelper::strcpy_x(lpstrOut, cchLen, lpstrIn);
3667  return true;
3668  }
3669 
3670  lpstrOut[0] = 0;
3671 
3672  // check if the separator is a slash or a backslash
3673  TCHAR chSlash = _T('\\');
3674  for(LPTSTR lpstr = (LPTSTR)lpstrIn; *lpstr != 0; lpstr = ::CharNext(lpstr))
3675  {
3676  if((*lpstr == _T('/')) || (*lpstr == _T('\\')))
3677  chSlash = *lpstr;
3678  }
3679 
3680  // find the filename portion of the path
3681  LPCTSTR lpstrFileName = lpstrIn;
3682  for(LPCTSTR pPath = lpstrIn; *pPath; pPath = ::CharNext(pPath))
3683  {
3684  if((pPath[0] == _T('\\') || pPath[0] == _T(':') || pPath[0] == _T('/'))
3685  && pPath[1] && pPath[1] != _T('\\') && pPath[1] != _T('/'))
3686  lpstrFileName = pPath + 1;
3687  }
3688  int cchFileName = lstrlen(lpstrFileName);
3689 
3690  // handle just the filename without a path
3691  if(lpstrFileName == lpstrIn && cchLen > cchEndEllipsis)
3692  {
3693  bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchLen - cchEndEllipsis - 1) == 0);
3694  if(bRet)
3695  {
3696 #ifndef _UNICODE
3697  if(_IsDBCSTrailByte(lpstrIn, cchLen - cchEndEllipsis))
3698  lpstrOut[cchLen - cchEndEllipsis - 1] = 0;
3699 #endif // _UNICODE
3700  SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
3701  }
3702  return bRet;
3703  }
3704 
3705  // handle just ellipsis
3706  if((cchLen < (cchMidEllipsis + cchEndEllipsis)))
3707  {
3708  for(int i = 0; i < cchLen - 1; i++)
3709  lpstrOut[i] = ((i + 1) == cchMidEllipsis) ? chSlash : _T('.');
3710  lpstrOut[cchLen - 1] = 0;
3711  return true;
3712  }
3713 
3714  // calc how much we have to copy
3715  int cchToCopy = cchLen - (cchMidEllipsis + cchFileName) - 1;
3716 
3717  if(cchToCopy < 0)
3718  cchToCopy = 0;
3719 
3720 #ifndef _UNICODE
3721  if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrIn, cchToCopy))
3722  cchToCopy--;
3723 #endif // _UNICODE
3724 
3725  bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchToCopy) == 0);
3726  if(!bRet)
3727  return false;
3728 
3729  // add ellipsis
3730  SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
3731  TCHAR szSlash[2] = { chSlash, 0 };
3732  SecureHelper::strcat_x(lpstrOut, cchLen, szSlash);
3733 
3734  // add filename (and ellipsis, if needed)
3735  if(cchLen > (cchMidEllipsis + cchFileName))
3736  {
3737  SecureHelper::strcat_x(lpstrOut, cchLen, lpstrFileName);
3738  }
3739  else
3740  {
3741  cchToCopy = cchLen - cchMidEllipsis - cchEndEllipsis - 1;
3742 #ifndef _UNICODE
3743  if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrFileName, cchToCopy))
3744  cchToCopy--;
3745 #endif // _UNICODE
3746  bRet = (SecureHelper::strncpy_x(&lpstrOut[cchMidEllipsis], cchLen - cchMidEllipsis, lpstrFileName, cchToCopy) == 0);
3747  if(bRet)
3748  SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
3749  }
3750 
3751  return bRet;
3752 }
3753 
3754 }; // namespace WTL
3755 
3756 #endif // __ATLMISC_H__
Definition: atlmisc.h:2820
Definition: atlmisc.h:736
Definition: atlmisc.h:2824
Definition: atlmisc.h:72
Definition: atlapp.h:1872
Definition: atlmisc.h:289
Definition: atlmisc.h:3185
Definition: atlmisc.h:162
Definition: atlmisc.h:757
Definition: atlapp.h:553
Definition: atlapp.h:1455
Definition: atlmisc.h:3197
Definition: atluser.h:126