| // Windows Template Library - WTL version 8.0 | 
 | // Copyright (C) Microsoft Corporation. All rights reserved. | 
 | // | 
 | // This file is a part of the Windows Template Library. | 
 | // The use and distribution terms for this software are covered by the | 
 | // Microsoft Permissive License (Ms-PL) which can be found in the file | 
 | // Ms-PL.txt at the root of this distribution. | 
 |  | 
 | #ifndef __ATLMISC_H__ | 
 | #define __ATLMISC_H__ | 
 |  | 
 | #pragma once | 
 |  | 
 | #ifndef __cplusplus | 
 | 	#error ATL requires C++ compilation (use a .cpp suffix) | 
 | #endif | 
 |  | 
 | #ifndef __ATLAPP_H__ | 
 | 	#error atlmisc.h requires atlapp.h to be included first | 
 | #endif | 
 |  | 
 |  | 
 | #ifdef _ATL_TMP_NO_CSTRING | 
 |   #define _WTL_NO_CSTRING | 
 | #endif | 
 |  | 
 | #if defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING) | 
 | 	#error Conflicting options - both _WTL_USE_CSTRING and _WTL_NO_CSTRING are defined | 
 | #endif // defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING) | 
 |  | 
 | #if !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING) | 
 |   #define _WTL_USE_CSTRING | 
 | #endif // !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING) | 
 |  | 
 | #ifndef _WTL_NO_CSTRING | 
 |   #if defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT) | 
 | 	#error Cannot use CString floating point formatting with _ATL_MIN_CRT defined | 
 |   #endif // defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT) | 
 | #endif // !_WTL_NO_CSTRING | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // Classes in this file: | 
 | // | 
 | // CSize | 
 | // CPoint | 
 | // CRect | 
 | // CString | 
 | // | 
 | // CRecentDocumentListBase<T, t_cchItemLen, t_nFirstID, t_nLastID> | 
 | // CRecentDocumentList | 
 | // CFindFile | 
 | // | 
 | // Global functions: | 
 | //   AtlLoadAccelerators() | 
 | //   AtlLoadMenu() | 
 | //   AtlLoadBitmap() | 
 | //   AtlLoadSysBitmap() | 
 | //   AtlLoadCursor() | 
 | //   AtlLoadSysCursor() | 
 | //   AtlLoadIcon() | 
 | //   AtlLoadSysIcon() | 
 | //   AtlLoadBitmapImage() | 
 | //   AtlLoadCursorImage() | 
 | //   AtlLoadIconImage() | 
 | //   AtlLoadSysBitmapImage() | 
 | //   AtlLoadSysCursorImage() | 
 | //   AtlLoadSysIconImage() | 
 | //   AtlLoadString() | 
 | // | 
 | //   AtlGetStockPen() | 
 | //   AtlGetStockBrush() | 
 | //   AtlGetStockFont() | 
 | //   AtlGetStockPalette() | 
 | // | 
 | //   AtlCompactPath() | 
 |  | 
 |  | 
 | namespace WTL | 
 | { | 
 |  | 
 | #ifndef _WTL_NO_WTYPES | 
 |  | 
 | // forward declarations | 
 | class CSize; | 
 | class CPoint; | 
 | class CRect; | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // CSize - Wrapper for Windows SIZE structure. | 
 |  | 
 | class CSize : public SIZE | 
 | { | 
 | public: | 
 | // Constructors | 
 | 	CSize() | 
 | 	{ | 
 | 		cx = 0; | 
 | 		cy = 0; | 
 | 	} | 
 |  | 
 | 	CSize(int initCX, int initCY) | 
 | 	{ | 
 | 		cx = initCX; | 
 | 		cy = initCY; | 
 | 	} | 
 |  | 
 | 	CSize(SIZE initSize) | 
 | 	{ | 
 | 		*(SIZE*)this = initSize; | 
 | 	} | 
 |  | 
 | 	CSize(POINT initPt) | 
 | 	{ | 
 | 		*(POINT*)this = initPt; | 
 | 	} | 
 |  | 
 | 	CSize(DWORD dwSize) | 
 | 	{ | 
 | 		cx = (short)LOWORD(dwSize); | 
 | 		cy = (short)HIWORD(dwSize); | 
 | 	} | 
 |  | 
 | // Operations | 
 | 	BOOL operator ==(SIZE size) const | 
 | 	{ | 
 | 		return (cx == size.cx && cy == size.cy); | 
 | 	} | 
 |  | 
 | 	BOOL operator !=(SIZE size) const | 
 | 	{ | 
 | 		return (cx != size.cx || cy != size.cy); | 
 | 	} | 
 |  | 
 | 	void operator +=(SIZE size) | 
 | 	{ | 
 | 		cx += size.cx; | 
 | 		cy += size.cy; | 
 | 	} | 
 |  | 
 | 	void operator -=(SIZE size) | 
 | 	{ | 
 | 		cx -= size.cx; | 
 | 		cy -= size.cy; | 
 | 	} | 
 |  | 
 | 	void SetSize(int CX, int CY) | 
 | 	{ | 
 | 		cx = CX; | 
 | 		cy = CY; | 
 | 	} | 
 |  | 
 | // Operators returning CSize values | 
 | 	CSize operator +(SIZE size) const | 
 | 	{ | 
 | 		return CSize(cx + size.cx, cy + size.cy); | 
 | 	} | 
 |  | 
 | 	CSize operator -(SIZE size) const | 
 | 	{ | 
 | 		return CSize(cx - size.cx, cy - size.cy); | 
 | 	} | 
 |  | 
 | 	CSize operator -() const | 
 | 	{ | 
 | 		return CSize(-cx, -cy); | 
 | 	} | 
 |  | 
 | // Operators returning CPoint values | 
 | 	CPoint operator +(POINT point) const; | 
 | 	CPoint operator -(POINT point) const; | 
 |  | 
 | // Operators returning CRect values | 
 | 	CRect operator +(const RECT* lpRect) const; | 
 | 	CRect operator -(const RECT* lpRect) const; | 
 | }; | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // CPoint - Wrapper for Windows POINT structure. | 
 |  | 
 | class CPoint : public POINT | 
 | { | 
 | public: | 
 | // Constructors | 
 | 	CPoint() | 
 | 	{ | 
 | 		x = 0; | 
 | 		y = 0; | 
 | 	} | 
 |  | 
 | 	CPoint(int initX, int initY) | 
 | 	{ | 
 | 		x = initX; | 
 | 		y = initY; | 
 | 	} | 
 |  | 
 | 	CPoint(POINT initPt) | 
 | 	{ | 
 | 		*(POINT*)this = initPt; | 
 | 	} | 
 |  | 
 | 	CPoint(SIZE initSize) | 
 | 	{ | 
 | 		*(SIZE*)this = initSize; | 
 | 	} | 
 |  | 
 | 	CPoint(DWORD dwPoint) | 
 | 	{ | 
 | 		x = (short)LOWORD(dwPoint); | 
 | 		y = (short)HIWORD(dwPoint); | 
 | 	} | 
 |  | 
 | // Operations | 
 | 	void Offset(int xOffset, int yOffset) | 
 | 	{ | 
 | 		x += xOffset; | 
 | 		y += yOffset; | 
 | 	} | 
 |  | 
 | 	void Offset(POINT point) | 
 | 	{ | 
 | 		x += point.x; | 
 | 		y += point.y; | 
 | 	} | 
 |  | 
 | 	void Offset(SIZE size) | 
 | 	{ | 
 | 		x += size.cx; | 
 | 		y += size.cy; | 
 | 	} | 
 |  | 
 | 	BOOL operator ==(POINT point) const | 
 | 	{ | 
 | 		return (x == point.x && y == point.y); | 
 | 	} | 
 |  | 
 | 	BOOL operator !=(POINT point) const | 
 | 	{ | 
 | 		return (x != point.x || y != point.y); | 
 | 	} | 
 |  | 
 | 	void operator +=(SIZE size) | 
 | 	{ | 
 | 		x += size.cx; | 
 | 		y += size.cy; | 
 | 	} | 
 |  | 
 | 	void operator -=(SIZE size) | 
 | 	{ | 
 | 		x -= size.cx; | 
 | 		y -= size.cy; | 
 | 	} | 
 |  | 
 | 	void operator +=(POINT point) | 
 | 	{ | 
 | 		x += point.x; | 
 | 		y += point.y; | 
 | 	} | 
 |  | 
 | 	void operator -=(POINT point) | 
 | 	{ | 
 | 		x -= point.x; | 
 | 		y -= point.y; | 
 | 	} | 
 |  | 
 | 	void SetPoint(int X, int Y) | 
 | 	{ | 
 | 		x = X; | 
 | 		y = Y; | 
 | 	} | 
 |  | 
 | // Operators returning CPoint values | 
 | 	CPoint operator +(SIZE size) const | 
 | 	{ | 
 | 		return CPoint(x + size.cx, y + size.cy); | 
 | 	} | 
 |  | 
 | 	CPoint operator -(SIZE size) const | 
 | 	{ | 
 | 		return CPoint(x - size.cx, y - size.cy); | 
 | 	} | 
 |  | 
 | 	CPoint operator -() const | 
 | 	{ | 
 | 		return CPoint(-x, -y); | 
 | 	} | 
 |  | 
 | 	CPoint operator +(POINT point) const | 
 | 	{ | 
 | 		return CPoint(x + point.x, y + point.y); | 
 | 	} | 
 |  | 
 | // Operators returning CSize values | 
 | 	CSize operator -(POINT point) const | 
 | 	{ | 
 | 		return CSize(x - point.x, y - point.y); | 
 | 	} | 
 |  | 
 | // Operators returning CRect values | 
 | 	CRect operator +(const RECT* lpRect) const; | 
 | 	CRect operator -(const RECT* lpRect) const; | 
 | }; | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // CRect - Wrapper for Windows RECT structure. | 
 |  | 
 | class CRect : public RECT | 
 | { | 
 | public: | 
 | // Constructors | 
 | 	CRect() | 
 | 	{ | 
 | 		left = 0; | 
 | 		top = 0; | 
 | 		right = 0; | 
 | 		bottom = 0; | 
 | 	} | 
 |  | 
 | 	CRect(int l, int t, int r, int b) | 
 | 	{ | 
 | 		left = l; | 
 | 		top = t; | 
 | 		right = r; | 
 | 		bottom = b; | 
 | 	} | 
 |  | 
 | 	CRect(const RECT& srcRect) | 
 | 	{ | 
 | 		::CopyRect(this, &srcRect); | 
 | 	} | 
 |  | 
 | 	CRect(LPCRECT lpSrcRect) | 
 | 	{ | 
 | 		::CopyRect(this, lpSrcRect); | 
 | 	} | 
 |  | 
 | 	CRect(POINT point, SIZE size) | 
 | 	{ | 
 | 		right = (left = point.x) + size.cx; | 
 | 		bottom = (top = point.y) + size.cy; | 
 | 	} | 
 |  | 
 | 	CRect(POINT topLeft, POINT bottomRight) | 
 | 	{ | 
 | 		left = topLeft.x; | 
 | 		top = topLeft.y; | 
 | 		right = bottomRight.x; | 
 | 		bottom = bottomRight.y; | 
 | 	} | 
 |  | 
 | // Attributes (in addition to RECT members) | 
 | 	int Width() const | 
 | 	{ | 
 | 		return right - left; | 
 | 	} | 
 |  | 
 | 	int Height() const | 
 | 	{ | 
 | 		return bottom - top; | 
 | 	} | 
 |  | 
 | 	CSize Size() const | 
 | 	{ | 
 | 		return CSize(right - left, bottom - top); | 
 | 	} | 
 |  | 
 | 	CPoint& TopLeft() | 
 | 	{ | 
 | 		return *((CPoint*)this); | 
 | 	} | 
 |  | 
 | 	CPoint& BottomRight() | 
 | 	{ | 
 | 		return *((CPoint*)this + 1); | 
 | 	} | 
 |  | 
 | 	const CPoint& TopLeft() const | 
 | 	{ | 
 | 		return *((CPoint*)this); | 
 | 	} | 
 |  | 
 | 	const CPoint& BottomRight() const | 
 | 	{ | 
 | 		return *((CPoint*)this + 1); | 
 | 	} | 
 |  | 
 | 	CPoint CenterPoint() const | 
 | 	{ | 
 | 		return CPoint((left + right) / 2, (top + bottom) / 2); | 
 | 	} | 
 |  | 
 | 	// convert between CRect and LPRECT/LPCRECT (no need for &) | 
 | 	operator LPRECT() | 
 | 	{ | 
 | 		return this; | 
 | 	} | 
 |  | 
 | 	operator LPCRECT() const | 
 | 	{ | 
 | 		return this; | 
 | 	} | 
 |  | 
 | 	BOOL IsRectEmpty() const | 
 | 	{ | 
 | 		return ::IsRectEmpty(this); | 
 | 	} | 
 |  | 
 | 	BOOL IsRectNull() const | 
 | 	{ | 
 | 		return (left == 0 && right == 0 && top == 0 && bottom == 0); | 
 | 	} | 
 |  | 
 | 	BOOL PtInRect(POINT point) const | 
 | 	{ | 
 | 		return ::PtInRect(this, point); | 
 | 	} | 
 |  | 
 | // Operations | 
 | 	void SetRect(int x1, int y1, int x2, int y2) | 
 | 	{ | 
 | 		::SetRect(this, x1, y1, x2, y2); | 
 | 	} | 
 |  | 
 | 	void SetRect(POINT topLeft, POINT bottomRight) | 
 | 	{ | 
 | 		::SetRect(this, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); | 
 | 	} | 
 |  | 
 | 	void SetRectEmpty() | 
 | 	{ | 
 | 		::SetRectEmpty(this); | 
 | 	} | 
 |  | 
 | 	void CopyRect(LPCRECT lpSrcRect) | 
 | 	{ | 
 | 		::CopyRect(this, lpSrcRect); | 
 | 	} | 
 |  | 
 | 	BOOL EqualRect(LPCRECT lpRect) const | 
 | 	{ | 
 | 		return ::EqualRect(this, lpRect); | 
 | 	} | 
 |  | 
 | 	void InflateRect(int x, int y) | 
 | 	{ | 
 | 		::InflateRect(this, x, y); | 
 | 	} | 
 |  | 
 | 	void InflateRect(SIZE size) | 
 | 	{ | 
 | 		::InflateRect(this, size.cx, size.cy); | 
 | 	} | 
 |  | 
 | 	void InflateRect(LPCRECT lpRect) | 
 | 	{ | 
 | 		left -= lpRect->left; | 
 | 		top -= lpRect->top; | 
 | 		right += lpRect->right; | 
 | 		bottom += lpRect->bottom; | 
 | 	} | 
 |  | 
 | 	void InflateRect(int l, int t, int r, int b) | 
 | 	{ | 
 | 		left -= l; | 
 | 		top -= t; | 
 | 		right += r; | 
 | 		bottom += b; | 
 | 	} | 
 |  | 
 | 	void DeflateRect(int x, int y) | 
 | 	{ | 
 | 		::InflateRect(this, -x, -y); | 
 | 	} | 
 |  | 
 | 	void DeflateRect(SIZE size) | 
 | 	{ | 
 | 		::InflateRect(this, -size.cx, -size.cy); | 
 | 	} | 
 |  | 
 | 	void DeflateRect(LPCRECT lpRect) | 
 | 	{ | 
 | 		left += lpRect->left; | 
 | 		top += lpRect->top; | 
 | 		right -= lpRect->right; | 
 | 		bottom -= lpRect->bottom; | 
 | 	} | 
 |  | 
 | 	void DeflateRect(int l, int t, int r, int b) | 
 | 	{ | 
 | 		left += l; | 
 | 		top += t; | 
 | 		right -= r; | 
 | 		bottom -= b; | 
 | 	} | 
 |  | 
 | 	void OffsetRect(int x, int y) | 
 | 	{ | 
 | 		::OffsetRect(this, x, y); | 
 | 	} | 
 | 	void OffsetRect(SIZE size) | 
 | 	{ | 
 | 		::OffsetRect(this, size.cx, size.cy); | 
 | 	} | 
 |  | 
 | 	void OffsetRect(POINT point) | 
 | 	{ | 
 | 		::OffsetRect(this, point.x, point.y); | 
 | 	} | 
 |  | 
 | 	void NormalizeRect() | 
 | 	{ | 
 | 		int nTemp; | 
 | 		if (left > right) | 
 | 		{ | 
 | 			nTemp = left; | 
 | 			left = right; | 
 | 			right = nTemp; | 
 | 		} | 
 | 		if (top > bottom) | 
 | 		{ | 
 | 			nTemp = top; | 
 | 			top = bottom; | 
 | 			bottom = nTemp; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// absolute position of rectangle | 
 | 	void MoveToY(int y) | 
 | 	{ | 
 | 		bottom = Height() + y; | 
 | 		top = y; | 
 | 	} | 
 |  | 
 | 	void MoveToX(int x) | 
 | 	{ | 
 | 		right = Width() + x; | 
 | 		left = x; | 
 | 	} | 
 |  | 
 | 	void MoveToXY(int x, int y) | 
 | 	{ | 
 | 		MoveToX(x); | 
 | 		MoveToY(y); | 
 | 	} | 
 |  | 
 | 	void MoveToXY(POINT pt) | 
 | 	{ | 
 | 		MoveToX(pt.x); | 
 | 		MoveToY(pt.y); | 
 | 	} | 
 |  | 
 | 	// operations that fill '*this' with result | 
 | 	BOOL IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2) | 
 | 	{ | 
 | 		return ::IntersectRect(this, lpRect1, lpRect2); | 
 | 	} | 
 |  | 
 | 	BOOL UnionRect(LPCRECT lpRect1, LPCRECT lpRect2) | 
 | 	{ | 
 | 		return ::UnionRect(this, lpRect1, lpRect2); | 
 | 	} | 
 |  | 
 | 	BOOL SubtractRect(LPCRECT lpRectSrc1, LPCRECT lpRectSrc2) | 
 | 	{ | 
 | 		return ::SubtractRect(this, lpRectSrc1, lpRectSrc2); | 
 | 	} | 
 |  | 
 | // Additional Operations | 
 | 	void operator =(const RECT& srcRect) | 
 | 	{ | 
 | 		::CopyRect(this, &srcRect); | 
 | 	} | 
 |  | 
 | 	BOOL operator ==(const RECT& rect) const | 
 | 	{ | 
 | 		return ::EqualRect(this, &rect); | 
 | 	} | 
 |  | 
 | 	BOOL operator !=(const RECT& rect) const | 
 | 	{ | 
 | 		return !::EqualRect(this, &rect); | 
 | 	} | 
 |  | 
 | 	void operator +=(POINT point) | 
 | 	{ | 
 | 		::OffsetRect(this, point.x, point.y); | 
 | 	} | 
 |  | 
 | 	void operator +=(SIZE size) | 
 | 	{ | 
 | 		::OffsetRect(this, size.cx, size.cy); | 
 | 	} | 
 |  | 
 | 	void operator +=(LPCRECT lpRect) | 
 | 	{ | 
 | 		InflateRect(lpRect); | 
 | 	} | 
 |  | 
 | 	void operator -=(POINT point) | 
 | 	{ | 
 | 		::OffsetRect(this, -point.x, -point.y); | 
 | 	} | 
 |  | 
 | 	void operator -=(SIZE size) | 
 | 	{ | 
 | 		::OffsetRect(this, -size.cx, -size.cy); | 
 | 	} | 
 |  | 
 | 	void operator -=(LPCRECT lpRect) | 
 | 	{ | 
 | 		DeflateRect(lpRect); | 
 | 	} | 
 |  | 
 | 	void operator &=(const RECT& rect) | 
 | 	{ | 
 | 		::IntersectRect(this, this, &rect); | 
 | 	} | 
 |  | 
 | 	void operator |=(const RECT& rect) | 
 | 	{ | 
 | 		::UnionRect(this, this, &rect); | 
 | 	} | 
 |  | 
 | // Operators returning CRect values | 
 | 	CRect operator +(POINT pt) const | 
 | 	{ | 
 | 		CRect rect(*this); | 
 | 		::OffsetRect(&rect, pt.x, pt.y); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect operator -(POINT pt) const | 
 | 	{ | 
 | 		CRect rect(*this); | 
 | 		::OffsetRect(&rect, -pt.x, -pt.y); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect operator +(LPCRECT lpRect) const | 
 | 	{ | 
 | 		CRect rect(this); | 
 | 		rect.InflateRect(lpRect); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect operator +(SIZE size) const | 
 | 	{ | 
 | 		CRect rect(*this); | 
 | 		::OffsetRect(&rect, size.cx, size.cy); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect operator -(SIZE size) const | 
 | 	{ | 
 | 		CRect rect(*this); | 
 | 		::OffsetRect(&rect, -size.cx, -size.cy); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect operator -(LPCRECT lpRect) const | 
 | 	{ | 
 | 		CRect rect(this); | 
 | 		rect.DeflateRect(lpRect); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect operator &(const RECT& rect2) const | 
 | 	{ | 
 | 		CRect rect; | 
 | 		::IntersectRect(&rect, this, &rect2); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect operator |(const RECT& rect2) const | 
 | 	{ | 
 | 		CRect rect; | 
 | 		::UnionRect(&rect, this, &rect2); | 
 | 		return rect; | 
 | 	} | 
 |  | 
 | 	CRect MulDiv(int nMultiplier, int nDivisor) const | 
 | 	{ | 
 | 		return CRect( | 
 | 			::MulDiv(left, nMultiplier, nDivisor), | 
 | 			::MulDiv(top, nMultiplier, nDivisor), | 
 | 			::MulDiv(right, nMultiplier, nDivisor), | 
 | 			::MulDiv(bottom, nMultiplier, nDivisor)); | 
 | 	} | 
 | }; | 
 |  | 
 |  | 
 | // CSize implementation | 
 |  | 
 | inline CPoint CSize::operator +(POINT point) const | 
 | { return CPoint(cx + point.x, cy + point.y); } | 
 |  | 
 | inline CPoint CSize::operator -(POINT point) const | 
 | { return CPoint(cx - point.x, cy - point.y); } | 
 |  | 
 | inline CRect CSize::operator +(const RECT* lpRect) const | 
 | { return CRect(lpRect) + *this; } | 
 |  | 
 | inline CRect CSize::operator -(const RECT* lpRect) const | 
 | { return CRect(lpRect) - *this; } | 
 |  | 
 |  | 
 | // CPoint implementation | 
 |  | 
 | inline CRect CPoint::operator +(const RECT* lpRect) const | 
 | { return CRect(lpRect) + *this; } | 
 |  | 
 | inline CRect CPoint::operator -(const RECT* lpRect) const | 
 | { return CRect(lpRect) - *this; } | 
 |  | 
 | #endif // !_WTL_NO_WTYPES | 
 |  | 
 |  | 
 | // WTL::CSize or ATL::CSize scalar operators  | 
 |  | 
 | #if !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__)) | 
 |  | 
 | template <class Num> | 
 | inline CSize operator *(SIZE s, Num n)  | 
 | { | 
 | 	return CSize((int)(s.cx * n), (int)(s.cy * n)); | 
 | }; | 
 |  | 
 | template <class Num> | 
 | inline void operator *=(SIZE & s, Num n) | 
 | { | 
 | 	s = s * n; | 
 | };	 | 
 |  | 
 | template <class Num> | 
 | inline CSize operator /(SIZE s, Num n)  | 
 | { | 
 | 	return CSize((int)(s.cx / n), (int)(s.cy / n)); | 
 | }; | 
 |  | 
 | template <class Num> | 
 | inline void operator /=(SIZE & s, Num n) | 
 | { | 
 | 	s = s / n; | 
 | };	 | 
 |  | 
 | #endif // !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__)) | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // CString - String class | 
 |  | 
 | #ifndef _WTL_NO_CSTRING | 
 |  | 
 | struct CStringData | 
 | { | 
 | 	long nRefs;     // reference count | 
 | 	int nDataLength; | 
 | 	int nAllocLength; | 
 | 	// TCHAR data[nAllocLength] | 
 |  | 
 | 	TCHAR* data() | 
 | 	{ return (TCHAR*)(this + 1); } | 
 | }; | 
 |  | 
 | // Globals | 
 |  | 
 | // For an empty string, m_pchData will point here | 
 | // (note: avoids special case of checking for NULL m_pchData) | 
 | // empty string data (and locked) | 
 | _declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 }; | 
 | _declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData; | 
 | _declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData)); | 
 |  | 
 |  | 
 | class CString | 
 | { | 
 | public: | 
 | // Constructors | 
 | 	CString() | 
 | 	{ | 
 | 		Init(); | 
 | 	} | 
 |  | 
 | 	CString(const CString& stringSrc) | 
 | 	{ | 
 | 		ATLASSERT(stringSrc.GetData()->nRefs != 0); | 
 | 		if (stringSrc.GetData()->nRefs >= 0) | 
 | 		{ | 
 | 			ATLASSERT(stringSrc.GetData() != _atltmpDataNil); | 
 | 			m_pchData = stringSrc.m_pchData; | 
 | 			InterlockedIncrement(&GetData()->nRefs); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			Init(); | 
 | 			*this = stringSrc.m_pchData; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	CString(TCHAR ch, int nRepeat = 1) | 
 | 	{ | 
 | 		ATLASSERT(!_istlead(ch));   // can't create a lead byte string | 
 | 		Init(); | 
 | 		if (nRepeat >= 1) | 
 | 		{ | 
 | 			if(AllocBuffer(nRepeat)) | 
 | 			{ | 
 | #ifdef _UNICODE | 
 | 				for (int i = 0; i < nRepeat; i++) | 
 | 					m_pchData[i] = ch; | 
 | #else | 
 | 				memset(m_pchData, ch, nRepeat); | 
 | #endif | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	CString(LPCTSTR lpsz) | 
 | 	{ | 
 | 		Init(); | 
 | 		if (lpsz != NULL && HIWORD(lpsz) == NULL) | 
 | 		{ | 
 | 			UINT nID = LOWORD((DWORD_PTR)lpsz); | 
 | 			if (!LoadString(nID)) | 
 | 				ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			int nLen = SafeStrlen(lpsz); | 
 | 			if (nLen != 0) | 
 | 			{ | 
 | 				if(AllocBuffer(nLen)) | 
 | 					SecureHelper::memcpy_x(m_pchData, (nLen + 1) * sizeof(TCHAR), lpsz, nLen * sizeof(TCHAR)); | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | #ifdef _UNICODE | 
 | 	CString(LPCSTR lpsz) | 
 | 	{ | 
 | 		Init(); | 
 | #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) | 
 | 		int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0; | 
 | #else | 
 | 		int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0; | 
 | #endif | 
 | 		if (nSrcLen != 0) | 
 | 		{ | 
 | 			if(AllocBuffer(nSrcLen)) | 
 | 			{ | 
 | 				_mbstowcsz(m_pchData, lpsz, nSrcLen + 1); | 
 | 				ReleaseBuffer(); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | #else // !_UNICODE | 
 | 	CString(LPCWSTR lpsz) | 
 | 	{ | 
 | 		Init(); | 
 | 		int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0; | 
 | 		if (nSrcLen != 0) | 
 | 		{ | 
 | 			if(AllocBuffer(nSrcLen * 2)) | 
 | 			{ | 
 | 				_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1); | 
 | 				ReleaseBuffer(); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | #endif // !_UNICODE | 
 |  | 
 | 	CString(LPCTSTR lpch, int nLength) | 
 | 	{ | 
 | 		Init(); | 
 | 		if (nLength != 0) | 
 | 		{ | 
 | 			if(AllocBuffer(nLength)) | 
 | 				SecureHelper::memcpy_x(m_pchData, (nLength + 1) * sizeof(TCHAR), lpch, nLength * sizeof(TCHAR)); | 
 | 		} | 
 | 	} | 
 |  | 
 | #ifdef _UNICODE | 
 | 	CString(LPCSTR lpsz, int nLength) | 
 | 	{ | 
 | 		Init(); | 
 | 		if (nLength != 0) | 
 | 		{ | 
 | 			if(AllocBuffer(nLength)) | 
 | 			{ | 
 | 				int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength + 1); | 
 | 				ReleaseBuffer((n >= 0) ? n : -1); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | #else // !_UNICODE | 
 | 	CString(LPCWSTR lpsz, int nLength) | 
 | 	{ | 
 | 		Init(); | 
 | 		if (nLength != 0) | 
 | 		{ | 
 | 			if(((nLength * 2) > nLength) && AllocBuffer(nLength * 2)) | 
 | 			{ | 
 | 				int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData, (nLength * 2) + 1, NULL, NULL); | 
 | 				ReleaseBuffer((n >= 0) ? n : -1); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | #endif // !_UNICODE | 
 |  | 
 | 	CString(const unsigned char* lpsz) | 
 | 	{ | 
 | 		Init(); | 
 | 		*this = (LPCSTR)lpsz; | 
 | 	} | 
 |  | 
 | // Attributes & Operations | 
 | 	int GetLength() const   // as an array of characters | 
 | 	{ | 
 | 		return GetData()->nDataLength; | 
 | 	} | 
 |  | 
 | 	BOOL IsEmpty() const | 
 | 	{ | 
 | 		return GetData()->nDataLength == 0; | 
 | 	} | 
 |  | 
 | 	void Empty()   // free up the data | 
 | 	{ | 
 | 		if (GetData()->nDataLength == 0) | 
 | 			return; | 
 |  | 
 | 		if (GetData()->nRefs >= 0) | 
 | 			Release(); | 
 | 		else | 
 | 			*this = _T(""); | 
 |  | 
 | 		ATLASSERT(GetData()->nDataLength == 0); | 
 | 		ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0); | 
 | 	} | 
 |  | 
 | 	TCHAR GetAt(int nIndex) const   // 0 based | 
 | 	{ | 
 | 		ATLASSERT(nIndex >= 0); | 
 | 		ATLASSERT(nIndex < GetData()->nDataLength); | 
 | 		return m_pchData[nIndex]; | 
 | 	} | 
 |  | 
 | 	TCHAR operator [](int nIndex) const   // same as GetAt | 
 | 	{ | 
 | 		// same as GetAt | 
 | 		ATLASSERT(nIndex >= 0); | 
 | 		ATLASSERT(nIndex < GetData()->nDataLength); | 
 | 		return m_pchData[nIndex]; | 
 | 	} | 
 |  | 
 | 	void SetAt(int nIndex, TCHAR ch) | 
 | 	{ | 
 | 		ATLASSERT(nIndex >= 0); | 
 | 		ATLASSERT(nIndex < GetData()->nDataLength); | 
 |  | 
 | 		CopyBeforeWrite(); | 
 | 		m_pchData[nIndex] = ch; | 
 | 	} | 
 |  | 
 | 	operator LPCTSTR() const   // as a C string | 
 | 	{ | 
 | 		return m_pchData; | 
 | 	} | 
 |  | 
 | 	// overloaded assignment | 
 | 	CString& operator =(const CString& stringSrc) | 
 | 	{ | 
 | 		if (m_pchData != stringSrc.m_pchData) | 
 | 		{ | 
 | 			if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0) | 
 | 			{ | 
 | 				// actual copy necessary since one of the strings is locked | 
 | 				AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				// can just copy references around | 
 | 				Release(); | 
 | 				ATLASSERT(stringSrc.GetData() != _atltmpDataNil); | 
 | 				m_pchData = stringSrc.m_pchData; | 
 | 				InterlockedIncrement(&GetData()->nRefs); | 
 | 			} | 
 | 		} | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | 	CString& operator =(TCHAR ch) | 
 | 	{ | 
 | 		ATLASSERT(!_istlead(ch));   // can't set single lead byte | 
 | 		AssignCopy(1, &ch); | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | #ifdef _UNICODE | 
 | 	CString& operator =(char ch) | 
 | 	{ | 
 | 		*this = (TCHAR)ch; | 
 | 		return *this; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	CString& operator =(LPCTSTR lpsz) | 
 | 	{ | 
 | 		ATLASSERT(lpsz == NULL || _IsValidString(lpsz)); | 
 | 		AssignCopy(SafeStrlen(lpsz), lpsz); | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | #ifdef _UNICODE | 
 | 	CString& operator =(LPCSTR lpsz) | 
 | 	{ | 
 | #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) | 
 | 		int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0; | 
 | #else | 
 | 		int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0; | 
 | #endif | 
 | 		if(AllocBeforeWrite(nSrcLen)) | 
 | 		{ | 
 | 			_mbstowcsz(m_pchData, lpsz, nSrcLen + 1); | 
 | 			ReleaseBuffer(); | 
 | 		} | 
 | 		return *this; | 
 | 	} | 
 | #else // !_UNICODE | 
 | 	CString& operator =(LPCWSTR lpsz) | 
 | 	{ | 
 | 		int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0; | 
 | 		if(AllocBeforeWrite(nSrcLen * 2)) | 
 | 		{ | 
 | 			_wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1); | 
 | 			ReleaseBuffer(); | 
 | 		} | 
 | 		return *this; | 
 | 	} | 
 | #endif  // !_UNICODE | 
 |  | 
 | 	CString& operator =(const unsigned char* lpsz) | 
 | 	{ | 
 | 		*this = (LPCSTR)lpsz; | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | 	// string concatenation | 
 | 	CString& operator +=(const CString& string) | 
 | 	{ | 
 | 		ConcatInPlace(string.GetData()->nDataLength, string.m_pchData); | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | 	CString& operator +=(TCHAR ch) | 
 | 	{ | 
 | 		ConcatInPlace(1, &ch); | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | #ifdef _UNICODE | 
 | 	CString& operator +=(char ch) | 
 | 	{ | 
 | 		*this += (TCHAR)ch; | 
 | 		return *this; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	CString& operator +=(LPCTSTR lpsz) | 
 | 	{ | 
 | 		ATLASSERT(lpsz == NULL || _IsValidString(lpsz)); | 
 | 		ConcatInPlace(SafeStrlen(lpsz), lpsz); | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | 	friend CString __stdcall operator +(const CString& string1, const CString& string2); | 
 | 	friend CString __stdcall operator +(const CString& string, TCHAR ch); | 
 | 	friend CString __stdcall operator +(TCHAR ch, const CString& string); | 
 | #ifdef _UNICODE | 
 | 	friend CString __stdcall operator +(const CString& string, char ch); | 
 | 	friend CString __stdcall operator +(char ch, const CString& string); | 
 | #endif | 
 | 	friend CString __stdcall operator +(const CString& string, LPCTSTR lpsz); | 
 | 	friend CString __stdcall operator +(LPCTSTR lpsz, const CString& string); | 
 |  | 
 | 	// string comparison | 
 | 	int Compare(LPCTSTR lpsz) const   // straight character (MBCS/Unicode aware) | 
 | 	{ | 
 | 		return _cstrcmp(m_pchData, lpsz); | 
 | 	} | 
 |  | 
 | 	int CompareNoCase(LPCTSTR lpsz) const   // ignore case (MBCS/Unicode aware) | 
 | 	{ | 
 | 		return _cstrcmpi(m_pchData, lpsz); | 
 | 	} | 
 |  | 
 | #ifndef _WIN32_WCE | 
 | 	// CString::Collate is often slower than Compare but is MBSC/Unicode | 
 | 	//  aware as well as locale-sensitive with respect to sort order. | 
 | 	int Collate(LPCTSTR lpsz) const   // NLS aware | 
 | 	{ | 
 | 		return _cstrcoll(m_pchData, lpsz); | 
 | 	} | 
 |  | 
 | 	int CollateNoCase(LPCTSTR lpsz) const   // ignore case | 
 | 	{ | 
 | 		return _cstrcolli(m_pchData, lpsz); | 
 | 	} | 
 | #endif // !_WIN32_WCE | 
 |  | 
 | 	// simple sub-string extraction | 
 | 	CString Mid(int nFirst, int nCount) const | 
 | 	{ | 
 | 		// out-of-bounds requests return sensible things | 
 | 		if (nFirst < 0) | 
 | 			nFirst = 0; | 
 | 		if (nCount < 0) | 
 | 			nCount = 0; | 
 |  | 
 | 		if (nFirst + nCount > GetData()->nDataLength) | 
 | 			nCount = GetData()->nDataLength - nFirst; | 
 | 		if (nFirst > GetData()->nDataLength) | 
 | 			nCount = 0; | 
 |  | 
 | 		CString dest; | 
 | 		AllocCopy(dest, nCount, nFirst, 0); | 
 | 		return dest; | 
 | 	} | 
 |  | 
 | 	CString Mid(int nFirst) const | 
 | 	{ | 
 | 		return Mid(nFirst, GetData()->nDataLength - nFirst); | 
 | 	} | 
 |  | 
 | 	CString Left(int nCount) const | 
 | 	{ | 
 | 		if (nCount < 0) | 
 | 			nCount = 0; | 
 | 		else if (nCount > GetData()->nDataLength) | 
 | 			nCount = GetData()->nDataLength; | 
 |  | 
 | 		CString dest; | 
 | 		AllocCopy(dest, nCount, 0, 0); | 
 | 		return dest; | 
 | 	} | 
 |  | 
 | 	CString Right(int nCount) const | 
 | 	{ | 
 | 		if (nCount < 0) | 
 | 			nCount = 0; | 
 | 		else if (nCount > GetData()->nDataLength) | 
 | 			nCount = GetData()->nDataLength; | 
 |  | 
 | 		CString dest; | 
 | 		AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0); | 
 | 		return dest; | 
 | 	} | 
 |  | 
 | 	CString SpanIncluding(LPCTSTR lpszCharSet) const   // strspn equivalent | 
 | 	{ | 
 | 		ATLASSERT(_IsValidString(lpszCharSet)); | 
 | 		return Left(_cstrspn(m_pchData, lpszCharSet)); | 
 | 	} | 
 |  | 
 | 	CString SpanExcluding(LPCTSTR lpszCharSet) const   // strcspn equivalent | 
 | 	{ | 
 | 		ATLASSERT(_IsValidString(lpszCharSet)); | 
 | 		return Left(_cstrcspn(m_pchData, lpszCharSet)); | 
 | 	} | 
 |  | 
 | 	// upper/lower/reverse conversion | 
 | 	void MakeUpper() | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 | 		CharUpper(m_pchData); | 
 | 	} | 
 |  | 
 | 	void MakeLower() | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 | 		CharLower(m_pchData); | 
 | 	} | 
 |  | 
 | 	void MakeReverse() | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 | 		_cstrrev(m_pchData); | 
 | 	} | 
 |  | 
 | 	// trimming whitespace (either side) | 
 | 	void TrimRight() | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 |  | 
 | 		// find beginning of trailing spaces by starting at beginning (DBCS aware) | 
 | 		LPTSTR lpsz = m_pchData; | 
 | 		LPTSTR lpszLast = NULL; | 
 | 		while (*lpsz != _T('\0')) | 
 | 		{ | 
 | 			if (_cstrisspace(*lpsz)) | 
 | 			{ | 
 | 				if (lpszLast == NULL) | 
 | 					lpszLast = lpsz; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				lpszLast = NULL; | 
 | 			} | 
 | 			lpsz = ::CharNext(lpsz); | 
 | 		} | 
 |  | 
 | 		if (lpszLast != NULL) | 
 | 		{ | 
 | 			// truncate at trailing space start | 
 | 			*lpszLast = _T('\0'); | 
 | 			GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	void TrimLeft() | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 |  | 
 | 		// find first non-space character | 
 | 		LPCTSTR lpsz = m_pchData; | 
 | 		while (_cstrisspace(*lpsz)) | 
 | 			lpsz = ::CharNext(lpsz); | 
 |  | 
 | 		// fix up data and length | 
 | 		int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData); | 
 | 		SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR)); | 
 | 		GetData()->nDataLength = nDataLength; | 
 | 	} | 
 |  | 
 | 	// remove continuous occurrences of chTarget starting from right | 
 | 	void TrimRight(TCHAR chTarget) | 
 | 	{ | 
 | 		// find beginning of trailing matches | 
 | 		// by starting at beginning (DBCS aware) | 
 |  | 
 | 		CopyBeforeWrite(); | 
 | 		LPTSTR lpsz = m_pchData; | 
 | 		LPTSTR lpszLast = NULL; | 
 |  | 
 | 		while (*lpsz != _T('\0')) | 
 | 		{ | 
 | 			if (*lpsz == chTarget) | 
 | 			{ | 
 | 				if (lpszLast == NULL) | 
 | 					lpszLast = lpsz; | 
 | 			} | 
 | 			else | 
 | 				lpszLast = NULL; | 
 | 			lpsz = ::CharNext(lpsz); | 
 | 		} | 
 |  | 
 | 		if (lpszLast != NULL) | 
 | 		{ | 
 | 			// truncate at left-most matching character | 
 | 			*lpszLast = _T('\0'); | 
 | 			GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// remove continuous occcurrences of characters in passed string, starting from right | 
 | 	void TrimRight(LPCTSTR lpszTargetList) | 
 | 	{ | 
 | 		// find beginning of trailing matches by starting at beginning (DBCS aware) | 
 |  | 
 | 		CopyBeforeWrite(); | 
 | 		LPTSTR lpsz = m_pchData; | 
 | 		LPTSTR lpszLast = NULL; | 
 |  | 
 | 		while (*lpsz != _T('\0')) | 
 | 		{ | 
 | 			TCHAR* pNext = ::CharNext(lpsz); | 
 | 			if(pNext > lpsz + 1) | 
 | 			{ | 
 | 				if (_cstrchr_db(lpszTargetList, *lpsz, *(lpsz + 1)) != NULL) | 
 | 				{ | 
 | 					if (lpszLast == NULL) | 
 | 						lpszLast = lpsz; | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					lpszLast = NULL; | 
 | 				} | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				if (_cstrchr(lpszTargetList, *lpsz) != NULL) | 
 | 				{ | 
 | 					if (lpszLast == NULL) | 
 | 						lpszLast = lpsz; | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					lpszLast = NULL; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			lpsz = pNext; | 
 | 		} | 
 |  | 
 | 		if (lpszLast != NULL) | 
 | 		{ | 
 | 			// truncate at left-most matching character | 
 | 			*lpszLast = _T('\0'); | 
 | 			GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// remove continuous occurrences of chTarget starting from left | 
 | 	void TrimLeft(TCHAR chTarget) | 
 | 	{ | 
 | 		// find first non-matching character | 
 |  | 
 | 		CopyBeforeWrite(); | 
 | 		LPCTSTR lpsz = m_pchData; | 
 |  | 
 | 		while (chTarget == *lpsz) | 
 | 			lpsz = ::CharNext(lpsz); | 
 |  | 
 | 		if (lpsz != m_pchData) | 
 | 		{ | 
 | 			// fix up data and length | 
 | 			int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData); | 
 | 			SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR)); | 
 | 			GetData()->nDataLength = nDataLength; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// remove continuous occcurrences of characters in passed string, starting from left | 
 | 	void TrimLeft(LPCTSTR lpszTargets) | 
 | 	{ | 
 | 		// if we're not trimming anything, we're not doing any work | 
 | 		if (SafeStrlen(lpszTargets) == 0) | 
 | 			return; | 
 |  | 
 | 		CopyBeforeWrite(); | 
 | 		LPCTSTR lpsz = m_pchData; | 
 |  | 
 | 		while (*lpsz != _T('\0')) | 
 | 		{ | 
 | 			TCHAR* pNext = ::CharNext(lpsz); | 
 | 			if(pNext > lpsz + 1) | 
 | 			{ | 
 | 				if (_cstrchr_db(lpszTargets, *lpsz, *(lpsz + 1)) == NULL) | 
 | 					break; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				if (_cstrchr(lpszTargets, *lpsz) == NULL) | 
 | 					break; | 
 | 			} | 
 | 			lpsz = pNext; | 
 | 		} | 
 |  | 
 | 		if (lpsz != m_pchData) | 
 | 		{ | 
 | 			// fix up data and length | 
 | 			int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData); | 
 | 			SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR)); | 
 | 			GetData()->nDataLength = nDataLength; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// advanced manipulation | 
 | 	// replace occurrences of chOld with chNew | 
 | 	int Replace(TCHAR chOld, TCHAR chNew) | 
 | 	{ | 
 | 		int nCount = 0; | 
 |  | 
 | 		// short-circuit the nop case | 
 | 		if (chOld != chNew) | 
 | 		{ | 
 | 			// otherwise modify each character that matches in the string | 
 | 			CopyBeforeWrite(); | 
 | 			LPTSTR psz = m_pchData; | 
 | 			LPTSTR pszEnd = psz + GetData()->nDataLength; | 
 | 			while (psz < pszEnd) | 
 | 			{ | 
 | 				// replace instances of the specified character only | 
 | 				if (*psz == chOld) | 
 | 				{ | 
 | 					*psz = chNew; | 
 | 					nCount++; | 
 | 				} | 
 | 				psz = ::CharNext(psz); | 
 | 			} | 
 | 		} | 
 | 		return nCount; | 
 | 	} | 
 |  | 
 | 	// replace occurrences of substring lpszOld with lpszNew; | 
 | 	// empty lpszNew removes instances of lpszOld | 
 | 	int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew) | 
 | 	{ | 
 | 		// can't have empty or NULL lpszOld | 
 |  | 
 | 		int nSourceLen = SafeStrlen(lpszOld); | 
 | 		if (nSourceLen == 0) | 
 | 			return 0; | 
 | 		int nReplacementLen = SafeStrlen(lpszNew); | 
 |  | 
 | 		// loop once to figure out the size of the result string | 
 | 		int nCount = 0; | 
 | 		LPTSTR lpszStart = m_pchData; | 
 | 		LPTSTR lpszEnd = m_pchData + GetData()->nDataLength; | 
 | 		LPTSTR lpszTarget = NULL; | 
 | 		while (lpszStart < lpszEnd) | 
 | 		{ | 
 | 			while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL) | 
 | 			{ | 
 | 				nCount++; | 
 | 				lpszStart = lpszTarget + nSourceLen; | 
 | 			} | 
 | 			lpszStart += lstrlen(lpszStart) + 1; | 
 | 		} | 
 |  | 
 | 		// if any changes were made, make them | 
 | 		if (nCount > 0) | 
 | 		{ | 
 | 			CopyBeforeWrite(); | 
 |  | 
 | 			// if the buffer is too small, just allocate a new buffer (slow but sure) | 
 | 			int nOldLength = GetData()->nDataLength; | 
 | 			int nNewLength =  nOldLength + (nReplacementLen - nSourceLen) * nCount; | 
 | 			if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1) | 
 | 			{ | 
 | 				CStringData* pOldData = GetData(); | 
 | 				LPTSTR pstr = m_pchData; | 
 | 				if(!AllocBuffer(nNewLength)) | 
 | 					return -1; | 
 | 				SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, pOldData->nDataLength * sizeof(TCHAR)); | 
 | 				CString::Release(pOldData); | 
 | 			} | 
 | 			// else, we just do it in-place | 
 | 			lpszStart = m_pchData; | 
 | 			lpszEnd = m_pchData + GetData()->nDataLength; | 
 |  | 
 | 			// loop again to actually do the work | 
 | 			while (lpszStart < lpszEnd) | 
 | 			{ | 
 | 				while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL) | 
 | 				{ | 
 | 					int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen); | 
 | 					int cchBuffLen = GetData()->nAllocLength - (int)(DWORD_PTR)(lpszTarget - m_pchData); | 
 | 					SecureHelper::memmove_x(lpszTarget + nReplacementLen, (cchBuffLen - nReplacementLen + 1) * sizeof(TCHAR), lpszTarget + nSourceLen, nBalance * sizeof(TCHAR)); | 
 | 					SecureHelper::memcpy_x(lpszTarget, (cchBuffLen + 1) * sizeof(TCHAR), lpszNew, nReplacementLen * sizeof(TCHAR)); | 
 | 					lpszStart = lpszTarget + nReplacementLen; | 
 | 					lpszStart[nBalance] = _T('\0'); | 
 | 					nOldLength += (nReplacementLen - nSourceLen); | 
 | 				} | 
 | 				lpszStart += lstrlen(lpszStart) + 1; | 
 | 			} | 
 | 			ATLASSERT(m_pchData[nNewLength] == _T('\0')); | 
 | 			GetData()->nDataLength = nNewLength; | 
 | 		} | 
 |  | 
 | 		return nCount; | 
 | 	} | 
 |  | 
 | 	// remove occurrences of chRemove | 
 | 	int Remove(TCHAR chRemove) | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 |  | 
 | 		LPTSTR pstrSource = m_pchData; | 
 | 		LPTSTR pstrDest = m_pchData; | 
 | 		LPTSTR pstrEnd = m_pchData + GetData()->nDataLength; | 
 |  | 
 | 		while (pstrSource < pstrEnd) | 
 | 		{ | 
 | 			if (*pstrSource != chRemove) | 
 | 			{ | 
 | 				*pstrDest = *pstrSource; | 
 | 				pstrDest = ::CharNext(pstrDest); | 
 | 			} | 
 | 			pstrSource = ::CharNext(pstrSource); | 
 | 		} | 
 | 		*pstrDest = _T('\0'); | 
 | 		int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest); | 
 | 		GetData()->nDataLength -= nCount; | 
 |  | 
 | 		return nCount; | 
 | 	} | 
 |  | 
 | 	// insert character at zero-based index; concatenates if index is past end of string | 
 | 	int Insert(int nIndex, TCHAR ch) | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 |  | 
 | 		if (nIndex < 0) | 
 | 			nIndex = 0; | 
 |  | 
 | 		int nNewLength = GetData()->nDataLength; | 
 | 		if (nIndex > nNewLength) | 
 | 			nIndex = nNewLength; | 
 | 		nNewLength++; | 
 |  | 
 | 		if (GetData()->nAllocLength < nNewLength) | 
 | 		{ | 
 | 			CStringData* pOldData = GetData(); | 
 | 			LPTSTR pstr = m_pchData; | 
 | 			if(!AllocBuffer(nNewLength)) | 
 | 				return -1; | 
 | 			SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR)); | 
 | 			CString::Release(pOldData); | 
 | 		} | 
 |  | 
 | 		// move existing bytes down | 
 | 		SecureHelper::memmove_x(m_pchData + nIndex + 1, (GetData()->nAllocLength - nIndex) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR)); | 
 | 		m_pchData[nIndex] = ch; | 
 | 		GetData()->nDataLength = nNewLength; | 
 |  | 
 | 		return nNewLength; | 
 | 	} | 
 |  | 
 | 	// insert substring at zero-based index; concatenates if index is past end of string | 
 | 	int Insert(int nIndex, LPCTSTR pstr) | 
 | 	{ | 
 | 		if (nIndex < 0) | 
 | 			nIndex = 0; | 
 |  | 
 | 		int nInsertLength = SafeStrlen(pstr); | 
 | 		int nNewLength = GetData()->nDataLength; | 
 | 		if (nInsertLength > 0) | 
 | 		{ | 
 | 			CopyBeforeWrite(); | 
 | 			if (nIndex > nNewLength) | 
 | 				nIndex = nNewLength; | 
 | 			nNewLength += nInsertLength; | 
 |  | 
 | 			if (GetData()->nAllocLength < nNewLength) | 
 | 			{ | 
 | 				CStringData* pOldData = GetData(); | 
 | 				LPTSTR pstr = m_pchData; | 
 | 				if(!AllocBuffer(nNewLength)) | 
 | 					return -1; | 
 | 				SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR)); | 
 | 				CString::Release(pOldData); | 
 | 			} | 
 |  | 
 | 			// move existing bytes down | 
 | 			SecureHelper::memmove_x(m_pchData + nIndex + nInsertLength, (GetData()->nAllocLength + 1 - nIndex - nInsertLength) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR)); | 
 | 			SecureHelper::memcpy_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), pstr, nInsertLength * sizeof(TCHAR)); | 
 | 			GetData()->nDataLength = nNewLength; | 
 | 		} | 
 |  | 
 | 		return nNewLength; | 
 | 	} | 
 |  | 
 | 	// delete nCount characters starting at zero-based index | 
 | 	int Delete(int nIndex, int nCount = 1) | 
 | 	{ | 
 | 		if (nIndex < 0) | 
 | 			nIndex = 0; | 
 | 		int nLength = GetData()->nDataLength; | 
 | 		if (nCount > 0 && nIndex < nLength) | 
 | 		{ | 
 | 			if((nIndex + nCount) > nLength) | 
 | 				nCount = nLength - nIndex; | 
 | 			CopyBeforeWrite(); | 
 | 			int nBytesToCopy = nLength - (nIndex + nCount) + 1; | 
 |  | 
 | 			SecureHelper::memmove_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR)); | 
 | 			nLength -= nCount; | 
 | 			GetData()->nDataLength = nLength; | 
 | 		} | 
 |  | 
 | 		return nLength; | 
 | 	} | 
 |  | 
 | 	// searching (return starting index, or -1 if not found) | 
 | 	// look for a single character match | 
 | 	int Find(TCHAR ch) const   // like "C" strchr | 
 | 	{ | 
 | 		return Find(ch, 0); | 
 | 	} | 
 |  | 
 | 	int ReverseFind(TCHAR ch) const | 
 | 	{ | 
 | 		// find last single character | 
 | 		LPCTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch); | 
 |  | 
 | 		// return -1 if not found, distance from beginning otherwise | 
 | 		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); | 
 | 	} | 
 |  | 
 | 	int Find(TCHAR ch, int nStart) const   // starting at index | 
 | 	{ | 
 | 		int nLength = GetData()->nDataLength; | 
 | 		if (nStart < 0 || nStart >= nLength) | 
 | 			return -1; | 
 |  | 
 | 		// find first single character | 
 | 		LPCTSTR lpsz = _cstrchr(m_pchData + nStart, (_TUCHAR)ch); | 
 |  | 
 | 		// return -1 if not found and index otherwise | 
 | 		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); | 
 | 	} | 
 |  | 
 | 	int FindOneOf(LPCTSTR lpszCharSet) const | 
 | 	{ | 
 | 		ATLASSERT(_IsValidString(lpszCharSet)); | 
 | 		LPCTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet); | 
 | 		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); | 
 | 	} | 
 |  | 
 | 	// look for a specific sub-string | 
 | 	// find a sub-string (like strstr) | 
 | 	int Find(LPCTSTR lpszSub) const   // like "C" strstr | 
 | 	{ | 
 | 		return Find(lpszSub, 0); | 
 | 	} | 
 |  | 
 | 	int Find(LPCTSTR lpszSub, int nStart) const   // starting at index | 
 | 	{ | 
 | 		ATLASSERT(_IsValidString(lpszSub)); | 
 |  | 
 | 		int nLength = GetData()->nDataLength; | 
 | 		if (nStart < 0 || nStart > nLength) | 
 | 			return -1; | 
 |  | 
 | 		// find first matching substring | 
 | 		LPCTSTR lpsz = _cstrstr(m_pchData + nStart, lpszSub); | 
 |  | 
 | 		// return -1 for not found, distance from beginning otherwise | 
 | 		return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); | 
 | 	} | 
 |  | 
 | 	// Concatentation for non strings | 
 | 	CString& Append(int n) | 
 | 	{ | 
 | 		const int cchBuff = 12; | 
 | 		TCHAR szBuffer[cchBuff] = { 0 }; | 
 | 		SecureHelper::wsprintf_x(szBuffer, cchBuff, _T("%d"), n); | 
 | 		ConcatInPlace(SafeStrlen(szBuffer), szBuffer); | 
 | 		return *this; | 
 | 	} | 
 |  | 
 | 	// simple formatting | 
 | 	// formatting (using wsprintf style formatting) | 
 | 	BOOL __cdecl Format(LPCTSTR lpszFormat, ...) | 
 | 	{ | 
 | 		ATLASSERT(_IsValidString(lpszFormat)); | 
 |  | 
 | 		va_list argList; | 
 | 		va_start(argList, lpszFormat); | 
 | 		BOOL bRet = FormatV(lpszFormat, argList); | 
 | 		va_end(argList); | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	BOOL __cdecl Format(UINT nFormatID, ...) | 
 | 	{ | 
 | 		CString strFormat; | 
 | 		BOOL bRet = strFormat.LoadString(nFormatID); | 
 | 		ATLASSERT(bRet != 0); | 
 |  | 
 | 		va_list argList; | 
 | 		va_start(argList, nFormatID); | 
 | 		bRet = FormatV(strFormat, argList); | 
 | 		va_end(argList); | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	BOOL FormatV(LPCTSTR lpszFormat, va_list argList) | 
 | 	{ | 
 | 		ATLASSERT(_IsValidString(lpszFormat)); | 
 |  | 
 | 		enum _FormatModifiers | 
 | 		{ | 
 | 			FORCE_ANSI =	0x10000, | 
 | 			FORCE_UNICODE =	0x20000, | 
 | 			FORCE_INT64 =	0x40000 | 
 | 		}; | 
 |  | 
 | 		va_list argListSave = argList; | 
 |  | 
 | 		// make a guess at the maximum length of the resulting string | 
 | 		int nMaxLen = 0; | 
 | 		for (LPCTSTR lpsz = lpszFormat; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz)) | 
 | 		{ | 
 | 			// handle '%' character, but watch out for '%%' | 
 | 			if (*lpsz != _T('%') || *(lpsz = ::CharNext(lpsz)) == _T('%')) | 
 | 			{ | 
 | 				nMaxLen += (int)(::CharNext(lpsz) - lpsz); | 
 | 				continue; | 
 | 			} | 
 |  | 
 | 			int nItemLen = 0; | 
 |  | 
 | 			// handle '%' character with format | 
 | 			int nWidth = 0; | 
 | 			for (; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz)) | 
 | 			{ | 
 | 				// check for valid flags | 
 | 				if (*lpsz == _T('#')) | 
 | 					nMaxLen += 2;   // for '0x' | 
 | 				else if (*lpsz == _T('*')) | 
 | 					nWidth = va_arg(argList, int); | 
 | 				else if (*lpsz == _T('-') || *lpsz == _T('+') || *lpsz == _T('0') || *lpsz == _T(' ')) | 
 | 					; | 
 | 				else // hit non-flag character | 
 | 					break; | 
 | 			} | 
 | 			// get width and skip it | 
 | 			if (nWidth == 0) | 
 | 			{ | 
 | 				// width indicated by | 
 | 				nWidth = _cstrtoi(lpsz); | 
 | 				for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz)) | 
 | 					; | 
 | 			} | 
 | 			ATLASSERT(nWidth >= 0); | 
 |  | 
 | 			int nPrecision = 0; | 
 | 			if (*lpsz == _T('.')) | 
 | 			{ | 
 | 				// skip past '.' separator (width.precision) | 
 | 				lpsz = ::CharNext(lpsz); | 
 |  | 
 | 				// get precision and skip it | 
 | 				if (*lpsz == _T('*')) | 
 | 				{ | 
 | 					nPrecision = va_arg(argList, int); | 
 | 					lpsz = ::CharNext(lpsz); | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					nPrecision = _cstrtoi(lpsz); | 
 | 					for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz)) | 
 | 						; | 
 | 				} | 
 | 				ATLASSERT(nPrecision >= 0); | 
 | 			} | 
 |  | 
 | 			// should be on type modifier or specifier | 
 | 			int nModifier = 0; | 
 | 			if(lpsz[0] == _T('I') && lpsz[1] == _T('6') && lpsz[2] == _T('4')) | 
 | 			{ | 
 | 				lpsz += 3; | 
 | 				nModifier = FORCE_INT64; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				switch (*lpsz) | 
 | 				{ | 
 | 				// modifiers that affect size | 
 | 				case _T('h'): | 
 | 					nModifier = FORCE_ANSI; | 
 | 					lpsz = ::CharNext(lpsz); | 
 | 					break; | 
 | 				case _T('l'): | 
 | 					nModifier = FORCE_UNICODE; | 
 | 					lpsz = ::CharNext(lpsz); | 
 | 					break; | 
 |  | 
 | 				// modifiers that do not affect size | 
 | 				case _T('F'): | 
 | 				case _T('N'): | 
 | 				case _T('L'): | 
 | 					lpsz = ::CharNext(lpsz); | 
 | 					break; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			// now should be on specifier | 
 | 			switch (*lpsz | nModifier) | 
 | 			{ | 
 | 			// single characters | 
 | 			case _T('c'): | 
 | 			case _T('C'): | 
 | 				nItemLen = 2; | 
 | 				va_arg(argList, TCHAR); | 
 | 				break; | 
 | 			case _T('c') | FORCE_ANSI: | 
 | 			case _T('C') | FORCE_ANSI: | 
 | 				nItemLen = 2; | 
 | 				va_arg(argList, char); | 
 | 				break; | 
 | 			case _T('c') | FORCE_UNICODE: | 
 | 			case _T('C') | FORCE_UNICODE: | 
 | 				nItemLen = 2; | 
 | 				va_arg(argList, WCHAR); | 
 | 				break; | 
 |  | 
 | 			// strings | 
 | 			case _T('s'): | 
 | 			{ | 
 | 				LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR); | 
 | 				if (pstrNextArg == NULL) | 
 | 				{ | 
 | 					nItemLen = 6;  // "(null)" | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					nItemLen = lstrlen(pstrNextArg); | 
 | 					nItemLen = __max(1, nItemLen); | 
 | 				} | 
 | 				break; | 
 | 			} | 
 |  | 
 | 			case _T('S'): | 
 | 			{ | 
 | #ifndef _UNICODE | 
 | 				LPWSTR pstrNextArg = va_arg(argList, LPWSTR); | 
 | 				if (pstrNextArg == NULL) | 
 | 				{ | 
 | 					nItemLen = 6;  // "(null)" | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					nItemLen = (int)wcslen(pstrNextArg); | 
 | 					nItemLen = __max(1, nItemLen); | 
 | 				} | 
 | #else // _UNICODE | 
 | 				LPCSTR pstrNextArg = va_arg(argList, LPCSTR); | 
 | 				if (pstrNextArg == NULL) | 
 | 				{ | 
 | 					nItemLen = 6; // "(null)" | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) | 
 | 					nItemLen = ATL::lstrlenA(pstrNextArg); | 
 | #else | 
 | 					nItemLen = lstrlenA(pstrNextArg); | 
 | #endif | 
 | 					nItemLen = __max(1, nItemLen); | 
 | 				} | 
 | #endif // _UNICODE | 
 | 				break; | 
 | 			} | 
 |  | 
 | 			case _T('s') | FORCE_ANSI: | 
 | 			case _T('S') | FORCE_ANSI: | 
 | 			{ | 
 | 				LPCSTR pstrNextArg = va_arg(argList, LPCSTR); | 
 | 				if (pstrNextArg == NULL) | 
 | 				{ | 
 | 					nItemLen = 6; // "(null)" | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | #if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800) | 
 | 					nItemLen = ATL::lstrlenA(pstrNextArg); | 
 | #else | 
 | 					nItemLen = lstrlenA(pstrNextArg); | 
 | #endif | 
 | 					nItemLen = __max(1, nItemLen); | 
 | 				} | 
 | 				break; | 
 | 			} | 
 |  | 
 | 			case _T('s') | FORCE_UNICODE: | 
 | 			case _T('S') | FORCE_UNICODE: | 
 | 			{ | 
 | 				LPWSTR pstrNextArg = va_arg(argList, LPWSTR); | 
 | 				if (pstrNextArg == NULL) | 
 | 				{ | 
 | 					nItemLen = 6; // "(null)" | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					nItemLen = (int)wcslen(pstrNextArg); | 
 | 					nItemLen = __max(1, nItemLen); | 
 | 				} | 
 | 				break; | 
 | 			} | 
 | 			} | 
 |  | 
 | 			// adjust nItemLen for strings | 
 | 			if (nItemLen != 0) | 
 | 			{ | 
 | 				nItemLen = __max(nItemLen, nWidth); | 
 | 				if (nPrecision != 0) | 
 | 					nItemLen = __min(nItemLen, nPrecision); | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				switch (*lpsz) | 
 | 				{ | 
 | 				// integers | 
 | 				case _T('d'): | 
 | 				case _T('i'): | 
 | 				case _T('u'): | 
 | 				case _T('x'): | 
 | 				case _T('X'): | 
 | 				case _T('o'): | 
 | 					if (nModifier & FORCE_INT64) | 
 | 						va_arg(argList, __int64); | 
 | 					else | 
 | 						va_arg(argList, int); | 
 | 					nItemLen = 32; | 
 | 					nItemLen = __max(nItemLen, nWidth + nPrecision); | 
 | 					break; | 
 |  | 
 | #ifndef _ATL_USE_CSTRING_FLOAT | 
 | 				case _T('e'): | 
 | 				case _T('E'): | 
 | 				case _T('f'): | 
 | 				case _T('g'): | 
 | 				case _T('G'): | 
 | 					ATLASSERT(!"Floating point (%%e, %%E, %%f, %%g, and %%G) is not supported by the WTL::CString class."); | 
 | #ifndef _DEBUG | 
 | 					::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class.")); | 
 | #ifndef _WIN32_WCE | 
 | 					::DebugBreak(); | 
 | #else // CE specific | 
 | 					DebugBreak(); | 
 | #endif // _WIN32_WCE | 
 | #endif // !_DEBUG | 
 | 					break; | 
 | #else // _ATL_USE_CSTRING_FLOAT | 
 | 				case _T('e'): | 
 | 				case _T('E'): | 
 | 				case _T('g'): | 
 | 				case _T('G'): | 
 | 					va_arg(argList, double); | 
 | 					nItemLen = 128; | 
 | 					nItemLen = __max(nItemLen, nWidth + nPrecision); | 
 | 					break; | 
 | 				case _T('f'): | 
 | 					{ | 
 | 						double f = va_arg(argList, double); | 
 | 						// 312 == strlen("-1+(309 zeroes).") | 
 | 						// 309 zeroes == max precision of a double | 
 | 						// 6 == adjustment in case precision is not specified, | 
 | 						//   which means that the precision defaults to 6 | 
 | 						int cchLen = __max(nWidth, 312 + nPrecision + 6); | 
 | 						CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff; | 
 | 						LPTSTR pszTemp = buff.Allocate(cchLen); | 
 | 						if(pszTemp != NULL) | 
 | 						{ | 
 | 							SecureHelper::sprintf_x(pszTemp, cchLen, _T("%*.*f"), nWidth, nPrecision + 6, f); | 
 | 							nItemLen = (int)_tcslen(pszTemp); | 
 | 						} | 
 | 						else | 
 | 						{ | 
 | 							nItemLen = cchLen; | 
 | 						} | 
 | 					} | 
 | 					break; | 
 | #endif // _ATL_USE_CSTRING_FLOAT | 
 |  | 
 | 				case _T('p'): | 
 | 					va_arg(argList, void*); | 
 | 					nItemLen = 32; | 
 | 					nItemLen = __max(nItemLen, nWidth + nPrecision); | 
 | 					break; | 
 |  | 
 | 				// no output | 
 | 				case _T('n'): | 
 | 					va_arg(argList, int*); | 
 | 					break; | 
 |  | 
 | 				default: | 
 | 					ATLASSERT(FALSE);  // unknown formatting option | 
 | 				} | 
 | 			} | 
 |  | 
 | 			// adjust nMaxLen for output nItemLen | 
 | 			nMaxLen += nItemLen; | 
 | 		} | 
 |  | 
 | 		if(GetBuffer(nMaxLen) == NULL) | 
 | 			return FALSE; | 
 | #ifndef _ATL_USE_CSTRING_FLOAT | 
 | 		int nRet = SecureHelper::wvsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave); | 
 | #else // _ATL_USE_CSTRING_FLOAT | 
 | 		int nRet = SecureHelper::vsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave); | 
 | #endif // _ATL_USE_CSTRING_FLOAT | 
 | 		nRet;   // ref | 
 | 		ATLASSERT(nRet <= GetAllocLength()); | 
 | 		ReleaseBuffer(); | 
 |  | 
 | 		va_end(argListSave); | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | 	// formatting for localization (uses FormatMessage API) | 
 | 	// formatting (using FormatMessage style formatting) | 
 | 	BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...) | 
 | 	{ | 
 | 		// format message into temporary buffer lpszTemp | 
 | 		va_list argList; | 
 | 		va_start(argList, lpszFormat); | 
 | 		LPTSTR lpszTemp; | 
 | 		BOOL bRet = TRUE; | 
 |  | 
 | 		if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, | 
 | 				lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL) | 
 | 			bRet = FALSE; | 
 |  | 
 | 		// assign lpszTemp into the resulting string and free the temporary | 
 | 		*this = lpszTemp; | 
 | 		LocalFree(lpszTemp); | 
 | 		va_end(argList); | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	BOOL __cdecl FormatMessage(UINT nFormatID, ...) | 
 | 	{ | 
 | 		// get format string from string table | 
 | 		CString strFormat; | 
 | 		BOOL bRetTmp = strFormat.LoadString(nFormatID); | 
 | 		bRetTmp;   // ref | 
 | 		ATLASSERT(bRetTmp != 0); | 
 |  | 
 | 		// format message into temporary buffer lpszTemp | 
 | 		va_list argList; | 
 | 		va_start(argList, nFormatID); | 
 | 		LPTSTR lpszTemp; | 
 | 		BOOL bRet = TRUE; | 
 |  | 
 | 		if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, | 
 | 				strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL) | 
 | 			bRet = FALSE; | 
 |  | 
 | 		// assign lpszTemp into the resulting string and free lpszTemp | 
 | 		*this = lpszTemp; | 
 | 		LocalFree(lpszTemp); | 
 | 		va_end(argList); | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	// Windows support | 
 | 	BOOL LoadString(UINT nID)   // load from string resource (255 chars max.) | 
 | 	{ | 
 | #ifdef _UNICODE | 
 | 		const int CHAR_FUDGE = 1;   // one TCHAR unused is good enough | 
 | #else | 
 | 		const int CHAR_FUDGE = 2;   // two BYTES unused for case of DBC last char | 
 | #endif | 
 |  | 
 | 		// try fixed buffer first (to avoid wasting space in the heap) | 
 | 		TCHAR szTemp[256]; | 
 | 		int nCount =  sizeof(szTemp) / sizeof(szTemp[0]); | 
 | 		int nLen = _LoadString(nID, szTemp, nCount); | 
 | 		if (nCount - nLen > CHAR_FUDGE) | 
 | 		{ | 
 | 			*this = szTemp; | 
 | 			return (nLen > 0); | 
 | 		} | 
 |  | 
 | 		// try buffer size of 512, then larger size until entire string is retrieved | 
 | 		int nSize = 256; | 
 | 		do | 
 | 		{ | 
 | 			nSize += 256; | 
 | 			LPTSTR lpstr = GetBuffer(nSize - 1); | 
 | 			if(lpstr == NULL) | 
 | 			{ | 
 | 				nLen = 0; | 
 | 				break; | 
 | 			} | 
 | 			nLen = _LoadString(nID, lpstr, nSize); | 
 | 		} while (nSize - nLen <= CHAR_FUDGE); | 
 | 		ReleaseBuffer(); | 
 |  | 
 | 		return (nLen > 0); | 
 | 	} | 
 |  | 
 | #ifndef _UNICODE | 
 | 	// ANSI <-> OEM support (convert string in place) | 
 | 	void AnsiToOem() | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 | 		::AnsiToOem(m_pchData, m_pchData); | 
 | 	} | 
 |  | 
 | 	void OemToAnsi() | 
 | 	{ | 
 | 		CopyBeforeWrite(); | 
 | 		::OemToAnsi(m_pchData, m_pchData); | 
 | 	} | 
 | #endif | 
 |  | 
 | #ifndef _ATL_NO_COM | 
 | 	// OLE BSTR support (use for OLE automation) | 
 | 	BSTR AllocSysString() const | 
 | 	{ | 
 | #if defined(_UNICODE) || defined(OLE2ANSI) | 
 | 		BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength); | 
 | #else | 
 | 		int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData, | 
 | 			GetData()->nDataLength, NULL, NULL); | 
 | 		BSTR bstr = ::SysAllocStringLen(NULL, nLen); | 
 | 		if(bstr != NULL) | 
 | 			MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen); | 
 | #endif | 
 | 		return bstr; | 
 | 	} | 
 |  | 
 | 	BSTR SetSysString(BSTR* pbstr) const | 
 | 	{ | 
 | #if defined(_UNICODE) || defined(OLE2ANSI) | 
 | 		::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength); | 
 | #else | 
 | 		int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData, | 
 | 			GetData()->nDataLength, NULL, NULL); | 
 | 		if(::SysReAllocStringLen(pbstr, NULL, nLen)) | 
 | 			MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen); | 
 | #endif | 
 | 		ATLASSERT(*pbstr != NULL); | 
 | 		return *pbstr; | 
 | 	} | 
 | #endif // !_ATL_NO_COM | 
 |  | 
 | 	// Access to string implementation buffer as "C" character array | 
 | 	LPTSTR GetBuffer(int nMinBufLength) | 
 | 	{ | 
 | 		ATLASSERT(nMinBufLength >= 0); | 
 |  | 
 | 		if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength) | 
 | 		{ | 
 | 			// we have to grow the buffer | 
 | 			CStringData* pOldData = GetData(); | 
 | 			int nOldLen = GetData()->nDataLength;   // AllocBuffer will tromp it | 
 | 			if (nMinBufLength < nOldLen) | 
 | 				nMinBufLength = nOldLen; | 
 |  | 
 | 			if(!AllocBuffer(nMinBufLength)) | 
 | 				return NULL; | 
 |  | 
 | 			SecureHelper::memcpy_x(m_pchData, (nMinBufLength + 1) * sizeof(TCHAR), pOldData->data(), (nOldLen + 1) * sizeof(TCHAR)); | 
 | 			GetData()->nDataLength = nOldLen; | 
 | 			CString::Release(pOldData); | 
 | 		} | 
 | 		ATLASSERT(GetData()->nRefs <= 1); | 
 |  | 
 | 		// return a pointer to the character storage for this string | 
 | 		ATLASSERT(m_pchData != NULL); | 
 | 		return m_pchData; | 
 | 	} | 
 |  | 
 | 	void ReleaseBuffer(int nNewLength = -1) | 
 | 	{ | 
 | 		CopyBeforeWrite();   // just in case GetBuffer was not called | 
 |  | 
 | 		if (nNewLength == -1) | 
 | 			nNewLength = lstrlen(m_pchData);   // zero terminated | 
 |  | 
 | 		ATLASSERT(nNewLength <= GetData()->nAllocLength); | 
 | 		GetData()->nDataLength = nNewLength; | 
 | 		m_pchData[nNewLength] = _T('\0'); | 
 | 	} | 
 |  | 
 | 	LPTSTR GetBufferSetLength(int nNewLength) | 
 | 	{ | 
 | 		ATLASSERT(nNewLength >= 0); | 
 |  | 
 | 		if(GetBuffer(nNewLength) == NULL) | 
 | 			return NULL; | 
 |  | 
 | 		GetData()->nDataLength = nNewLength; | 
 | 		m_pchData[nNewLength] = _T('\0'); | 
 | 		return m_pchData; | 
 | 	} | 
 |  | 
 | 	void FreeExtra() | 
 | 	{ | 
 | 		ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength); | 
 | 		if (GetData()->nDataLength != GetData()->nAllocLength) | 
 | 		{ | 
 | 			CStringData* pOldData = GetData(); | 
 | 			if(AllocBuffer(GetData()->nDataLength)) | 
 | 			{ | 
 | 				SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pOldData->data(), pOldData->nDataLength * sizeof(TCHAR)); | 
 | 				ATLASSERT(m_pchData[GetData()->nDataLength] == _T('\0')); | 
 | 				CString::Release(pOldData); | 
 | 			} | 
 | 		} | 
 | 		ATLASSERT(GetData() != NULL); | 
 | 	} | 
 |  | 
 | 	// Use LockBuffer/UnlockBuffer to turn refcounting off | 
 | 	LPTSTR LockBuffer() | 
 | 	{ | 
 | 		LPTSTR lpsz = GetBuffer(0); | 
 | 		if(lpsz != NULL) | 
 | 			GetData()->nRefs = -1; | 
 | 		return lpsz; | 
 | 	} | 
 |  | 
 | 	void UnlockBuffer() | 
 | 	{ | 
 | 		ATLASSERT(GetData()->nRefs == -1); | 
 | 		if (GetData() != _atltmpDataNil) | 
 | 			GetData()->nRefs = 1; | 
 | 	} | 
 |  | 
 | // Implementation | 
 | public: | 
 | 	~CString()   //  free any attached data | 
 | 	{ | 
 | 		if (GetData() != _atltmpDataNil) | 
 | 		{ | 
 | 			if (InterlockedDecrement(&GetData()->nRefs) <= 0) | 
 | 				delete[] (BYTE*)GetData(); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	int GetAllocLength() const | 
 | 	{ | 
 | 		return GetData()->nAllocLength; | 
 | 	} | 
 |  | 
 | 	static BOOL __stdcall _IsValidString(LPCTSTR lpsz, int /*nLength*/ = -1) | 
 | 	{ | 
 | 		return (lpsz != NULL) ? TRUE : FALSE; | 
 | 	} | 
 |  | 
 | protected: | 
 | 	LPTSTR m_pchData;   // pointer to ref counted string data | 
 |  | 
 | 	// implementation helpers | 
 | 	CStringData* GetData() const | 
 | 	{ | 
 | 		ATLASSERT(m_pchData != NULL); | 
 | 		return ((CStringData*)m_pchData) - 1; | 
 | 	} | 
 |  | 
 | 	void Init() | 
 | 	{ | 
 | 		m_pchData = _GetEmptyString().m_pchData; | 
 | 	} | 
 |  | 
 | 	BOOL AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const | 
 | 	{ | 
 | 		// will clone the data attached to this string | 
 | 		// allocating 'nExtraLen' characters | 
 | 		// Places results in uninitialized string 'dest' | 
 | 		// Will copy the part or all of original data to start of new string | 
 |  | 
 | 		BOOL bRet = FALSE; | 
 | 		int nNewLen = nCopyLen + nExtraLen; | 
 | 		if (nNewLen == 0) | 
 | 		{ | 
 | 			dest.Init(); | 
 | 			bRet = TRUE; | 
 | 		} | 
 | 		else if(nNewLen >= nCopyLen) | 
 | 		{ | 
 | 			if(dest.AllocBuffer(nNewLen)) | 
 | 			{ | 
 | 				SecureHelper::memcpy_x(dest.m_pchData, (nNewLen + 1) * sizeof(TCHAR), m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR)); | 
 | 				bRet = TRUE; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	// always allocate one extra character for '\0' termination | 
 | 	// assumes [optimistically] that data length will equal allocation length | 
 | 	BOOL AllocBuffer(int nLen) | 
 | 	{ | 
 | 		ATLASSERT(nLen >= 0); | 
 | 		ATLASSERT(nLen <= INT_MAX - 1);   // max size (enough room for 1 extra) | 
 |  | 
 | 		if (nLen == 0) | 
 | 		{ | 
 | 			Init(); | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			CStringData* pData = NULL; | 
 | 			ATLTRY(pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR)]); | 
 | 			if(pData == NULL) | 
 | 				return FALSE; | 
 |  | 
 | 			pData->nRefs = 1; | 
 | 			pData->data()[nLen] = _T('\0'); | 
 | 			pData->nDataLength = nLen; | 
 | 			pData->nAllocLength = nLen; | 
 | 			m_pchData = pData->data(); | 
 | 		} | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | 	// Assignment operators | 
 | 	//  All assign a new value to the string | 
 | 	//      (a) first see if the buffer is big enough | 
 | 	//      (b) if enough room, copy on top of old buffer, set size and type | 
 | 	//      (c) otherwise free old string data, and create a new one | 
 | 	// | 
 | 	//  All routines return the new string (but as a 'const CString&' so that | 
 | 	//      assigning it again will cause a copy, eg: s1 = s2 = "hi there". | 
 | 	// | 
 | 	void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData) | 
 | 	{ | 
 | 		if(AllocBeforeWrite(nSrcLen)) | 
 | 		{ | 
 | 			SecureHelper::memcpy_x(m_pchData, (nSrcLen + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR)); | 
 | 			GetData()->nDataLength = nSrcLen; | 
 | 			m_pchData[nSrcLen] = _T('\0'); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	// Concatenation | 
 | 	// NOTE: "operator +" is done as friend functions for simplicity | 
 | 	//      There are three variants: | 
 | 	//          CString + CString | 
 | 	// and for ? = TCHAR, LPCTSTR | 
 | 	//          CString + ? | 
 | 	//          ? + CString | 
 | 	BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data) | 
 | 	{ | 
 | 		// -- master concatenation routine | 
 | 		// Concatenate two sources | 
 | 		// -- assume that 'this' is a new CString object | 
 |  | 
 | 		BOOL bRet = TRUE; | 
 | 		int nNewLen = nSrc1Len + nSrc2Len; | 
 | 		if(nNewLen < nSrc1Len || nNewLen < nSrc2Len) | 
 | 		{ | 
 | 			bRet = FALSE; | 
 | 		} | 
 | 		else if(nNewLen != 0) | 
 | 		{ | 
 | 			bRet = AllocBuffer(nNewLen); | 
 | 			if (bRet) | 
 | 			{ | 
 | 				SecureHelper::memcpy_x(m_pchData, (nNewLen + 1) * sizeof(TCHAR), lpszSrc1Data, nSrc1Len * sizeof(TCHAR)); | 
 | 				SecureHelper::memcpy_x(m_pchData + nSrc1Len, (nNewLen + 1 - nSrc1Len) * sizeof(TCHAR), lpszSrc2Data, nSrc2Len * sizeof(TCHAR)); | 
 | 			} | 
 | 		} | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData) | 
 | 	{ | 
 | 		//  -- the main routine for += operators | 
 |  | 
 | 		// concatenating an empty string is a no-op! | 
 | 		if (nSrcLen == 0) | 
 | 			return; | 
 |  | 
 | 		// if the buffer is too small, or we have a width mis-match, just | 
 | 		//   allocate a new buffer (slow but sure) | 
 | 		if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength) | 
 | 		{ | 
 | 			// we have to grow the buffer, use the ConcatCopy routine | 
 | 			CStringData* pOldData = GetData(); | 
 | 			if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData)) | 
 | 			{ | 
 | 				ATLASSERT(pOldData != NULL); | 
 | 				CString::Release(pOldData); | 
 | 			} | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			// fast concatenation when buffer big enough | 
 | 			SecureHelper::memcpy_x(m_pchData + GetData()->nDataLength, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR)); | 
 | 			GetData()->nDataLength += nSrcLen; | 
 | 			ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength); | 
 | 			m_pchData[GetData()->nDataLength] = _T('\0'); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	void CopyBeforeWrite() | 
 | 	{ | 
 | 		if (GetData()->nRefs > 1) | 
 | 		{ | 
 | 			CStringData* pData = GetData(); | 
 | 			Release(); | 
 | 			if(AllocBuffer(pData->nDataLength)) | 
 | 				SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR)); | 
 | 		} | 
 | 		ATLASSERT(GetData()->nRefs <= 1); | 
 | 	} | 
 |  | 
 | 	BOOL AllocBeforeWrite(int nLen) | 
 | 	{ | 
 | 		BOOL bRet = TRUE; | 
 | 		if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength) | 
 | 		{ | 
 | 			Release(); | 
 | 			bRet = AllocBuffer(nLen); | 
 | 		} | 
 | 		ATLASSERT(GetData()->nRefs <= 1); | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	void Release() | 
 | 	{ | 
 | 		if (GetData() != _atltmpDataNil) | 
 | 		{ | 
 | 			ATLASSERT(GetData()->nRefs != 0); | 
 | 			if (InterlockedDecrement(&GetData()->nRefs) <= 0) | 
 | 				delete[] (BYTE*)GetData(); | 
 | 			Init(); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	static void PASCAL Release(CStringData* pData) | 
 | 	{ | 
 | 		if (pData != _atltmpDataNil) | 
 | 		{ | 
 | 			ATLASSERT(pData->nRefs != 0); | 
 | 			if (InterlockedDecrement(&pData->nRefs) <= 0) | 
 | 				delete[] (BYTE*)pData; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	static int PASCAL SafeStrlen(LPCTSTR lpsz) | 
 | 	{ | 
 | 		return (lpsz == NULL) ? 0 : lstrlen(lpsz); | 
 | 	} | 
 |  | 
 | 	static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf) | 
 | 	{ | 
 | #ifdef _DEBUG | 
 | 		// LoadString without annoying warning from the Debug kernel if the | 
 | 		//  segment containing the string is not present | 
 | 		if (::FindResource(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE((nID >> 4) + 1), RT_STRING) == NULL) | 
 | 		{ | 
 | 			lpszBuf[0] = _T('\0'); | 
 | 			return 0;   // not found | 
 | 		} | 
 | #endif // _DEBUG | 
 |  | 
 | 		int nLen = ::LoadString(ModuleHelper::GetResourceInstance(), nID, lpszBuf, nMaxBuf); | 
 | 		if (nLen == 0) | 
 | 			lpszBuf[0] = _T('\0'); | 
 |  | 
 | 		return nLen; | 
 | 	} | 
 |  | 
 | 	static const CString& __stdcall _GetEmptyString() | 
 | 	{ | 
 | 		return *(CString*)&_atltmpPchNil; | 
 | 	} | 
 |  | 
 | // CString conversion helpers | 
 | 	static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count) | 
 | 	{ | 
 | 		if (count == 0 && mbstr != NULL) | 
 | 			return 0; | 
 |  | 
 | 		int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL); | 
 | 		ATLASSERT(mbstr == NULL || result <= (int)count); | 
 | 		if (result > 0) | 
 | 			mbstr[result - 1] = 0; | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count) | 
 | 	{ | 
 | 		if (count == 0 && wcstr != NULL) | 
 | 			return 0; | 
 |  | 
 | 		int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count); | 
 | 		ATLASSERT(wcstr == NULL || result <= (int)count); | 
 | 		if (result > 0) | 
 | 			wcstr[result - 1] = 0; | 
 | 		return result; | 
 | 	} | 
 |  | 
 | // Helpers to avoid CRT startup code | 
 | #ifdef _ATL_MIN_CRT | 
 | 	static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch) | 
 | 	{ | 
 | 		// strchr for '\0' should succeed | 
 | 		while (*p != 0) | 
 | 		{ | 
 | 			if (*p == ch) | 
 | 				break; | 
 | 			p = ::CharNext(p); | 
 | 		} | 
 | 		return (*p == ch) ? p : NULL; | 
 | 	} | 
 |  | 
 | 	static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch) | 
 | 	{ | 
 | 		const TCHAR* lpsz = NULL; | 
 | 		while (*p != 0) | 
 | 		{ | 
 | 			if (*p == ch) | 
 | 				lpsz = p; | 
 | 			p = ::CharNext(p); | 
 | 		} | 
 | 		return lpsz; | 
 | 	} | 
 |  | 
 | 	static TCHAR* _cstrrev(TCHAR* pStr) | 
 | 	{ | 
 | 		// optimize NULL, zero-length, and single-char case | 
 | 		if ((pStr == NULL) || (pStr[0] == _T('\0')) || (pStr[1] == _T('\0'))) | 
 | 			return pStr; | 
 |  | 
 | 		TCHAR* p = pStr; | 
 |  | 
 | 		while (*p != 0)  | 
 | 		{ | 
 | 			TCHAR* pNext = ::CharNext(p); | 
 | 			if(pNext > p + 1) | 
 | 			{ | 
 | 				char p1 = *(char*)p; | 
 | 				*(char*)p = *(char*)(p + 1); | 
 | 				*(char*)(p + 1) = p1; | 
 | 			} | 
 | 			p = pNext; | 
 | 		} | 
 |  | 
 | 		p--; | 
 | 		TCHAR* q = pStr; | 
 |  | 
 | 		while (q < p) | 
 | 		{ | 
 | 			TCHAR t = *q; | 
 | 			*q = *p; | 
 | 			*p = t; | 
 | 			q++; | 
 | 			p--; | 
 | 		} | 
 | 		return pStr; | 
 | 	} | 
 |  | 
 | 	static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet) | 
 | 	{ | 
 | 		int nLen = lstrlen(pCharSet); | 
 | 		if (nLen == 0) | 
 | 			return (TCHAR*)pStr; | 
 |  | 
 | 		const TCHAR* pRet = NULL; | 
 | 		const TCHAR* pCur = pStr; | 
 | 		while((pCur = _cstrchr(pCur, *pCharSet)) != NULL) | 
 | 		{ | 
 | 			if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0) | 
 | 			{ | 
 | 				pRet = pCur; | 
 | 				break; | 
 | 			} | 
 | 			pCur = ::CharNext(pCur); | 
 | 		} | 
 | 		return pRet; | 
 | 	} | 
 |  | 
 | 	static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet) | 
 | 	{ | 
 | 		int nRet = 0; | 
 | 		const TCHAR* p = pStr; | 
 | 		while (*p != 0) | 
 | 		{ | 
 | 			const TCHAR* pNext = ::CharNext(p); | 
 | 			if(pNext > p + 1) | 
 | 			{ | 
 | 				if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL) | 
 | 					break; | 
 | 				nRet += 2; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				if(_cstrchr(pCharSet, *p) == NULL) | 
 | 					break; | 
 | 				nRet++; | 
 | 			} | 
 | 			p = pNext; | 
 | 		} | 
 | 		return nRet; | 
 | 	} | 
 |  | 
 | 	static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet) | 
 | 	{ | 
 | 		int nRet = 0; | 
 | 		TCHAR* p = (TCHAR*)pStr; | 
 | 		while (*p != 0) | 
 | 		{ | 
 | 			TCHAR* pNext = ::CharNext(p); | 
 | 			if(pNext > p + 1) | 
 | 			{ | 
 | 				if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL) | 
 | 					break; | 
 | 				nRet += 2; | 
 | 			} | 
 | 			else | 
 | 			{ | 
 | 				if(_cstrchr(pCharSet, *p) != NULL) | 
 | 					break; | 
 | 				nRet++; | 
 | 			} | 
 | 			p = pNext; | 
 | 		} | 
 | 		return nRet; | 
 | 	} | 
 |  | 
 | 	static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet) | 
 | 	{ | 
 | 		int n = _cstrcspn(p, lpszCharSet); | 
 | 		return (p[n] != 0) ? &p[n] : NULL; | 
 | 	} | 
 |  | 
 | 	static int _cstrisdigit(TCHAR ch) | 
 | 	{ | 
 | 		WORD type; | 
 | 		GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type); | 
 | 		return (type & C1_DIGIT) == C1_DIGIT; | 
 | 	} | 
 |  | 
 | 	static int _cstrisspace(TCHAR ch) | 
 | 	{ | 
 | 		WORD type; | 
 | 		GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type); | 
 | 		return (type & C1_SPACE) == C1_SPACE; | 
 | 	} | 
 |  | 
 | 	static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		return lstrcmp(pstrOne, pstrOther); | 
 | 	} | 
 |  | 
 | 	static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		return lstrcmpi(pstrOne, pstrOther); | 
 | 	} | 
 |  | 
 | 	static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1); | 
 | 		ATLASSERT(nRet != 0); | 
 | 		return nRet - 2;   // convert to strcmp convention | 
 | 	} | 
 |  | 
 | 	static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1); | 
 | 		ATLASSERT(nRet != 0); | 
 | 		return nRet - 2;   // convert to strcmp convention | 
 | 	} | 
 |  | 
 | 	static int _cstrtoi(const TCHAR* nptr) | 
 | 	{ | 
 | 		int c;       // current char | 
 | 		int total;   // current total | 
 | 		int sign;    // if '-', then negative, otherwise positive | 
 |  | 
 | 		while (_cstrisspace(*nptr)) | 
 | 			++nptr; | 
 |  | 
 | 		c = (int)(_TUCHAR)*nptr++; | 
 | 		sign = c;   // save sign indication | 
 | 		if (c == _T('-') || c == _T('+')) | 
 | 			c = (int)(_TUCHAR)*nptr++;   // skip sign | 
 |  | 
 | 		total = 0; | 
 |  | 
 | 		while (_cstrisdigit((TCHAR)c)) | 
 | 		{ | 
 | 			total = 10 * total + (c - '0');   // accumulate digit | 
 | 			c = (int)(_TUCHAR)*nptr++;        // get next char | 
 | 		} | 
 |  | 
 | 		if (sign == '-') | 
 | 			return -total; | 
 | 		else | 
 | 			return total;   // return result, negated if necessary | 
 | 	} | 
 | #else // !_ATL_MIN_CRT | 
 | 	static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch) | 
 | 	{ | 
 | 		return _tcschr(p, ch); | 
 | 	} | 
 |  | 
 | 	static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch) | 
 | 	{ | 
 | 		return _tcsrchr(p, ch); | 
 | 	} | 
 |  | 
 | 	static TCHAR* _cstrrev(TCHAR* pStr) | 
 | 	{ | 
 | 		return _tcsrev(pStr); | 
 | 	} | 
 |  | 
 | 	static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet) | 
 | 	{ | 
 | 		return _tcsstr(pStr, pCharSet); | 
 | 	} | 
 |  | 
 | 	static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet) | 
 | 	{ | 
 | 		return (int)_tcsspn(pStr, pCharSet); | 
 | 	} | 
 |  | 
 | 	static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet) | 
 | 	{ | 
 | 		return (int)_tcscspn(pStr, pCharSet); | 
 | 	} | 
 |  | 
 | 	static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet) | 
 | 	{ | 
 | 		return _tcspbrk(p, lpszCharSet); | 
 | 	} | 
 |  | 
 | 	static int _cstrisdigit(TCHAR ch) | 
 | 	{ | 
 | 		return _istdigit(ch); | 
 | 	} | 
 |  | 
 | 	static int _cstrisspace(TCHAR ch) | 
 | 	{ | 
 | 		return _istspace((_TUCHAR)ch); | 
 | 	} | 
 |  | 
 | 	static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		return _tcscmp(pstrOne, pstrOther); | 
 | 	} | 
 |  | 
 | 	static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		return _tcsicmp(pstrOne, pstrOther); | 
 | 	} | 
 |  | 
 | #ifndef _WIN32_WCE | 
 | 	static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		return _tcscoll(pstrOne, pstrOther); | 
 | 	} | 
 |  | 
 | 	static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther) | 
 | 	{ | 
 | 		return _tcsicoll(pstrOne, pstrOther); | 
 | 	} | 
 | #endif // !_WIN32_WCE | 
 |  | 
 | 	static int _cstrtoi(const TCHAR* nptr) | 
 | 	{ | 
 | 		return _ttoi(nptr); | 
 | 	} | 
 | #endif // !_ATL_MIN_CRT | 
 |  | 
 | 	static const TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2) | 
 | 	{ | 
 | 		const TCHAR* lpsz = NULL; | 
 | 		while (*p != 0) | 
 | 		{ | 
 | 			if (*p == ch1 && *(p + 1) == ch2) | 
 | 			{ | 
 | 				lpsz = p; | 
 | 				break; | 
 | 			} | 
 | 			p = ::CharNext(p); | 
 | 		} | 
 | 		return lpsz; | 
 | 	} | 
 | }; | 
 |  | 
 |  | 
 | // Compare helpers | 
 |  | 
 | inline bool __stdcall operator ==(const CString& s1, const CString& s2) | 
 | { return s1.Compare(s2) == 0; } | 
 |  | 
 | inline bool __stdcall operator ==(const CString& s1, LPCTSTR s2) | 
 | { return s1.Compare(s2) == 0; } | 
 |  | 
 | inline bool __stdcall operator ==(LPCTSTR s1, const CString& s2) | 
 | { return s2.Compare(s1) == 0; } | 
 |  | 
 | inline bool __stdcall operator !=(const CString& s1, const CString& s2) | 
 | { return s1.Compare(s2) != 0; } | 
 |  | 
 | inline bool __stdcall operator !=(const CString& s1, LPCTSTR s2) | 
 | { return s1.Compare(s2) != 0; } | 
 |  | 
 | inline bool __stdcall operator !=(LPCTSTR s1, const CString& s2) | 
 | { return s2.Compare(s1) != 0; } | 
 |  | 
 | inline bool __stdcall operator <(const CString& s1, const CString& s2) | 
 | { return s1.Compare(s2) < 0; } | 
 |  | 
 | inline bool __stdcall operator <(const CString& s1, LPCTSTR s2) | 
 | { return s1.Compare(s2) < 0; } | 
 |  | 
 | inline bool __stdcall operator <(LPCTSTR s1, const CString& s2) | 
 | { return s2.Compare(s1) > 0; } | 
 |  | 
 | inline bool __stdcall operator >(const CString& s1, const CString& s2) | 
 | { return s1.Compare(s2) > 0; } | 
 |  | 
 | inline bool __stdcall operator >(const CString& s1, LPCTSTR s2) | 
 | { return s1.Compare(s2) > 0; } | 
 |  | 
 | inline bool __stdcall operator >(LPCTSTR s1, const CString& s2) | 
 | { return s2.Compare(s1) < 0; } | 
 |  | 
 | inline bool __stdcall operator <=(const CString& s1, const CString& s2) | 
 | { return s1.Compare(s2) <= 0; } | 
 |  | 
 | inline bool __stdcall operator <=(const CString& s1, LPCTSTR s2) | 
 | { return s1.Compare(s2) <= 0; } | 
 |  | 
 | inline bool __stdcall operator <=(LPCTSTR s1, const CString& s2) | 
 | { return s2.Compare(s1) >= 0; } | 
 |  | 
 | inline bool __stdcall operator >=(const CString& s1, const CString& s2) | 
 | { return s1.Compare(s2) >= 0; } | 
 |  | 
 | inline bool __stdcall operator >=(const CString& s1, LPCTSTR s2) | 
 | { return s1.Compare(s2) >= 0; } | 
 |  | 
 | inline bool __stdcall operator >=(LPCTSTR s1, const CString& s2) | 
 | { return s2.Compare(s1) <= 0; } | 
 |  | 
 |  | 
 | // CString "operator +" functions | 
 |  | 
 | inline CString __stdcall operator +(const CString& string1, const CString& string2) | 
 | { | 
 | 	CString s; | 
 | 	s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData); | 
 | 	return s; | 
 | } | 
 |  | 
 | inline CString __stdcall operator +(const CString& string, TCHAR ch) | 
 | { | 
 | 	CString s; | 
 | 	s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, 1, &ch); | 
 | 	return s; | 
 | } | 
 |  | 
 | inline CString __stdcall operator +(TCHAR ch, const CString& string) | 
 | { | 
 | 	CString s; | 
 | 	s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData); | 
 | 	return s; | 
 | } | 
 |  | 
 | #ifdef _UNICODE | 
 | inline CString __stdcall operator +(const CString& string, char ch) | 
 | { | 
 | 	return string + (TCHAR)ch; | 
 | } | 
 |  | 
 | inline CString __stdcall operator +(char ch, const CString& string) | 
 | { | 
 | 	return (TCHAR)ch + string; | 
 | } | 
 | #endif // _UNICODE | 
 |  | 
 | inline CString __stdcall operator +(const CString& string, LPCTSTR lpsz) | 
 | { | 
 | 	ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz)); | 
 | 	CString s; | 
 | 	s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz); | 
 | 	return s; | 
 | } | 
 |  | 
 | inline CString __stdcall operator +(LPCTSTR lpsz, const CString& string) | 
 | { | 
 | 	ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz)); | 
 | 	CString s; | 
 | 	s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData); | 
 | 	return s; | 
 | } | 
 |  | 
 | #endif // !_WTL_NO_CSTRING | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // CRecentDocumentList - MRU List Support | 
 |  | 
 | #ifndef _WIN32_WCE | 
 |  | 
 | #ifndef _WTL_MRUEMPTY_TEXT | 
 |   #define _WTL_MRUEMPTY_TEXT	_T("(empty)") | 
 | #endif | 
 |  | 
 | // forward declaration | 
 | inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen); | 
 |  | 
 | template <class T, int t_cchItemLen = MAX_PATH, int t_nFirstID = ID_FILE_MRU_FIRST, int t_nLastID = ID_FILE_MRU_LAST> | 
 | class CRecentDocumentListBase | 
 | { | 
 | public: | 
 | // Declarations | 
 | 	struct _DocEntry | 
 | 	{ | 
 | 		TCHAR szDocName[t_cchItemLen]; | 
 | 		bool operator ==(const _DocEntry& de) const | 
 | 		{ return (lstrcmpi(szDocName, de.szDocName) == 0); } | 
 | 	}; | 
 |  | 
 | 	enum | 
 | 	{ | 
 | 		m_nMaxEntries_Min = 2, | 
 | 		m_nMaxEntries_Max = t_nLastID - t_nFirstID + 1, | 
 | 		m_cchMaxItemLen_Min = 6, | 
 | 		m_cchMaxItemLen_Max = t_cchItemLen, | 
 | 		m_cchItemNameLen = 11 | 
 | 	}; | 
 |  | 
 | // Data members | 
 | 	ATL::CSimpleArray<_DocEntry> m_arrDocs; | 
 | 	int m_nMaxEntries;   // default is 4 | 
 | 	HMENU m_hMenu; | 
 |  | 
 | 	TCHAR m_szNoEntries[t_cchItemLen]; | 
 |  | 
 | 	int m_cchMaxItemLen; | 
 |  | 
 | // Constructor | 
 | 	CRecentDocumentListBase() : m_hMenu(NULL), m_nMaxEntries(4), m_cchMaxItemLen(-1) | 
 | 	{ | 
 | 		// These ASSERTs verify values of the template arguments | 
 | 		ATLASSERT(t_cchItemLen > m_cchMaxItemLen_Min); | 
 | 		ATLASSERT(m_nMaxEntries_Max > m_nMaxEntries_Min); | 
 | 	} | 
 |  | 
 | // Attributes | 
 | 	HMENU GetMenuHandle() const | 
 | 	{ | 
 | 		return m_hMenu; | 
 | 	} | 
 |  | 
 | 	void SetMenuHandle(HMENU hMenu) | 
 | 	{ | 
 | 		ATLASSERT(hMenu == NULL || ::IsMenu(hMenu)); | 
 | 		m_hMenu = hMenu; | 
 | 		if(m_hMenu == NULL || (::GetMenuString(m_hMenu, t_nFirstID, m_szNoEntries, t_cchItemLen, MF_BYCOMMAND) == 0)) | 
 | 		{ | 
 | 			T* pT = static_cast<T*>(this); | 
 | 			pT;   // avoid level 4 warning | 
 | 			SecureHelper::strncpy_x(m_szNoEntries, _countof(m_szNoEntries), pT->GetMRUEmptyText(), _TRUNCATE); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	int GetMaxEntries() const | 
 | 	{ | 
 | 		return m_nMaxEntries; | 
 | 	} | 
 |  | 
 | 	void SetMaxEntries(int nMaxEntries) | 
 | 	{ | 
 | 		ATLASSERT(nMaxEntries >= m_nMaxEntries_Min && nMaxEntries <= m_nMaxEntries_Max); | 
 | 		if(nMaxEntries < m_nMaxEntries_Min) | 
 | 			nMaxEntries = m_nMaxEntries_Min; | 
 | 		else if(nMaxEntries > m_nMaxEntries_Max) | 
 | 			nMaxEntries = m_nMaxEntries_Max; | 
 | 		m_nMaxEntries = nMaxEntries; | 
 | 	} | 
 |  | 
 | 	int GetMaxItemLength() const | 
 | 	{ | 
 | 		return m_cchMaxItemLen; | 
 | 	} | 
 |  | 
 | 	void SetMaxItemLength(int cchMaxLen) | 
 | 	{ | 
 | 		ATLASSERT((cchMaxLen >= m_cchMaxItemLen_Min && cchMaxLen <= m_cchMaxItemLen_Max) || cchMaxLen == -1); | 
 | 		if(cchMaxLen != -1) | 
 | 		{ | 
 | 			if(cchMaxLen < m_cchMaxItemLen_Min) | 
 | 				cchMaxLen = m_cchMaxItemLen_Min; | 
 | 			else if(cchMaxLen > m_cchMaxItemLen_Max) | 
 | 				cchMaxLen = m_cchMaxItemLen_Max; | 
 | 		} | 
 | 		m_cchMaxItemLen = cchMaxLen; | 
 | 		T* pT = static_cast<T*>(this); | 
 | 		pT->UpdateMenu(); | 
 | 	} | 
 |  | 
 | // Operations | 
 | 	BOOL AddToList(LPCTSTR lpstrDocName) | 
 | 	{ | 
 | 		_DocEntry de; | 
 | 		errno_t nRet = SecureHelper::strncpy_x(de.szDocName, _countof(de.szDocName), lpstrDocName, _TRUNCATE); | 
 | 		if(nRet != 0 && nRet != STRUNCATE) | 
 | 			return FALSE; | 
 |  | 
 | 		for(int i = 0; i < m_arrDocs.GetSize(); i++) | 
 | 		{ | 
 | 			if(lstrcmpi(m_arrDocs[i].szDocName, lpstrDocName) == 0) | 
 | 			{ | 
 | 				m_arrDocs.RemoveAt(i); | 
 | 				break; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if(m_arrDocs.GetSize() == m_nMaxEntries) | 
 | 			m_arrDocs.RemoveAt(0); | 
 |  | 
 | 		BOOL bRet = m_arrDocs.Add(de); | 
 | 		if(bRet) | 
 | 		{ | 
 | 			T* pT = static_cast<T*>(this); | 
 | 			bRet = pT->UpdateMenu(); | 
 | 		} | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	// This function is deprecated because it is not safe.  | 
 | 	// Use the version below that accepts the buffer length. | 
 | #if (_MSC_VER >= 1300) | 
 | 	__declspec(deprecated) | 
 | #endif | 
 | 	BOOL GetFromList(int /*nItemID*/, LPTSTR /*lpstrDocName*/) | 
 | 	{ | 
 | 		ATLASSERT(FALSE); | 
 | 		return FALSE; | 
 | 	} | 
 |  | 
 | 	BOOL GetFromList(int nItemID, LPTSTR lpstrDocName, int cchLength) | 
 | 	{ | 
 | 		int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; | 
 | 		if(nIndex < 0 || nIndex >= m_arrDocs.GetSize()) | 
 | 			return FALSE; | 
 | 		if(lstrlen(m_arrDocs[nIndex].szDocName) >= cchLength) | 
 | 			return FALSE; | 
 | 		SecureHelper::strcpy_x(lpstrDocName, cchLength, m_arrDocs[nIndex].szDocName); | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) | 
 | 	BOOL GetFromList(int nItemID, _CSTRING_NS::CString& strDocName) | 
 | 	{ | 
 | 		int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; | 
 | 		if(nIndex < 0 || nIndex >= m_arrDocs.GetSize()) | 
 | 			return FALSE; | 
 | 		strDocName = m_arrDocs[nIndex].szDocName; | 
 | 		return TRUE; | 
 | 	} | 
 | #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) | 
 |  | 
 | 	BOOL RemoveFromList(int nItemID) | 
 | 	{ | 
 | 		int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; | 
 | 		BOOL bRet = m_arrDocs.RemoveAt(nIndex); | 
 | 		if(bRet) | 
 | 		{ | 
 | 			T* pT = static_cast<T*>(this); | 
 | 			bRet = pT->UpdateMenu(); | 
 | 		} | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	BOOL MoveToTop(int nItemID) | 
 | 	{ | 
 | 		int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1; | 
 | 		if(nIndex < 0 || nIndex >= m_arrDocs.GetSize()) | 
 | 			return FALSE; | 
 | 		_DocEntry de; | 
 | 		de = m_arrDocs[nIndex]; | 
 | 		m_arrDocs.RemoveAt(nIndex); | 
 | 		BOOL bRet = m_arrDocs.Add(de); | 
 | 		if(bRet) | 
 | 		{ | 
 | 			T* pT = static_cast<T*>(this); | 
 | 			bRet = pT->UpdateMenu(); | 
 | 		} | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	BOOL ReadFromRegistry(LPCTSTR lpstrRegKey) | 
 | 	{ | 
 | 		T* pT = static_cast<T*>(this); | 
 | 		ATL::CRegKey rkParent; | 
 | 		ATL::CRegKey rk; | 
 |  | 
 | 		LONG lRet = rkParent.Open(HKEY_CURRENT_USER, lpstrRegKey); | 
 | 		if(lRet != ERROR_SUCCESS) | 
 | 			return FALSE; | 
 | 		lRet = rk.Open(rkParent, pT->GetRegKeyName()); | 
 | 		if(lRet != ERROR_SUCCESS) | 
 | 			return FALSE; | 
 |  | 
 | 		DWORD dwRet = 0; | 
 | #if (_ATL_VER >= 0x0700) | 
 | 		lRet = rk.QueryDWORDValue(pT->GetRegCountName(), dwRet); | 
 | #else | 
 | 		lRet = rk.QueryValue(dwRet, pT->GetRegCountName()); | 
 | #endif | 
 | 		if(lRet != ERROR_SUCCESS) | 
 | 			return FALSE; | 
 | 		SetMaxEntries(dwRet); | 
 |  | 
 | 		m_arrDocs.RemoveAll(); | 
 |  | 
 | 		TCHAR szRetString[t_cchItemLen] = { 0 }; | 
 | 		_DocEntry de; | 
 |  | 
 | 		for(int nItem = m_nMaxEntries; nItem > 0; nItem--) | 
 | 		{ | 
 | 			TCHAR szBuff[m_cchItemNameLen] = { 0 }; | 
 | 			SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem); | 
 | #if (_ATL_VER >= 0x0700) | 
 | 			ULONG ulCount = t_cchItemLen; | 
 | 			lRet = rk.QueryStringValue(szBuff, szRetString, &ulCount); | 
 | #else | 
 | 			DWORD dwCount = t_cchItemLen * sizeof(TCHAR); | 
 | 			lRet = rk.QueryValue(szRetString, szBuff, &dwCount); | 
 | #endif | 
 | 			if(lRet == ERROR_SUCCESS) | 
 | 			{ | 
 | 				SecureHelper::strcpy_x(de.szDocName, _countof(de.szDocName), szRetString); | 
 | 				m_arrDocs.Add(de); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		rk.Close(); | 
 | 		rkParent.Close(); | 
 |  | 
 | 		return pT->UpdateMenu(); | 
 | 	} | 
 |  | 
 | 	BOOL WriteToRegistry(LPCTSTR lpstrRegKey) | 
 | 	{ | 
 | 		T* pT = static_cast<T*>(this); | 
 | 		pT;   // avoid level 4 warning | 
 | 		ATL::CRegKey rkParent; | 
 | 		ATL::CRegKey rk; | 
 |  | 
 | 		LONG lRet = rkParent.Create(HKEY_CURRENT_USER, lpstrRegKey); | 
 | 		if(lRet != ERROR_SUCCESS) | 
 | 			return FALSE; | 
 | 		lRet = rk.Create(rkParent, pT->GetRegKeyName()); | 
 | 		if(lRet != ERROR_SUCCESS) | 
 | 			return FALSE; | 
 |  | 
 | #if (_ATL_VER >= 0x0700) | 
 | 		lRet = rk.SetDWORDValue(pT->GetRegCountName(), m_nMaxEntries); | 
 | #else | 
 | 		lRet = rk.SetValue(m_nMaxEntries, pT->GetRegCountName()); | 
 | #endif | 
 | 		ATLASSERT(lRet == ERROR_SUCCESS); | 
 |  | 
 | 		// set new values | 
 | 		int nItem; | 
 | 		for(nItem = m_arrDocs.GetSize(); nItem > 0; nItem--) | 
 | 		{ | 
 | 			TCHAR szBuff[m_cchItemNameLen] = { 0 }; | 
 | 			SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem); | 
 | 			TCHAR szDocName[t_cchItemLen] = { 0 }; | 
 | 			GetFromList(t_nFirstID + nItem - 1, szDocName, t_cchItemLen); | 
 | #if (_ATL_VER >= 0x0700) | 
 | 			lRet = rk.SetStringValue(szBuff, szDocName); | 
 | #else | 
 | 			lRet = rk.SetValue(szDocName, szBuff); | 
 | #endif | 
 | 			ATLASSERT(lRet == ERROR_SUCCESS); | 
 | 		} | 
 |  | 
 | 		// delete unused keys | 
 | 		for(nItem = m_arrDocs.GetSize() + 1; nItem < m_nMaxEntries_Max; nItem++) | 
 | 		{ | 
 | 			TCHAR szBuff[m_cchItemNameLen] = { 0 }; | 
 | 			SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem); | 
 | 			rk.DeleteValue(szBuff); | 
 | 		} | 
 |  | 
 | 		rk.Close(); | 
 | 		rkParent.Close(); | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | // Implementation | 
 | 	BOOL UpdateMenu() | 
 | 	{ | 
 | 		if(m_hMenu == NULL) | 
 | 			return FALSE; | 
 | 		ATLASSERT(::IsMenu(m_hMenu)); | 
 |  | 
 | 		int nItems = ::GetMenuItemCount(m_hMenu); | 
 | 		int nInsertPoint; | 
 | 		for(nInsertPoint = 0; nInsertPoint < nItems; nInsertPoint++) | 
 | 		{ | 
 | 			CMenuItemInfo mi; | 
 | 			mi.fMask = MIIM_ID; | 
 | 			::GetMenuItemInfo(m_hMenu, nInsertPoint, TRUE, &mi); | 
 | 			if (mi.wID == t_nFirstID) | 
 | 				break; | 
 | 		} | 
 | 		ATLASSERT(nInsertPoint < nItems && "You need a menu item with an ID = t_nFirstID"); | 
 |  | 
 | 		int nItem; | 
 | 		for(nItem = t_nFirstID; nItem < t_nFirstID + m_nMaxEntries; nItem++) | 
 | 		{ | 
 | 			// keep the first one as an insertion point | 
 | 			if (nItem != t_nFirstID) | 
 | 				::DeleteMenu(m_hMenu, nItem, MF_BYCOMMAND); | 
 | 		} | 
 |  | 
 | 		TCHAR szItemText[t_cchItemLen + 6] = { 0 };   // add space for &, 2 digits, and a space | 
 | 		int nSize = m_arrDocs.GetSize(); | 
 | 		nItem = 0; | 
 | 		if(nSize > 0) | 
 | 		{ | 
 | 			for(nItem = 0; nItem < nSize; nItem++) | 
 | 			{ | 
 | 				if(m_cchMaxItemLen == -1) | 
 | 				{ | 
 | 					SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, m_arrDocs[nSize - 1 - nItem].szDocName); | 
 | 				} | 
 | 				else | 
 | 				{ | 
 | 					TCHAR szBuff[t_cchItemLen] = { 0 }; | 
 | 					T* pT = static_cast<T*>(this); | 
 | 					pT;   // avoid level 4 warning | 
 | 					bool bRet = pT->CompactDocumentName(szBuff, m_arrDocs[nSize - 1 - nItem].szDocName, m_cchMaxItemLen); | 
 | 					bRet;   // avoid level 4 warning | 
 | 					ATLASSERT(bRet); | 
 | 					SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, szBuff); | 
 | 				} | 
 | 				::InsertMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION | MF_STRING, t_nFirstID + nItem, szItemText); | 
 | 			} | 
 | 		} | 
 | 		else	// empty | 
 | 		{ | 
 | 			::InsertMenu(m_hMenu, nInsertPoint, MF_BYPOSITION | MF_STRING, t_nFirstID, m_szNoEntries); | 
 | 			::EnableMenuItem(m_hMenu, t_nFirstID, MF_GRAYED); | 
 | 			nItem++; | 
 | 		} | 
 | 		::DeleteMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION); | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | // Overrideables | 
 | 	// override to provide a different method of compacting document names | 
 | 	static bool CompactDocumentName(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen) | 
 | 	{ | 
 | 		return AtlCompactPath(lpstrOut, lpstrIn, cchLen); | 
 | 	} | 
 |  | 
 | 	static LPCTSTR GetRegKeyName() | 
 | 	{ | 
 | 		return _T("Recent Document List"); | 
 | 	} | 
 |  | 
 | 	static LPCTSTR GetRegCountName() | 
 | 	{ | 
 | 		return _T("DocumentCount"); | 
 | 	} | 
 |  | 
 | 	static LPCTSTR GetRegItemName() | 
 | 	{ | 
 | 		// Note: This string is a format string used with wsprintf(). | 
 | 		// Resulting formatted string must be m_cchItemNameLen or less  | 
 | 		// characters long, including the terminating null character. | 
 | 		return _T("Document%i"); | 
 | 	} | 
 |  | 
 | 	static LPCTSTR GetMRUEmptyText() | 
 | 	{ | 
 | 		return _WTL_MRUEMPTY_TEXT; | 
 | 	} | 
 | }; | 
 |  | 
 | class CRecentDocumentList : public CRecentDocumentListBase<CRecentDocumentList> | 
 | { | 
 | public: | 
 | // nothing here | 
 | }; | 
 |  | 
 | #endif // _WIN32_WCE | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // CFindFile - file search helper class | 
 |  | 
 | class CFindFile | 
 | { | 
 | public: | 
 | // Data members | 
 | 	WIN32_FIND_DATA m_fd; | 
 | 	TCHAR m_lpszRoot[MAX_PATH]; | 
 | 	TCHAR m_chDirSeparator; | 
 | 	HANDLE m_hFind; | 
 | 	BOOL m_bFound; | 
 |  | 
 | // Constructor/destructor | 
 | 	CFindFile() : m_hFind(NULL), m_chDirSeparator(_T('\\')), m_bFound(FALSE) | 
 | 	{ } | 
 |  | 
 | 	~CFindFile() | 
 | 	{ | 
 | 		Close(); | 
 | 	} | 
 |  | 
 | // Attributes | 
 | 	ULONGLONG GetFileSize() const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		ULARGE_INTEGER nFileSize = { 0 }; | 
 |  | 
 | 		if(m_bFound) | 
 | 		{ | 
 | 			nFileSize.LowPart = m_fd.nFileSizeLow; | 
 | 			nFileSize.HighPart = m_fd.nFileSizeHigh; | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			nFileSize.QuadPart = 0; | 
 | 		} | 
 |  | 
 | 		return nFileSize.QuadPart; | 
 | 	} | 
 |  | 
 | 	BOOL GetFileName(LPTSTR lpstrFileName, int cchLength) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 | 		if(lstrlen(m_fd.cFileName) >= cchLength) | 
 | 			return FALSE; | 
 |  | 
 | 		if(m_bFound) | 
 | 			SecureHelper::strcpy_x(lpstrFileName, cchLength, m_fd.cFileName); | 
 |  | 
 | 		return m_bFound; | 
 | 	} | 
 |  | 
 | 	BOOL GetFilePath(LPTSTR lpstrFilePath, int cchLength) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		int nLen = lstrlen(m_lpszRoot); | 
 | #ifndef _WIN32_WCE | 
 | 		ATLASSERT(nLen > 0); | 
 | 		if(nLen == 0) | 
 | 			return FALSE; | 
 |  | 
 | 		bool bAddSep = (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/')); | 
 | #else // CE specific | 
 | 		// allow diskless devices (nLen == 0) | 
 | 		bool bAddSep = ((nLen == 0) || (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/'))); | 
 | #endif // _WIN32_WCE | 
 |  | 
 | 		if((lstrlen(m_lpszRoot) + (bAddSep ?  1 : 0)) >= cchLength) | 
 | 			return FALSE; | 
 |  | 
 | 		SecureHelper::strcpy_x(lpstrFilePath, cchLength, m_lpszRoot); | 
 |  | 
 | 		if(bAddSep) | 
 | 		{ | 
 | 			TCHAR szSeparator[2] = { m_chDirSeparator, 0 }; | 
 | 			SecureHelper::strcat_x(lpstrFilePath, cchLength, szSeparator); | 
 | 		} | 
 |  | 
 | 		SecureHelper::strcat_x(lpstrFilePath, cchLength, m_fd.cFileName); | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | #ifndef _WIN32_WCE | 
 | 	BOOL GetFileTitle(LPTSTR lpstrFileTitle, int cchLength) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		TCHAR szBuff[MAX_PATH] = { 0 }; | 
 | 		if(!GetFileName(szBuff, MAX_PATH)) | 
 | 			return FALSE; | 
 |  | 
 | 		if(lstrlen(szBuff) >= cchLength || cchLength < 1) | 
 | 			return FALSE; | 
 |  | 
 | 		// find the last dot | 
 | 		LPTSTR pstrDot  = (LPTSTR)_cstrrchr(szBuff, _T('.')); | 
 | 		if(pstrDot != NULL) | 
 | 			*pstrDot = 0; | 
 |  | 
 | 		SecureHelper::strcpy_x(lpstrFileTitle, cchLength, szBuff); | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 | #endif // !_WIN32_WCE | 
 |  | 
 | 	BOOL GetFileURL(LPTSTR lpstrFileURL, int cchLength) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		TCHAR szBuff[MAX_PATH] = { 0 }; | 
 | 		if(!GetFilePath(szBuff, MAX_PATH)) | 
 | 			return FALSE; | 
 | 		LPCTSTR lpstrFileURLPrefix = _T("file://"); | 
 | 		if(lstrlen(szBuff) + lstrlen(lpstrFileURLPrefix) >= cchLength) | 
 | 			return FALSE; | 
 | 		SecureHelper::strcpy_x(lpstrFileURL, cchLength, lpstrFileURLPrefix); | 
 | 		SecureHelper::strcat_x(lpstrFileURL, cchLength, szBuff); | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | 	BOOL GetRoot(LPTSTR lpstrRoot, int cchLength) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 | 		if(lstrlen(m_lpszRoot) >= cchLength) | 
 | 			return FALSE; | 
 |  | 
 | 		SecureHelper::strcpy_x(lpstrRoot, cchLength, m_lpszRoot); | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | #if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) | 
 | 	_CSTRING_NS::CString GetFileName() const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		_CSTRING_NS::CString ret; | 
 |  | 
 | 		if(m_bFound) | 
 | 			ret = m_fd.cFileName; | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	_CSTRING_NS::CString GetFilePath() const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		_CSTRING_NS::CString strResult = m_lpszRoot; | 
 | 		int nLen = strResult.GetLength(); | 
 | #ifndef _WIN32_WCE | 
 | 		ATLASSERT(nLen > 0); | 
 | 		if(nLen == 0) | 
 | 			return strResult; | 
 |  | 
 | 		if((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/'))) | 
 | #else // CE specific | 
 | 		// allow diskless devices (nLen == 0) | 
 | 		if((nLen == 0) || ((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/')))) | 
 | #endif // _WIN32_WCE | 
 | 			strResult += m_chDirSeparator; | 
 | 		strResult += GetFileName(); | 
 | 		return strResult; | 
 | 	} | 
 |  | 
 | #ifndef _WIN32_WCE | 
 | 	_CSTRING_NS::CString GetFileTitle() const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		_CSTRING_NS::CString strResult; | 
 | 		GetFileTitle(strResult.GetBuffer(MAX_PATH), MAX_PATH); | 
 | 		strResult.ReleaseBuffer(); | 
 |  | 
 | 		return strResult; | 
 | 	} | 
 | #endif // !_WIN32_WCE | 
 |  | 
 | 	_CSTRING_NS::CString GetFileURL() const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		_CSTRING_NS::CString strResult("file://"); | 
 | 		strResult += GetFilePath(); | 
 | 		return strResult; | 
 | 	} | 
 |  | 
 | 	_CSTRING_NS::CString GetRoot() const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		_CSTRING_NS::CString str = m_lpszRoot; | 
 | 		return str; | 
 | 	} | 
 | #endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__) | 
 |  | 
 | 	BOOL GetLastWriteTime(FILETIME* pTimeStamp) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 | 		ATLASSERT(pTimeStamp != NULL); | 
 |  | 
 | 		if(m_bFound && pTimeStamp != NULL) | 
 | 		{ | 
 | 			*pTimeStamp = m_fd.ftLastWriteTime; | 
 | 			return TRUE; | 
 | 		} | 
 |  | 
 | 		return FALSE; | 
 | 	} | 
 |  | 
 | 	BOOL GetLastAccessTime(FILETIME* pTimeStamp) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 | 		ATLASSERT(pTimeStamp != NULL); | 
 |  | 
 | 		if(m_bFound && pTimeStamp != NULL) | 
 | 		{ | 
 | 			*pTimeStamp = m_fd.ftLastAccessTime; | 
 | 			return TRUE; | 
 | 		} | 
 |  | 
 | 		return FALSE; | 
 | 	} | 
 |  | 
 | 	BOOL GetCreationTime(FILETIME* pTimeStamp) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		if(m_bFound && pTimeStamp != NULL) | 
 | 		{ | 
 | 			*pTimeStamp = m_fd.ftCreationTime; | 
 | 			return TRUE; | 
 | 		} | 
 |  | 
 | 		return FALSE; | 
 | 	} | 
 |  | 
 | 	BOOL MatchesMask(DWORD dwMask) const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		if(m_bFound) | 
 | 			return ((m_fd.dwFileAttributes & dwMask) != 0); | 
 |  | 
 | 		return FALSE; | 
 | 	} | 
 |  | 
 | 	BOOL IsDots() const | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		// return TRUE if the file name is "." or ".." and | 
 | 		// the file is a directory | 
 |  | 
 | 		BOOL bResult = FALSE; | 
 | 		if(m_bFound && IsDirectory()) | 
 | 		{ | 
 | 			if(m_fd.cFileName[0] == _T('.') && (m_fd.cFileName[1] == _T('\0') || (m_fd.cFileName[1] == _T('.') && m_fd.cFileName[2] == _T('\0')))) | 
 | 				bResult = TRUE; | 
 | 		} | 
 |  | 
 | 		return bResult; | 
 | 	} | 
 |  | 
 | 	BOOL IsReadOnly() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_READONLY); | 
 | 	} | 
 |  | 
 | 	BOOL IsDirectory() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); | 
 | 	} | 
 |  | 
 | 	BOOL IsCompressed() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_COMPRESSED); | 
 | 	} | 
 |  | 
 | 	BOOL IsSystem() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_SYSTEM); | 
 | 	} | 
 |  | 
 | 	BOOL IsHidden() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_HIDDEN); | 
 | 	} | 
 |  | 
 | 	BOOL IsTemporary() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_TEMPORARY); | 
 | 	} | 
 |  | 
 | 	BOOL IsNormal() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_NORMAL); | 
 | 	} | 
 |  | 
 | 	BOOL IsArchived() const | 
 | 	{ | 
 | 		return MatchesMask(FILE_ATTRIBUTE_ARCHIVE); | 
 | 	} | 
 |  | 
 | // Operations | 
 | 	BOOL FindFile(LPCTSTR pstrName = NULL) | 
 | 	{ | 
 | 		Close(); | 
 |  | 
 | 		if(pstrName == NULL) | 
 | 		{ | 
 | 			pstrName = _T("*.*"); | 
 | 		} | 
 | 		else if(lstrlen(pstrName) >= MAX_PATH) | 
 | 		{ | 
 | 			ATLASSERT(FALSE); | 
 | 			return FALSE; | 
 | 		} | 
 |  | 
 | 		SecureHelper::strcpy_x(m_fd.cFileName, _countof(m_fd.cFileName), pstrName); | 
 |  | 
 | 		m_hFind = ::FindFirstFile(pstrName, &m_fd); | 
 |  | 
 | 		if(m_hFind == INVALID_HANDLE_VALUE) | 
 | 			return FALSE; | 
 |  | 
 | #ifndef _WIN32_WCE | 
 | 		bool bFullPath = (::GetFullPathName(pstrName, MAX_PATH, m_lpszRoot, NULL) != 0); | 
 | #else // CE specific | 
 | 		errno_t nRet = SecureHelper::strncpy_x(m_lpszRoot, _countof(m_lpszRoot), pstrName, _TRUNCATE); | 
 | 		bool bFullPath = (nRet == 0 || nRet == STRUNCATE); | 
 | #endif // _WIN32_WCE | 
 |  | 
 | 		// passed name isn't a valid path but was found by the API | 
 | 		ATLASSERT(bFullPath); | 
 | 		if(!bFullPath) | 
 | 		{ | 
 | 			Close(); | 
 | 			::SetLastError(ERROR_INVALID_NAME); | 
 | 			return FALSE; | 
 | 		} | 
 | 		else | 
 | 		{ | 
 | 			// find the last forward or backward whack | 
 | 			LPTSTR pstrBack  = (LPTSTR)_cstrrchr(m_lpszRoot, _T('\\')); | 
 | 			LPTSTR pstrFront = (LPTSTR)_cstrrchr(m_lpszRoot, _T('/')); | 
 |  | 
 | 			if(pstrFront != NULL || pstrBack != NULL) | 
 | 			{ | 
 | 				if(pstrFront == NULL) | 
 | 					pstrFront = m_lpszRoot; | 
 | 				if(pstrBack == NULL) | 
 | 					pstrBack = m_lpszRoot; | 
 |  | 
 | 				// from the start to the last whack is the root | 
 |  | 
 | 				if(pstrFront >= pstrBack) | 
 | 					*pstrFront = _T('\0'); | 
 | 				else | 
 | 					*pstrBack = _T('\0'); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		m_bFound = TRUE; | 
 |  | 
 | 		return TRUE; | 
 | 	} | 
 |  | 
 | 	BOOL FindNextFile() | 
 | 	{ | 
 | 		ATLASSERT(m_hFind != NULL); | 
 |  | 
 | 		if(m_hFind == NULL) | 
 | 			return FALSE; | 
 |  | 
 | 		if(!m_bFound) | 
 | 			return FALSE; | 
 |  | 
 | 		m_bFound = ::FindNextFile(m_hFind, &m_fd); | 
 |  | 
 | 		return m_bFound; | 
 | 	} | 
 |  | 
 | 	void Close() | 
 | 	{ | 
 | 		m_bFound = FALSE; | 
 |  | 
 | 		if(m_hFind != NULL && m_hFind != INVALID_HANDLE_VALUE) | 
 | 		{ | 
 | 			::FindClose(m_hFind); | 
 | 			m_hFind = NULL; | 
 | 		} | 
 | 	} | 
 |  | 
 | // Helper | 
 | 	static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch) | 
 | 	{ | 
 | #ifdef _ATL_MIN_CRT | 
 | 		const TCHAR* lpsz = NULL; | 
 | 		while (*p != 0) | 
 | 		{ | 
 | 			if (*p == ch) | 
 | 				lpsz = p; | 
 | 			p = ::CharNext(p); | 
 | 		} | 
 | 		return lpsz; | 
 | #else // !_ATL_MIN_CRT | 
 | 		return _tcsrchr(p, ch); | 
 | #endif // !_ATL_MIN_CRT | 
 | 	} | 
 | }; | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // Global functions for loading resources | 
 |  | 
 | inline HACCEL AtlLoadAccelerators(ATL::_U_STRINGorID table) | 
 | { | 
 | 	return ::LoadAccelerators(ModuleHelper::GetResourceInstance(), table.m_lpstr); | 
 | } | 
 |  | 
 | inline HMENU AtlLoadMenu(ATL::_U_STRINGorID menu) | 
 | { | 
 | 	return ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr); | 
 | } | 
 |  | 
 | inline HBITMAP AtlLoadBitmap(ATL::_U_STRINGorID bitmap) | 
 | { | 
 | 	return ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr); | 
 | } | 
 |  | 
 | #ifdef OEMRESOURCE | 
 | inline HBITMAP AtlLoadSysBitmap(ATL::_U_STRINGorID bitmap) | 
 | { | 
 | #ifdef _DEBUG | 
 | 	WORD wID = (WORD)bitmap.m_lpstr; | 
 | 	ATLASSERT(wID >= 32734 && wID <= 32767); | 
 | #endif // _DEBUG | 
 | 	return ::LoadBitmap(NULL, bitmap.m_lpstr); | 
 | } | 
 | #endif // OEMRESOURCE | 
 |  | 
 | inline HCURSOR AtlLoadCursor(ATL::_U_STRINGorID cursor) | 
 | { | 
 | 	return ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr); | 
 | } | 
 |  | 
 | inline HCURSOR AtlLoadSysCursor(LPCTSTR lpCursorName) | 
 | { | 
 | #if (WINVER >= 0x0500) | 
 | 	ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT || | 
 | 		lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE || | 
 | 		lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW || | 
 | 		lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL || | 
 | 		lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP || | 
 | 		lpCursorName == IDC_HAND); | 
 | #else // !(WINVER >= 0x0500) | 
 | 	ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT || | 
 | 		lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE || | 
 | 		lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW || | 
 | 		lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL || | 
 | 		lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP); | 
 | #endif // !(WINVER >= 0x0500) | 
 | 	return ::LoadCursor(NULL, lpCursorName); | 
 | } | 
 |  | 
 | inline HICON AtlLoadIcon(ATL::_U_STRINGorID icon) | 
 | { | 
 | 	return ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr); | 
 | } | 
 |  | 
 | #ifndef _WIN32_WCE | 
 | inline HICON AtlLoadSysIcon(LPCTSTR lpIconName) | 
 | { | 
 | #if (WINVER >= 0x0600) | 
 | 	ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION || | 
 | 	          lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO || | 
 | 	          lpIconName == IDI_SHIELD); | 
 | #else // !(WINVER >= 0x0600) | 
 | 	ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION || | 
 | 	          lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO); | 
 | #endif // !(WINVER >= 0x0600) | 
 | 	return ::LoadIcon(NULL, lpIconName); | 
 | } | 
 | #endif // !_WIN32_WCE | 
 |  | 
 | inline HBITMAP AtlLoadBitmapImage(ATL::_U_STRINGorID bitmap, UINT fuLoad = LR_DEFAULTCOLOR) | 
 | { | 
 | 	return (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, IMAGE_BITMAP, 0, 0, fuLoad); | 
 | } | 
 |  | 
 | inline HCURSOR AtlLoadCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) | 
 | { | 
 | 	return (HCURSOR)::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); | 
 | } | 
 |  | 
 | inline HICON AtlLoadIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) | 
 | { | 
 | 	return (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); | 
 | } | 
 |  | 
 | #ifdef OEMRESOURCE | 
 | inline HBITMAP AtlLoadSysBitmapImage(WORD wBitmapID, UINT fuLoad = LR_DEFAULTCOLOR) | 
 | { | 
 | 	ATLASSERT(wBitmapID >= 32734 && wBitmapID <= 32767); | 
 | 	ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file | 
 | 	return (HBITMAP)::LoadImage(NULL, MAKEINTRESOURCE(wBitmapID), IMAGE_BITMAP, 0, 0, fuLoad); | 
 | } | 
 | #endif // OEMRESOURCE | 
 |  | 
 | inline HCURSOR AtlLoadSysCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) | 
 | { | 
 | #ifdef _DEBUG | 
 | 	WORD wID = (WORD)cursor.m_lpstr; | 
 | 	ATLASSERT((wID >= 32512 && wID <= 32516) || (wID >= 32640 && wID <= 32648) || (wID == 32650) || (wID == 32651)); | 
 | 	ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file | 
 | #endif // _DEBUG | 
 | 	return (HCURSOR)::LoadImage(NULL, cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad); | 
 | } | 
 |  | 
 | inline HICON AtlLoadSysIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0) | 
 | { | 
 | #ifdef _DEBUG | 
 | 	WORD wID = (WORD)icon.m_lpstr; | 
 | 	ATLASSERT(wID >= 32512 && wID <= 32517); | 
 | 	ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file | 
 | #endif // _DEBUG | 
 | 	return (HICON)::LoadImage(NULL, icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad); | 
 | } | 
 |  | 
 | #if (_ATL_VER < 0x0700) | 
 | inline int AtlLoadString(UINT uID, LPTSTR lpBuffer, int nBufferMax) | 
 | { | 
 | 	return ::LoadString(_Module.GetResourceInstance(), uID, lpBuffer, nBufferMax); | 
 | } | 
 | #endif // (_ATL_VER < 0x0700) | 
 |  | 
 | #ifdef _WIN32_WCE // CE only direct access to the resource | 
 | inline LPCTSTR AtlLoadString(UINT uID) | 
 | { | 
 | 	LPCTSTR s = (LPCTSTR)::LoadString(ModuleHelper::GetResourceInstance(), uID, NULL, 0); | 
 | #ifdef DEBUG // Check for null-termination | 
 | 	if(s != NULL) | 
 | 		// Note: RC -n <file.rc> compiles null-terminated resource strings | 
 | 		ATLASSERT(s[*((WORD*)s -1) - 1] == L'\0'); | 
 | #endif | 
 | 	return s; | 
 | } | 
 | #endif // _WIN32_WCE | 
 |  | 
 | inline bool AtlLoadString(UINT uID, BSTR& bstrText) | 
 | { | 
 | 	USES_CONVERSION; | 
 | 	ATLASSERT(bstrText == NULL); | 
 |  | 
 | 	LPTSTR lpstrText = NULL; | 
 | 	int nRes = 0; | 
 | 	for(int nLen = 256; ; nLen *= 2) | 
 | 	{ | 
 | 		ATLTRY(lpstrText = new TCHAR[nLen]); | 
 | 		if(lpstrText == NULL) | 
 | 			break; | 
 | 		nRes = ::LoadString(ModuleHelper::GetResourceInstance(), uID, lpstrText, nLen); | 
 | 		if(nRes < nLen - 1) | 
 | 			break; | 
 | 		delete [] lpstrText; | 
 | 		lpstrText = NULL; | 
 | 	} | 
 |  | 
 | 	if(lpstrText != NULL) | 
 | 	{ | 
 | 		if(nRes != 0) | 
 | 			bstrText = ::SysAllocString(T2OLE(lpstrText)); | 
 | 		delete [] lpstrText; | 
 | 	} | 
 |  | 
 | 	return (bstrText != NULL) ? true : false; | 
 | } | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // Global functions for stock GDI objects | 
 |  | 
 | inline HPEN AtlGetStockPen(int nPen) | 
 | { | 
 | #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE) | 
 | 	ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN || nPen == DC_PEN); | 
 | #else | 
 | 	ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN); | 
 | #endif | 
 | 	return (HPEN)::GetStockObject(nPen); | 
 | } | 
 |  | 
 | inline HBRUSH AtlGetStockBrush(int nBrush) | 
 | { | 
 | #if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE) | 
 | 	ATLASSERT((nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH) || nBrush == DC_BRUSH); | 
 | #else | 
 | 	ATLASSERT(nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH); | 
 | #endif | 
 | 	return (HBRUSH)::GetStockObject(nBrush); | 
 | } | 
 |  | 
 | inline HFONT AtlGetStockFont(int nFont) | 
 | { | 
 | #ifndef _WIN32_WCE | 
 | 	ATLASSERT((nFont >= OEM_FIXED_FONT && nFont <= SYSTEM_FIXED_FONT) || nFont == DEFAULT_GUI_FONT); | 
 | #else // CE specific | 
 | 	ATLASSERT(nFont == SYSTEM_FONT); | 
 | #endif // _WIN32_WCE | 
 | 	return (HFONT)::GetStockObject(nFont); | 
 | } | 
 |  | 
 | inline HPALETTE AtlGetStockPalette(int nPalette) | 
 | { | 
 | 	ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported | 
 | 	return (HPALETTE)::GetStockObject(nPalette); | 
 | } | 
 |  | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 | // Global function for compacting a path by replacing parts with ellipsis | 
 |  | 
 | // helper for multi-byte character sets | 
 | inline bool _IsDBCSTrailByte(LPCTSTR lpstr, int nChar) | 
 | { | 
 | #ifndef _UNICODE | 
 | 	int i = nChar; | 
 | 	for( ; i > 0; i--) | 
 | 	{ | 
 | 		if(!::IsDBCSLeadByte(lpstr[i - 1])) | 
 | 			break; | 
 | 	} | 
 | 	return ((nChar > 0) && (((nChar - i) & 1) != 0)); | 
 | #else // _UNICODE | 
 | 	lpstr; nChar; | 
 | 	return false; | 
 | #endif // _UNICODE | 
 | } | 
 |  | 
 | inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen) | 
 | { | 
 | 	ATLASSERT(lpstrOut != NULL); | 
 | 	ATLASSERT(lpstrIn != NULL); | 
 | 	ATLASSERT(cchLen > 0); | 
 |  | 
 | 	LPCTSTR szEllipsis = _T("..."); | 
 | 	const int cchEndEllipsis = 3; | 
 | 	const int cchMidEllipsis = 4; | 
 |  | 
 | 	if(lstrlen(lpstrIn) < cchLen) | 
 | 	{ | 
 | 		SecureHelper::strcpy_x(lpstrOut, cchLen, lpstrIn); | 
 | 		return true; | 
 | 	} | 
 |  | 
 | 	lpstrOut[0] = 0; | 
 |  | 
 | 	// check if the separator is a slash or a backslash | 
 | 	TCHAR chSlash = _T('\\'); | 
 | 	for(LPTSTR lpstr = (LPTSTR)lpstrIn; *lpstr != 0; lpstr = ::CharNext(lpstr)) | 
 | 	{ | 
 | 		if((*lpstr == _T('/')) || (*lpstr == _T('\\'))) | 
 | 			chSlash = *lpstr; | 
 | 	} | 
 |  | 
 | 	// find the filename portion of the path | 
 | 	LPCTSTR lpstrFileName = lpstrIn; | 
 | 	for(LPCTSTR pPath = lpstrIn; *pPath; pPath = ::CharNext(pPath)) | 
 | 	{ | 
 | 		if((pPath[0] == _T('\\') || pPath[0] == _T(':') || pPath[0] == _T('/')) | 
 | 				&& pPath[1] && pPath[1] != _T('\\') && pPath[1] != _T('/')) | 
 | 			lpstrFileName = pPath + 1; | 
 | 	} | 
 | 	int cchFileName = lstrlen(lpstrFileName); | 
 |  | 
 | 	// handle just the filename without a path | 
 | 	if(lpstrFileName == lpstrIn && cchLen > cchEndEllipsis) | 
 | 	{ | 
 | 		bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchLen - cchEndEllipsis - 1) == 0); | 
 | 		if(bRet) | 
 | 		{ | 
 | #ifndef _UNICODE | 
 | 			if(_IsDBCSTrailByte(lpstrIn, cchLen - cchEndEllipsis)) | 
 | 				lpstrOut[cchLen - cchEndEllipsis - 1] = 0; | 
 | #endif // _UNICODE | 
 | 			SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis); | 
 | 		} | 
 | 		return bRet; | 
 | 	} | 
 |  | 
 | 	// handle just ellipsis | 
 | 	if((cchLen < (cchMidEllipsis + cchEndEllipsis))) | 
 | 	{ | 
 | 		for(int i = 0; i < cchLen - 1; i++) | 
 | 			lpstrOut[i] = ((i + 1) == cchMidEllipsis) ? chSlash : _T('.'); | 
 | 		lpstrOut[cchLen - 1] = 0; | 
 | 		return true; | 
 | 	} | 
 |  | 
 | 	// calc how much we have to copy | 
 | 	int cchToCopy = cchLen - (cchMidEllipsis + cchFileName) - 1; | 
 |  | 
 | 	if(cchToCopy < 0) | 
 | 		cchToCopy = 0; | 
 |  | 
 | #ifndef _UNICODE | 
 | 	if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrIn, cchToCopy)) | 
 | 		cchToCopy--; | 
 | #endif // _UNICODE | 
 |  | 
 | 	bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchToCopy) == 0); | 
 | 	if(!bRet) | 
 | 		return false; | 
 |  | 
 | 	// add ellipsis | 
 | 	SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis); | 
 | 	if(!bRet) | 
 | 		return false; | 
 | 	TCHAR szSlash[2] = { chSlash, 0 }; | 
 | 	SecureHelper::strcat_x(lpstrOut, cchLen, szSlash); | 
 | 	if(!bRet) | 
 | 		return false; | 
 |  | 
 | 	// add filename (and ellipsis, if needed) | 
 | 	if(cchLen > (cchMidEllipsis + cchFileName)) | 
 | 	{ | 
 | 		SecureHelper::strcat_x(lpstrOut, cchLen, lpstrFileName); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 		cchToCopy = cchLen - cchMidEllipsis - cchEndEllipsis - 1; | 
 | #ifndef _UNICODE | 
 | 		if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrFileName, cchToCopy)) | 
 | 			cchToCopy--; | 
 | #endif // _UNICODE | 
 | 		bRet = (SecureHelper::strncpy_x(&lpstrOut[cchMidEllipsis], cchLen - cchMidEllipsis, lpstrFileName, cchToCopy) == 0); | 
 | 		if(bRet) | 
 | 			SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis); | 
 | 	} | 
 |  | 
 | 	return bRet; | 
 | } | 
 |  | 
 | }; // namespace WTL | 
 |  | 
 | #endif // __ATLMISC_H__ |