[ATL][ATL_APITEST] Implement / Improve CString, based upon the code that was already...
[reactos.git] / reactos / sdk / lib / atl / cstringt.h
1 #ifndef __CSTRINGT_H__
2 #define __CSTRINGT_H__
3
4 #pragma once
5 #include <atlsimpstr.h>
6 #include <stddef.h>
7 #include <stdio.h>
8 #include <wchar.h>
9 #include <atlmem.h>
10
11 namespace ATL
12 {
13
14 inline UINT WINAPI _AtlGetConversionACP() throw()
15 {
16 #ifdef _CONVERSION_DONT_USE_THREAD_LOCALE
17 return CP_ACP;
18 #else
19 return CP_THREAD_ACP;
20 #endif
21 }
22
23
24 template<typename _CharType = wchar_t>
25 class ChTraitsCRT : public ChTraitsBase<_CharType>
26 {
27 public:
28
29 static int __cdecl GetBaseTypeLength(_In_z_ LPCWSTR pszSource) throw()
30 {
31 if (pszSource == NULL) return -1;
32 return static_cast<int>(wcslen(pszSource));
33 }
34
35 static int __cdecl GetBaseTypeLength(_In_z_ LPCSTR pszSource) throw()
36 {
37 if (pszSource == NULL) return 0;
38 return ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSource, -1, NULL, 0) - 1;
39 }
40
41 static int __cdecl GetBaseTypeLength(
42 _In_reads_(nLength) LPCWSTR pszSource,
43 _In_ int nLength) throw()
44 {
45 return nLength;
46 }
47
48 static int __cdecl GetBaseTypeLength(
49 _In_reads_(nLength) LPCSTR pszSource,
50 _In_ int nLength) throw()
51 {
52 return ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0);
53 }
54
55 static void __cdecl ConvertToBaseType(
56 _Out_writes_(nDestLength) LPWSTR pszDest,
57 _In_ int nDestLength,
58 _In_ LPCWSTR pszSrc,
59 _In_ int nSrcLength = -1)
60 {
61 if (nSrcLength == -1)
62 nSrcLength = 1 + GetBaseTypeLength(pszSrc);
63
64 wmemcpy(pszDest, pszSrc, nSrcLength);
65 }
66
67 static void __cdecl ConvertToBaseType(
68 _Out_writes_(nDestLength) LPWSTR pszDest,
69 _In_ int nDestLength,
70 _In_ LPCSTR pszSrc,
71 _In_ int nSrcLength = -1)
72 {
73 if (nSrcLength == -1)
74 nSrcLength = 1 + GetBaseTypeLength(pszSrc);
75
76 ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength);
77 }
78
79 static void __cdecl MakeLower(
80 _Out_writes_(nSrcLength) LPWSTR pszSource,
81 _In_ int nSrcLength)
82 {
83 ::CharLowerBuffW(pszSource, nSrcLength);
84 }
85
86 static void __cdecl MakeUpper(
87 _Out_writes_(nSrcLength) LPWSTR pszSource,
88 _In_ int nSrcLength)
89 {
90 ::CharUpperBuffW(pszSource, nSrcLength);
91 }
92
93 static LPWSTR __cdecl FindString(
94 _In_z_ LPCWSTR pszSource,
95 _In_z_ LPCWSTR pszSub)
96 {
97 return ::wcsstr(pszSource, pszSub);
98 }
99
100 static LPWSTR __cdecl FindChar(
101 _In_z_ LPCWSTR pszSource,
102 _In_ WCHAR ch)
103 {
104 return ::wcschr(pszSource, ch);
105 }
106
107 static LPWSTR __cdecl FindCharReverse(
108 _In_z_ LPCWSTR pszSource,
109 _In_ WCHAR ch)
110 {
111 return ::wcsrchr(pszSource, ch);
112 }
113
114 static LPWSTR __cdecl FindOneOf(
115 _In_z_ LPCWSTR pszSource,
116 _In_z_ LPCWSTR pszCharSet)
117 {
118 return ::wcspbrk(pszSource, pszCharSet);
119 }
120
121 static int __cdecl Compare(
122 _In_z_ LPCWSTR psz1,
123 _In_z_ LPCWSTR psz2)
124 {
125 return ::wcscmp(psz1, psz2);
126 }
127
128 static int __cdecl FormatV(
129 _In_opt_z_ LPWSTR pszDest,
130 _In_z_ LPCWSTR pszFormat,
131 _In_ va_list args)
132 {
133 if (pszDest == NULL)
134 return ::_vscwprintf(pszFormat, args);
135 return ::vswprintf(pszDest, pszFormat, args);
136 }
137
138 };
139
140
141 // Template specialization
142
143 template<>
144 class ChTraitsCRT<char> : public ChTraitsBase<char>
145 {
146 public:
147
148 static int __cdecl GetBaseTypeLength(_In_z_ LPCWSTR pszSource) throw()
149 {
150 return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL) - 1;
151 }
152
153 static int __cdecl GetBaseTypeLength(_In_z_ LPCSTR pszSource) throw()
154 {
155 if (pszSource == NULL) return 0;
156 return static_cast<int>(strlen(pszSource));
157 }
158
159 static int __cdecl GetBaseTypeLength(
160 _In_reads_(nLength) LPCWSTR pszSource,
161 _In_ int nLength) throw()
162 {
163 return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL);
164 }
165
166 static int __cdecl GetBaseTypeLength(
167 _In_reads_(nLength) LPCSTR pszSource,
168 _In_ int nLength) throw()
169 {
170 return nLength;
171 }
172
173 static void __cdecl ConvertToBaseType(
174 _Out_writes_(nDestLength) LPSTR pszDest,
175 _In_ int nDestLength,
176 _In_ LPCWSTR pszSrc,
177 _In_ int nSrcLength = -1)
178 {
179 if (nSrcLength == -1)
180 nSrcLength = 1 + GetBaseTypeLength(pszSrc);
181
182 ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL);
183 }
184
185 static void __cdecl ConvertToBaseType(
186 _Out_writes_(nDestLength) LPSTR pszDest,
187 _In_ int nDestLength,
188 _In_ LPCSTR pszSrc,
189 _In_ int nSrcLength = -1)
190 {
191 if (nSrcLength == -1)
192 nSrcLength = 1 + GetBaseTypeLength(pszSrc);
193
194 memcpy(pszDest, pszSrc, nSrcLength);
195 }
196
197 static void __cdecl MakeLower(
198 _Out_writes_(nSrcLength) LPSTR pszSource,
199 _In_ int nSrcLength)
200 {
201 ::CharLowerBuffA(pszSource, nSrcLength);
202 }
203
204 static void __cdecl MakeUpper(
205 _Out_writes_(nSrcLength) LPSTR pszSource,
206 _In_ int nSrcLength)
207 {
208 ::CharUpperBuffA(pszSource, nSrcLength);
209 }
210
211 static LPSTR __cdecl FindString(
212 _In_z_ LPCSTR pszSource,
213 _In_z_ LPCSTR pszSub)
214 {
215 return ::strstr(pszSource, pszSub);
216 }
217
218 static LPSTR __cdecl FindChar(
219 _In_z_ LPCSTR pszSource,
220 _In_ CHAR ch)
221 {
222 return ::strchr(pszSource, ch);
223 }
224
225 static LPSTR __cdecl FindCharReverse(
226 _In_z_ LPCSTR pszSource,
227 _In_ CHAR ch)
228 {
229 return ::strrchr(pszSource, ch);
230 }
231
232 static LPSTR __cdecl FindOneOf(
233 _In_z_ LPCSTR pszSource,
234 _In_z_ LPCSTR pszCharSet)
235 {
236 return ::strpbrk(pszSource, pszCharSet);
237 }
238
239 static int __cdecl Compare(
240 _In_z_ LPCSTR psz1,
241 _In_z_ LPCSTR psz2)
242 {
243 return ::strcmp(psz1, psz2);
244 }
245
246 static int __cdecl FormatV(
247 _In_opt_z_ LPSTR pszDest,
248 _In_z_ LPCSTR pszFormat,
249 _In_ va_list args)
250 {
251 if (pszDest == NULL)
252 return ::_vscprintf(pszFormat, args);
253 return ::vsprintf(pszDest, pszFormat, args);
254 }
255
256 };
257
258
259 namespace _CSTRING_IMPL_
260 {
261 template <typename _CharType, class StringTraits>
262 struct _MFCDLLTraitsCheck
263 {
264 const static bool c_bIsMFCDLLTraits = false;
265 };
266 }
267
268
269 // TODO: disable conversion functions when _CSTRING_DISABLE_NARROW_WIDE_CONVERSION is defined.
270
271 template <typename BaseType, class StringTraits>
272 class CStringT :
273 public CSimpleStringT <BaseType, _CSTRING_IMPL_::_MFCDLLTraitsCheck<BaseType, StringTraits>::c_bIsMFCDLLTraits>
274 {
275 public:
276 typedef CSimpleStringT<BaseType, _CSTRING_IMPL_::_MFCDLLTraitsCheck<BaseType, StringTraits>::c_bIsMFCDLLTraits> CThisSimpleString;
277 typedef StringTraits StrTraits;
278 typedef typename CThisSimpleString::XCHAR XCHAR;
279 typedef typename CThisSimpleString::PXSTR PXSTR;
280 typedef typename CThisSimpleString::PCXSTR PCXSTR;
281 typedef typename CThisSimpleString::YCHAR YCHAR;
282 typedef typename CThisSimpleString::PYSTR PYSTR;
283 typedef typename CThisSimpleString::PCYSTR PCYSTR;
284
285 public:
286 CStringT() throw() :
287 CThisSimpleString(StringTraits::GetDefaultManager())
288 {
289 }
290
291 explicit CStringT( _In_ IAtlStringMgr* pStringMgr) throw() :
292 CThisSimpleString(pStringMgr)
293 {
294 }
295
296 static void __cdecl Construct(_In_ CStringT* pString)
297 {
298 new(pString) CStringT;
299 }
300
301 CStringT(_In_ const CStringT& strSrc) :
302 CThisSimpleString(strSrc)
303 {
304 }
305
306 template<class StringTraits_>
307 CStringT(_In_ const CStringT<YCHAR, StringTraits_> & strSrc) :
308 CThisSimpleString(StringTraits::GetDefaultManager())
309 {
310 *this = static_cast<const YCHAR*>(strSrc);
311 }
312
313 CStringT(_In_opt_z_ const XCHAR* pszSrc) :
314 CThisSimpleString( StringTraits::GetDefaultManager() )
315 {
316 // FIXME: Check whether pszSrc is not a resource string ID!
317 *this = pszSrc;
318 }
319
320 CStringT(
321 _In_opt_z_ const XCHAR* pszSrc,
322 _In_ IAtlStringMgr* pStringMgr) :
323 CThisSimpleString( pStringMgr )
324 {
325 // FIXME: Check whether pszSrc is not a resource string ID!
326 *this = pszSrc;
327 }
328
329 CStringT(_In_opt_z_ const YCHAR* pszSrc) :
330 CThisSimpleString( StringTraits::GetDefaultManager() )
331 {
332 // FIXME: Check whether pszSrc is not a resource string ID!
333 *this = pszSrc;
334 }
335
336 CStringT(
337 _In_opt_z_ const YCHAR* pszSrc,
338 _In_ IAtlStringMgr* pStringMgr) :
339 CThisSimpleString( pStringMgr )
340 {
341 // FIXME: Check whether pszSrc is not a resource string ID!
342 *this = pszSrc;
343 }
344
345 CStringT(
346 _In_reads_z_(nLength) const XCHAR* pch,
347 _In_ int nLength) :
348 CThisSimpleString(pch, nLength, StringTraits::GetDefaultManager())
349 {
350 }
351
352 CStringT(
353 _In_reads_z_(nLength) const YCHAR* pch,
354 _In_ int nLength) :
355 CThisSimpleString(pch, nLength, StringTraits::GetDefaultManager())
356 {
357 }
358
359 CStringT& operator=(_In_ const CStringT& strSrc)
360 {
361 CThisSimpleString::operator=(strSrc);
362 return *this;
363 }
364
365 CStringT& operator=(_In_opt_z_ PCXSTR pszSrc)
366 {
367 CThisSimpleString::operator=(pszSrc);
368 return *this;
369 }
370
371 CStringT& operator=(_In_opt_z_ PCYSTR pszSrc)
372 {
373 int length = pszSrc ? StringTraits::GetBaseTypeLength(pszSrc) : 0;
374 if (length > 0)
375 {
376 PXSTR result = CThisSimpleString::GetBuffer(length);
377 StringTraits::ConvertToBaseType(result, length, pszSrc);
378 CThisSimpleString::ReleaseBufferSetLength(length);
379 }
380 else
381 {
382 CThisSimpleString::Empty();
383 }
384 return *this;
385 }
386
387 friend bool operator==(const CStringT& str1, const CStringT& str2) throw()
388 {
389 return str1.Compare(str2) == 0;
390 }
391
392 friend bool operator==(const CStringT& str1, PCXSTR psz2) throw()
393 {
394 return str1.Compare(psz2) == 0;
395 }
396
397 friend bool operator==(const CStringT& str1, PCYSTR psz2) throw()
398 {
399 CStringT tmp(psz2, str1.GetManager());
400 return tmp == str1;
401 }
402
403 friend bool operator==(const CStringT& str1, XCHAR ch2) throw()
404 {
405 return str1.GetLength() == 1 && str1[0] == ch2;
406 }
407
408 friend bool operator==(PCXSTR psz1, const CStringT& str2) throw()
409 {
410 return str2.Compare(psz1) == 0;
411 }
412
413 friend bool operator==(PCYSTR psz1, const CStringT& str2) throw()
414 {
415 CStringT tmp(psz1, str2.GetManager());
416 return tmp.Compare(str2) == 0;
417 }
418
419 friend bool operator==(XCHAR ch1, const CStringT& str2) throw()
420 {
421 return str2.GetLength() == 1 && str2[0] == ch1;
422 }
423
424 CStringT& operator+=(_In_ const CThisSimpleString& str)
425 {
426 CThisSimpleString::operator+=(str);
427 return *this;
428 }
429
430 CStringT& operator+=(_In_z_ PCXSTR pszSrc)
431 {
432 CThisSimpleString::operator+=(pszSrc);
433 return *this;
434 }
435
436 _Check_return_ BOOL LoadString(_In_ HINSTANCE hInstance,
437 _In_ UINT nID)
438 {
439 const ATLSTRINGRESOURCEIMAGE* pImage = AtlGetStringResourceImage(hInstance, nID);
440 if (pImage == NULL) return FALSE;
441
442 int nLength = StringTraits::GetBaseTypeLength(pImage->achString, pImage->nLength);
443 PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength);
444 StringTraits::ConvertToBaseType(pszBuffer, nLength, pImage->achString, pImage->nLength);
445 CThisSimpleString::ReleaseBufferSetLength(nLength);
446
447 return TRUE;
448 }
449
450 CStringT& MakeLower()
451 {
452 int nLength = CThisSimpleString::GetLength();
453 PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength);
454
455 StringTraits::MakeLower(pszBuffer, nLength);
456 CThisSimpleString::ReleaseBufferSetLength(nLength);
457
458 return *this;
459 }
460
461 CStringT& MakeUpper()
462 {
463 int nLength = CThisSimpleString::GetLength();
464 PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength);
465
466 StringTraits::MakeUpper(pszBuffer, nLength);
467 CThisSimpleString::ReleaseBufferSetLength(nLength);
468
469 return *this;
470 }
471
472 int Find(_In_ PCXSTR pszSub, _In_opt_ int iStart = 0) const throw()
473 {
474 int nLength = CThisSimpleString::GetLength();
475
476 if (iStart >= nLength || iStart < 0)
477 return -1;
478
479 PCXSTR pszString = CThisSimpleString::GetString();
480 PCXSTR pszResult = StringTraits::FindString(pszString + iStart, pszSub);
481
482 return pszResult ? ((int)(pszResult - pszString)) : -1;
483 }
484
485 int Find(_In_ XCHAR ch, _In_opt_ int iStart = 0) const throw()
486 {
487 int nLength = CThisSimpleString::GetLength();
488
489 if (iStart >= nLength || iStart < 0)
490 return -1;
491
492 PCXSTR pszString = CThisSimpleString::GetString();
493 PCXSTR pszResult = StringTraits::FindChar(pszString + iStart, ch);
494
495 return pszResult ? ((int)(pszResult - pszString)) : -1;
496 }
497
498 int FindOneOf(_In_ PCXSTR pszCharSet) const throw()
499 {
500 PCXSTR pszString = CThisSimpleString::GetString();
501 PCXSTR pszResult = StringTraits::FindOneOf(pszString, pszCharSet);
502
503 return pszResult ? ((int)(pszResult - pszString)) : -1;
504 }
505
506 int ReverseFind(_In_ XCHAR ch) const throw()
507 {
508 PCXSTR pszString = CThisSimpleString::GetString();
509 PCXSTR pszResult = StringTraits::FindCharReverse(pszString, ch);
510
511 return pszResult ? ((int)(pszResult - pszString)) : -1;
512 }
513
514 int Compare(_In_z_ PCXSTR psz) const
515 {
516 return StringTraits::Compare(CThisSimpleString::GetString(), psz);
517 }
518
519
520 CStringT Mid(int iFirst, int nCount) const
521 {
522 int nLength = CThisSimpleString::GetLength();
523
524 if (iFirst < 0)
525 iFirst = 0;
526 if (nCount < 0)
527 nCount = 0;
528 if (iFirst > nLength)
529 iFirst = nLength;
530 if (iFirst + nCount > nLength)
531 nCount = nLength - iFirst;
532
533 return CStringT(CThisSimpleString::GetString() + iFirst, nCount);
534 }
535
536 CStringT Mid(int iFirst) const
537 {
538 int nLength = CThisSimpleString::GetLength();
539
540 if (iFirst < 0)
541 iFirst = 0;
542 if (iFirst > nLength)
543 iFirst = nLength;
544
545 return CStringT(CThisSimpleString::GetString() + iFirst, nLength - iFirst);
546 }
547
548 CStringT Left(int nCount) const
549 {
550 int nLength = CThisSimpleString::GetLength();
551
552 if (nCount < 0)
553 nCount = 0;
554 if (nCount > nLength)
555 nCount = nLength;
556
557 return CStringT(CThisSimpleString::GetString(), nCount);
558 }
559
560 CStringT Right(int nCount) const
561 {
562 int nLength = CThisSimpleString::GetLength();
563
564 if (nCount < 0)
565 nCount = 0;
566 if (nCount > nLength)
567 nCount = nLength;
568
569 return CStringT(CThisSimpleString::GetString() + nLength - nCount, nCount);
570 }
571
572
573 //void __cdecl Format(UINT nFormatID, ...)
574 //{
575 // va_list args;
576 // va_start(args, dwMessageId);
577 // CStringT formatString;
578 // formatString.LoadString(?????);
579 // FormatV(formatString, args);
580 // va_end(args);
581 //}
582
583 void __cdecl Format(PCXSTR pszFormat, ...)
584 {
585 va_list args;
586 va_start(args, pszFormat);
587 FormatV(pszFormat, args);
588 va_end(args);
589 }
590
591 void FormatV(PCXSTR pszFormat, va_list args)
592 {
593 int nLength = StringTraits::FormatV(NULL, pszFormat, args);
594
595 PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength);
596 StringTraits::FormatV(pszBuffer, pszFormat, args);
597 CThisSimpleString::ReleaseBufferSetLength(nLength);
598 }
599
600
601 int Replace(PCXSTR pszOld, PCXSTR pszNew)
602 {
603 PCXSTR pszString = CThisSimpleString::GetString();
604
605 const int nLength = CThisSimpleString::GetLength();
606 const int nOldLen = StringTraits::GetBaseTypeLength(pszOld);
607 const int nNewLen = StringTraits::GetBaseTypeLength(pszNew);
608 const int nDiff = nNewLen - nOldLen;
609 int nResultLength = nLength;
610
611 PCXSTR pszFound;
612 while ((pszFound = StringTraits::FindString(pszString, pszOld)))
613 {
614 nResultLength += nDiff;
615 pszString = pszFound + nOldLen;
616 }
617
618 if (pszString == CThisSimpleString::GetString())
619 return 0;
620
621 PXSTR pszResult = CThisSimpleString::GetBuffer(nResultLength);
622 PXSTR pszNext;
623 int nCount = 0, nRemaining = nLength;
624 while (nRemaining && (pszNext = StringTraits::FindString(pszResult, pszOld)))
625 {
626 nRemaining -= (pszNext - pszResult);
627 nRemaining -= nOldLen;
628 if (nRemaining > 0)
629 CThisSimpleString::CopyCharsOverlapped(pszNext + nNewLen, nRemaining + 1, pszNext + nOldLen, nRemaining + 1);
630 CThisSimpleString::CopyCharsOverlapped(pszNext, nNewLen, pszNew, nNewLen);
631 pszResult = pszNext + nNewLen;
632 nCount++;
633 }
634
635 CThisSimpleString::ReleaseBufferSetLength(nResultLength);
636
637 return nCount;
638 }
639
640 int Replace(XCHAR chOld, XCHAR chNew)
641 {
642 PCXSTR pszString = CThisSimpleString::GetString();
643 PXSTR pszFirst = StringTraits::FindChar(pszString, chOld);
644 if (!pszFirst)
645 return 0;
646
647 int nLength = CThisSimpleString::GetLength();
648 int nCount = 0;
649
650 PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength);
651 pszFirst = pszBuffer + (pszFirst - pszString);
652 do {
653 *pszFirst = chNew;
654 ++nCount;
655 } while ((pszFirst = StringTraits::FindChar(pszFirst + 1, chOld)));
656
657 CThisSimpleString::ReleaseBufferSetLength(nLength);
658 return nCount;
659 }
660
661
662 static PCXSTR DefaultTrimChars()
663 {
664 static XCHAR str[] = { ' ', '\t', '\r', '\n', 0 };
665 return str;
666 }
667
668
669 CStringT& TrimLeft()
670 {
671 return TrimLeft(DefaultTrimChars());
672 }
673
674 CStringT& TrimLeft(XCHAR chTarget)
675 {
676 XCHAR str[2] = { chTarget, 0 };
677 return TrimLeft(str);
678 }
679
680 CStringT& TrimLeft(PCXSTR pszTargets)
681 {
682 int nLength = CThisSimpleString::GetLength();
683 PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength);
684 int nCount = 0;
685
686 while (nCount < nLength && StringTraits::FindChar(pszTargets, pszBuffer[nCount]))
687 nCount++;
688
689 if (nCount > 0)
690 {
691 CThisSimpleString::CopyCharsOverlapped(pszBuffer, nLength - nCount, pszBuffer + nCount, nLength - nCount);
692 nLength -= nCount;
693 }
694 CThisSimpleString::ReleaseBufferSetLength(nLength);
695
696 return *this;
697 }
698
699
700 CStringT& TrimRight()
701 {
702 return TrimRight(DefaultTrimChars());
703 }
704
705 CStringT& TrimRight(XCHAR chTarget)
706 {
707 XCHAR str[2] = { chTarget, 0 };
708 return TrimRight(str);
709 }
710
711 CStringT& TrimRight(PCXSTR pszTargets)
712 {
713 int nLength = CThisSimpleString::GetLength();
714 PXSTR pszBuffer = CThisSimpleString::GetBuffer(nLength);
715
716 while (nLength > 0 && StringTraits::FindChar(pszTargets, pszBuffer[nLength-1]))
717 nLength--;
718
719 CThisSimpleString::ReleaseBufferSetLength(nLength);
720
721 return *this;
722 }
723
724
725 CStringT& Trim()
726 {
727 return Trim(DefaultTrimChars());
728 }
729
730 CStringT& Trim(XCHAR chTarget)
731 {
732 XCHAR str[2] = { chTarget, 0 };
733 return Trim(str);
734 }
735
736 CStringT& Trim(PCXSTR pszTargets)
737 {
738 return TrimRight(pszTargets).TrimLeft(pszTargets);
739 }
740
741
742 };
743
744 } //namespace ATL
745
746 #endif