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