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