text: // 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
  // Common Public License 1.0 (http://opensource.org/osi3.0/licenses/cpl1.0.php)
  // which can be found in the file CPL.TXT at the root of this distribution.
  // By using this software in any fashion, you are agreeing to be bound by
  // the terms of this license. You must not remove this notice, or
  // any other, from this software.
  
  #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__
  