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