root/third_party/wtl/include/atlmisc.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


// Windows Template Library - WTL version 8.0
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// This file is a part of the Windows Template Library.
// The use and distribution terms for this software are covered by the
// Microsoft Permissive License (Ms-PL) which can be found in the file
// Ms-PL.txt at the root of this distribution.

#ifndef __ATLMISC_H__
#define __ATLMISC_H__

#pragma once

#ifndef __cplusplus
        #error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLAPP_H__
        #error atlmisc.h requires atlapp.h to be included first
#endif


#ifdef _ATL_TMP_NO_CSTRING
  #define _WTL_NO_CSTRING
#endif

#if defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING)
        #error Conflicting options - both _WTL_USE_CSTRING and _WTL_NO_CSTRING are defined
#endif // defined(_WTL_USE_CSTRING) && defined(_WTL_NO_CSTRING)

#if !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING)
  #define _WTL_USE_CSTRING
#endif // !defined(_WTL_USE_CSTRING) && !defined(_WTL_NO_CSTRING)

#ifndef _WTL_NO_CSTRING
  #if defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
        #error Cannot use CString floating point formatting with _ATL_MIN_CRT defined
  #endif // defined(_ATL_USE_CSTRING_FLOAT) && defined(_ATL_MIN_CRT)
#endif // !_WTL_NO_CSTRING


///////////////////////////////////////////////////////////////////////////////
// Classes in this file:
//
// CSize
// CPoint
// CRect
// CString
//
// CRecentDocumentListBase<T, t_cchItemLen, t_nFirstID, t_nLastID>
// CRecentDocumentList
// CFindFile
//
// Global functions:
//   AtlLoadAccelerators()
//   AtlLoadMenu()
//   AtlLoadBitmap()
//   AtlLoadSysBitmap()
//   AtlLoadCursor()
//   AtlLoadSysCursor()
//   AtlLoadIcon()
//   AtlLoadSysIcon()
//   AtlLoadBitmapImage()
//   AtlLoadCursorImage()
//   AtlLoadIconImage()
//   AtlLoadSysBitmapImage()
//   AtlLoadSysCursorImage()
//   AtlLoadSysIconImage()
//   AtlLoadString()
//
//   AtlGetStockPen()
//   AtlGetStockBrush()
//   AtlGetStockFont()
//   AtlGetStockPalette()
//
//   AtlCompactPath()


namespace WTL
{

#ifndef _WTL_NO_WTYPES

// forward declarations
class CSize;
class CPoint;
class CRect;

///////////////////////////////////////////////////////////////////////////////
// CSize - Wrapper for Windows SIZE structure.

class CSize : public SIZE
{
public:
// Constructors
        CSize()
        {
                cx = 0;
                cy = 0;
        }

        CSize(int initCX, int initCY)
        {
                cx = initCX;
                cy = initCY;
        }

        CSize(SIZE initSize)
        {
                *(SIZE*)this = initSize;
        }

        CSize(POINT initPt)
        {
                *(POINT*)this = initPt;
        }

        CSize(DWORD dwSize)
        {
                cx = (short)LOWORD(dwSize);
                cy = (short)HIWORD(dwSize);
        }

// Operations
        BOOL operator ==(SIZE size) const
        {
                return (cx == size.cx && cy == size.cy);
        }

        BOOL operator !=(SIZE size) const
        {
                return (cx != size.cx || cy != size.cy);
        }

        void operator +=(SIZE size)
        {
                cx += size.cx;
                cy += size.cy;
        }

        void operator -=(SIZE size)
        {
                cx -= size.cx;
                cy -= size.cy;
        }

        void SetSize(int CX, int CY)
        {
                cx = CX;
                cy = CY;
        }

// Operators returning CSize values
        CSize operator +(SIZE size) const
        {
                return CSize(cx + size.cx, cy + size.cy);
        }

        CSize operator -(SIZE size) const
        {
                return CSize(cx - size.cx, cy - size.cy);
        }

        CSize operator -() const
        {
                return CSize(-cx, -cy);
        }

// Operators returning CPoint values
        CPoint operator +(POINT point) const;
        CPoint operator -(POINT point) const;

// Operators returning CRect values
        CRect operator +(const RECT* lpRect) const;
        CRect operator -(const RECT* lpRect) const;
};


///////////////////////////////////////////////////////////////////////////////
// CPoint - Wrapper for Windows POINT structure.

class CPoint : public POINT
{
public:
// Constructors
        CPoint()
        {
                x = 0;
                y = 0;
        }

        CPoint(int initX, int initY)
        {
                x = initX;
                y = initY;
        }

        CPoint(POINT initPt)
        {
                *(POINT*)this = initPt;
        }

        CPoint(SIZE initSize)
        {
                *(SIZE*)this = initSize;
        }

        CPoint(DWORD dwPoint)
        {
                x = (short)LOWORD(dwPoint);
                y = (short)HIWORD(dwPoint);
        }

// Operations
        void Offset(int xOffset, int yOffset)
        {
                x += xOffset;
                y += yOffset;
        }

        void Offset(POINT point)
        {
                x += point.x;
                y += point.y;
        }

        void Offset(SIZE size)
        {
                x += size.cx;
                y += size.cy;
        }

        BOOL operator ==(POINT point) const
        {
                return (x == point.x && y == point.y);
        }

        BOOL operator !=(POINT point) const
        {
                return (x != point.x || y != point.y);
        }

        void operator +=(SIZE size)
        {
                x += size.cx;
                y += size.cy;
        }

        void operator -=(SIZE size)
        {
                x -= size.cx;
                y -= size.cy;
        }

        void operator +=(POINT point)
        {
                x += point.x;
                y += point.y;
        }

        void operator -=(POINT point)
        {
                x -= point.x;
                y -= point.y;
        }

        void SetPoint(int X, int Y)
        {
                x = X;
                y = Y;
        }

// Operators returning CPoint values
        CPoint operator +(SIZE size) const
        {
                return CPoint(x + size.cx, y + size.cy);
        }

        CPoint operator -(SIZE size) const
        {
                return CPoint(x - size.cx, y - size.cy);
        }

        CPoint operator -() const
        {
                return CPoint(-x, -y);
        }

        CPoint operator +(POINT point) const
        {
                return CPoint(x + point.x, y + point.y);
        }

// Operators returning CSize values
        CSize operator -(POINT point) const
        {
                return CSize(x - point.x, y - point.y);
        }

// Operators returning CRect values
        CRect operator +(const RECT* lpRect) const;
        CRect operator -(const RECT* lpRect) const;
};


///////////////////////////////////////////////////////////////////////////////
// CRect - Wrapper for Windows RECT structure.

class CRect : public RECT
{
public:
// Constructors
        CRect()
        {
                left = 0;
                top = 0;
                right = 0;
                bottom = 0;
        }

        CRect(int l, int t, int r, int b)
        {
                left = l;
                top = t;
                right = r;
                bottom = b;
        }

        CRect(const RECT& srcRect)
        {
                ::CopyRect(this, &srcRect);
        }

        CRect(LPCRECT lpSrcRect)
        {
                ::CopyRect(this, lpSrcRect);
        }

        CRect(POINT point, SIZE size)
        {
                right = (left = point.x) + size.cx;
                bottom = (top = point.y) + size.cy;
        }

        CRect(POINT topLeft, POINT bottomRight)
        {
                left = topLeft.x;
                top = topLeft.y;
                right = bottomRight.x;
                bottom = bottomRight.y;
        }

// Attributes (in addition to RECT members)
        int Width() const
        {
                return right - left;
        }

        int Height() const
        {
                return bottom - top;
        }

        CSize Size() const
        {
                return CSize(right - left, bottom - top);
        }

        CPoint& TopLeft()
        {
                return *((CPoint*)this);
        }

        CPoint& BottomRight()
        {
                return *((CPoint*)this + 1);
        }

        const CPoint& TopLeft() const
        {
                return *((CPoint*)this);
        }

        const CPoint& BottomRight() const
        {
                return *((CPoint*)this + 1);
        }

        CPoint CenterPoint() const
        {
                return CPoint((left + right) / 2, (top + bottom) / 2);
        }

        // convert between CRect and LPRECT/LPCRECT (no need for &)
        operator LPRECT()
        {
                return this;
        }

        operator LPCRECT() const
        {
                return this;
        }

        BOOL IsRectEmpty() const
        {
                return ::IsRectEmpty(this);
        }

        BOOL IsRectNull() const
        {
                return (left == 0 && right == 0 && top == 0 && bottom == 0);
        }

        BOOL PtInRect(POINT point) const
        {
                return ::PtInRect(this, point);
        }

// Operations
        void SetRect(int x1, int y1, int x2, int y2)
        {
                ::SetRect(this, x1, y1, x2, y2);
        }

        void SetRect(POINT topLeft, POINT bottomRight)
        {
                ::SetRect(this, topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
        }

        void SetRectEmpty()
        {
                ::SetRectEmpty(this);
        }

        void CopyRect(LPCRECT lpSrcRect)
        {
                ::CopyRect(this, lpSrcRect);
        }

        BOOL EqualRect(LPCRECT lpRect) const
        {
                return ::EqualRect(this, lpRect);
        }

        void InflateRect(int x, int y)
        {
                ::InflateRect(this, x, y);
        }

        void InflateRect(SIZE size)
        {
                ::InflateRect(this, size.cx, size.cy);
        }

        void InflateRect(LPCRECT lpRect)
        {
                left -= lpRect->left;
                top -= lpRect->top;
                right += lpRect->right;
                bottom += lpRect->bottom;
        }

        void InflateRect(int l, int t, int r, int b)
        {
                left -= l;
                top -= t;
                right += r;
                bottom += b;
        }

        void DeflateRect(int x, int y)
        {
                ::InflateRect(this, -x, -y);
        }

        void DeflateRect(SIZE size)
        {
                ::InflateRect(this, -size.cx, -size.cy);
        }

        void DeflateRect(LPCRECT lpRect)
        {
                left += lpRect->left;
                top += lpRect->top;
                right -= lpRect->right;
                bottom -= lpRect->bottom;
        }

        void DeflateRect(int l, int t, int r, int b)
        {
                left += l;
                top += t;
                right -= r;
                bottom -= b;
        }

        void OffsetRect(int x, int y)
        {
                ::OffsetRect(this, x, y);
        }
        void OffsetRect(SIZE size)
        {
                ::OffsetRect(this, size.cx, size.cy);
        }

        void OffsetRect(POINT point)
        {
                ::OffsetRect(this, point.x, point.y);
        }

        void NormalizeRect()
        {
                int nTemp;
                if (left > right)
                {
                        nTemp = left;
                        left = right;
                        right = nTemp;
                }
                if (top > bottom)
                {
                        nTemp = top;
                        top = bottom;
                        bottom = nTemp;
                }
        }

        // absolute position of rectangle
        void MoveToY(int y)
        {
                bottom = Height() + y;
                top = y;
        }

        void MoveToX(int x)
        {
                right = Width() + x;
                left = x;
        }

        void MoveToXY(int x, int y)
        {
                MoveToX(x);
                MoveToY(y);
        }

        void MoveToXY(POINT pt)
        {
                MoveToX(pt.x);
                MoveToY(pt.y);
        }

        // operations that fill '*this' with result
        BOOL IntersectRect(LPCRECT lpRect1, LPCRECT lpRect2)
        {
                return ::IntersectRect(this, lpRect1, lpRect2);
        }

        BOOL UnionRect(LPCRECT lpRect1, LPCRECT lpRect2)
        {
                return ::UnionRect(this, lpRect1, lpRect2);
        }

        BOOL SubtractRect(LPCRECT lpRectSrc1, LPCRECT lpRectSrc2)
        {
                return ::SubtractRect(this, lpRectSrc1, lpRectSrc2);
        }

// Additional Operations
        void operator =(const RECT& srcRect)
        {
                ::CopyRect(this, &srcRect);
        }

        BOOL operator ==(const RECT& rect) const
        {
                return ::EqualRect(this, &rect);
        }

        BOOL operator !=(const RECT& rect) const
        {
                return !::EqualRect(this, &rect);
        }

        void operator +=(POINT point)
        {
                ::OffsetRect(this, point.x, point.y);
        }

        void operator +=(SIZE size)
        {
                ::OffsetRect(this, size.cx, size.cy);
        }

        void operator +=(LPCRECT lpRect)
        {
                InflateRect(lpRect);
        }

        void operator -=(POINT point)
        {
                ::OffsetRect(this, -point.x, -point.y);
        }

        void operator -=(SIZE size)
        {
                ::OffsetRect(this, -size.cx, -size.cy);
        }

        void operator -=(LPCRECT lpRect)
        {
                DeflateRect(lpRect);
        }

        void operator &=(const RECT& rect)
        {
                ::IntersectRect(this, this, &rect);
        }

        void operator |=(const RECT& rect)
        {
                ::UnionRect(this, this, &rect);
        }

// Operators returning CRect values
        CRect operator +(POINT pt) const
        {
                CRect rect(*this);
                ::OffsetRect(&rect, pt.x, pt.y);
                return rect;
        }

        CRect operator -(POINT pt) const
        {
                CRect rect(*this);
                ::OffsetRect(&rect, -pt.x, -pt.y);
                return rect;
        }

        CRect operator +(LPCRECT lpRect) const
        {
                CRect rect(this);
                rect.InflateRect(lpRect);
                return rect;
        }

        CRect operator +(SIZE size) const
        {
                CRect rect(*this);
                ::OffsetRect(&rect, size.cx, size.cy);
                return rect;
        }

        CRect operator -(SIZE size) const
        {
                CRect rect(*this);
                ::OffsetRect(&rect, -size.cx, -size.cy);
                return rect;
        }

        CRect operator -(LPCRECT lpRect) const
        {
                CRect rect(this);
                rect.DeflateRect(lpRect);
                return rect;
        }

        CRect operator &(const RECT& rect2) const
        {
                CRect rect;
                ::IntersectRect(&rect, this, &rect2);
                return rect;
        }

        CRect operator |(const RECT& rect2) const
        {
                CRect rect;
                ::UnionRect(&rect, this, &rect2);
                return rect;
        }

        CRect MulDiv(int nMultiplier, int nDivisor) const
        {
                return CRect(
                        ::MulDiv(left, nMultiplier, nDivisor),
                        ::MulDiv(top, nMultiplier, nDivisor),
                        ::MulDiv(right, nMultiplier, nDivisor),
                        ::MulDiv(bottom, nMultiplier, nDivisor));
        }
};


// CSize implementation

inline CPoint CSize::operator +(POINT point) const
{ return CPoint(cx + point.x, cy + point.y); }

inline CPoint CSize::operator -(POINT point) const
{ return CPoint(cx - point.x, cy - point.y); }

inline CRect CSize::operator +(const RECT* lpRect) const
{ return CRect(lpRect) + *this; }

inline CRect CSize::operator -(const RECT* lpRect) const
{ return CRect(lpRect) - *this; }


// CPoint implementation

inline CRect CPoint::operator +(const RECT* lpRect) const
{ return CRect(lpRect) + *this; }

inline CRect CPoint::operator -(const RECT* lpRect) const
{ return CRect(lpRect) - *this; }

#endif // !_WTL_NO_WTYPES


// WTL::CSize or ATL::CSize scalar operators 

#if !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__))

template <class Num>
inline CSize operator *(SIZE s, Num n) 
{
        return CSize((int)(s.cx * n), (int)(s.cy * n));
};

template <class Num>
inline void operator *=(SIZE & s, Num n)
{
        s = s * n;
};      

template <class Num>
inline CSize operator /(SIZE s, Num n) 
{
        return CSize((int)(s.cx / n), (int)(s.cy / n));
};

template <class Num>
inline void operator /=(SIZE & s, Num n)
{
        s = s / n;
};      

#endif // !defined(_WTL_NO_SIZE_SCALAR) && (!defined(_WTL_NO_WTYPES) || defined(__ATLTYPES_H__))


///////////////////////////////////////////////////////////////////////////////
// CString - String class

#ifndef _WTL_NO_CSTRING

struct CStringData
{
        long nRefs;     // reference count
        int nDataLength;
        int nAllocLength;
        // TCHAR data[nAllocLength]

        TCHAR* data()
        { return (TCHAR*)(this + 1); }
};

// Globals

// For an empty string, m_pchData will point here
// (note: avoids special case of checking for NULL m_pchData)
// empty string data (and locked)
_declspec(selectany) int rgInitData[] = { -1, 0, 0, 0 };
_declspec(selectany) CStringData* _atltmpDataNil = (CStringData*)&rgInitData;
_declspec(selectany) LPCTSTR _atltmpPchNil = (LPCTSTR)(((BYTE*)&rgInitData) + sizeof(CStringData));


class CString
{
public:
// Constructors
        CString()
        {
                Init();
        }

        CString(const CString& stringSrc)
        {
                ATLASSERT(stringSrc.GetData()->nRefs != 0);
                if (stringSrc.GetData()->nRefs >= 0)
                {
                        ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
                        m_pchData = stringSrc.m_pchData;
                        InterlockedIncrement(&GetData()->nRefs);
                }
                else
                {
                        Init();
                        *this = stringSrc.m_pchData;
                }
        }

        CString(TCHAR ch, int nRepeat = 1)
        {
                ATLASSERT(!_istlead(ch));   // can't create a lead byte string
                Init();
                if (nRepeat >= 1)
                {
                        if(AllocBuffer(nRepeat))
                        {
#ifdef _UNICODE
                                for (int i = 0; i < nRepeat; i++)
                                        m_pchData[i] = ch;
#else
                                memset(m_pchData, ch, nRepeat);
#endif
                        }
                }
        }

        CString(LPCTSTR lpsz)
        {
                Init();
                if (lpsz != NULL && HIWORD(lpsz) == NULL)
                {
                        UINT nID = LOWORD((DWORD_PTR)lpsz);
                        if (!LoadString(nID))
                                ATLTRACE2(atlTraceUI, 0, _T("Warning: implicit LoadString(%u) in CString failed\n"), nID);
                }
                else
                {
                        int nLen = SafeStrlen(lpsz);
                        if (nLen != 0)
                        {
                                if(AllocBuffer(nLen))
                                        SecureHelper::memcpy_x(m_pchData, (nLen + 1) * sizeof(TCHAR), lpsz, nLen * sizeof(TCHAR));
                        }
                }
        }

#ifdef _UNICODE
        CString(LPCSTR lpsz)
        {
                Init();
#if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
                int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0;
#else
                int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0;
#endif
                if (nSrcLen != 0)
                {
                        if(AllocBuffer(nSrcLen))
                        {
                                _mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
                                ReleaseBuffer();
                        }
                }
        }
#else // !_UNICODE
        CString(LPCWSTR lpsz)
        {
                Init();
                int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0;
                if (nSrcLen != 0)
                {
                        if(AllocBuffer(nSrcLen * 2))
                        {
                                _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
                                ReleaseBuffer();
                        }
                }
        }
#endif // !_UNICODE

        CString(LPCTSTR lpch, int nLength)
        {
                Init();
                if (nLength != 0)
                {
                        if(AllocBuffer(nLength))
                                SecureHelper::memcpy_x(m_pchData, (nLength + 1) * sizeof(TCHAR), lpch, nLength * sizeof(TCHAR));
                }
        }

#ifdef _UNICODE
        CString(LPCSTR lpsz, int nLength)
        {
                Init();
                if (nLength != 0)
                {
                        if(AllocBuffer(nLength))
                        {
                                int n = ::MultiByteToWideChar(CP_ACP, 0, lpsz, nLength, m_pchData, nLength + 1);
                                ReleaseBuffer((n >= 0) ? n : -1);
                        }
                }
        }
#else // !_UNICODE
        CString(LPCWSTR lpsz, int nLength)
        {
                Init();
                if (nLength != 0)
                {
                        if(((nLength * 2) > nLength) && AllocBuffer(nLength * 2))
                        {
                                int n = ::WideCharToMultiByte(CP_ACP, 0, lpsz, nLength, m_pchData, (nLength * 2) + 1, NULL, NULL);
                                ReleaseBuffer((n >= 0) ? n : -1);
                        }
                }
        }
#endif // !_UNICODE

        CString(const unsigned char* lpsz)
        {
                Init();
                *this = (LPCSTR)lpsz;
        }

// Attributes & Operations
        int GetLength() const   // as an array of characters
        {
                return GetData()->nDataLength;
        }

        BOOL IsEmpty() const
        {
                return GetData()->nDataLength == 0;
        }

        void Empty()   // free up the data
        {
                if (GetData()->nDataLength == 0)
                        return;

                if (GetData()->nRefs >= 0)
                        Release();
                else
                        *this = _T("");

                ATLASSERT(GetData()->nDataLength == 0);
                ATLASSERT(GetData()->nRefs < 0 || GetData()->nAllocLength == 0);
        }

        TCHAR GetAt(int nIndex) const   // 0 based
        {
                ATLASSERT(nIndex >= 0);
                ATLASSERT(nIndex < GetData()->nDataLength);
                return m_pchData[nIndex];
        }

        TCHAR operator [](int nIndex) const   // same as GetAt
        {
                // same as GetAt
                ATLASSERT(nIndex >= 0);
                ATLASSERT(nIndex < GetData()->nDataLength);
                return m_pchData[nIndex];
        }

        void SetAt(int nIndex, TCHAR ch)
        {
                ATLASSERT(nIndex >= 0);
                ATLASSERT(nIndex < GetData()->nDataLength);

                CopyBeforeWrite();
                m_pchData[nIndex] = ch;
        }

        operator LPCTSTR() const   // as a C string
        {
                return m_pchData;
        }

        // overloaded assignment
        CString& operator =(const CString& stringSrc)
        {
                if (m_pchData != stringSrc.m_pchData)
                {
                        if ((GetData()->nRefs < 0 && GetData() != _atltmpDataNil) || stringSrc.GetData()->nRefs < 0)
                        {
                                // actual copy necessary since one of the strings is locked
                                AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData);
                        }
                        else
                        {
                                // can just copy references around
                                Release();
                                ATLASSERT(stringSrc.GetData() != _atltmpDataNil);
                                m_pchData = stringSrc.m_pchData;
                                InterlockedIncrement(&GetData()->nRefs);
                        }
                }
                return *this;
        }

        CString& operator =(TCHAR ch)
        {
                ATLASSERT(!_istlead(ch));   // can't set single lead byte
                AssignCopy(1, &ch);
                return *this;
        }

#ifdef _UNICODE
        CString& operator =(char ch)
        {
                *this = (TCHAR)ch;
                return *this;
        }
#endif

        CString& operator =(LPCTSTR lpsz)
        {
                ATLASSERT(lpsz == NULL || _IsValidString(lpsz));
                AssignCopy(SafeStrlen(lpsz), lpsz);
                return *this;
        }

#ifdef _UNICODE
        CString& operator =(LPCSTR lpsz)
        {
#if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
                int nSrcLen = (lpsz != NULL) ? ATL::lstrlenA(lpsz) : 0;
#else
                int nSrcLen = (lpsz != NULL) ? lstrlenA(lpsz) : 0;
#endif
                if(AllocBeforeWrite(nSrcLen))
                {
                        _mbstowcsz(m_pchData, lpsz, nSrcLen + 1);
                        ReleaseBuffer();
                }
                return *this;
        }
#else // !_UNICODE
        CString& operator =(LPCWSTR lpsz)
        {
                int nSrcLen = (lpsz != NULL) ? (int)wcslen(lpsz) : 0;
                if(AllocBeforeWrite(nSrcLen * 2))
                {
                        _wcstombsz(m_pchData, lpsz, (nSrcLen * 2) + 1);
                        ReleaseBuffer();
                }
                return *this;
        }
#endif  // !_UNICODE

        CString& operator =(const unsigned char* lpsz)
        {
                *this = (LPCSTR)lpsz;
                return *this;
        }

        // string concatenation
        CString& operator +=(const CString& string)
        {
                ConcatInPlace(string.GetData()->nDataLength, string.m_pchData);
                return *this;
        }

        CString& operator +=(TCHAR ch)
        {
                ConcatInPlace(1, &ch);
                return *this;
        }

#ifdef _UNICODE
        CString& operator +=(char ch)
        {
                *this += (TCHAR)ch;
                return *this;
        }
#endif

        CString& operator +=(LPCTSTR lpsz)
        {
                ATLASSERT(lpsz == NULL || _IsValidString(lpsz));
                ConcatInPlace(SafeStrlen(lpsz), lpsz);
                return *this;
        }

        friend CString __stdcall operator +(const CString& string1, const CString& string2);
        friend CString __stdcall operator +(const CString& string, TCHAR ch);
        friend CString __stdcall operator +(TCHAR ch, const CString& string);
#ifdef _UNICODE
        friend CString __stdcall operator +(const CString& string, char ch);
        friend CString __stdcall operator +(char ch, const CString& string);
#endif
        friend CString __stdcall operator +(const CString& string, LPCTSTR lpsz);
        friend CString __stdcall operator +(LPCTSTR lpsz, const CString& string);

        // string comparison
        int Compare(LPCTSTR lpsz) const   // straight character (MBCS/Unicode aware)
        {
                return _cstrcmp(m_pchData, lpsz);
        }

        int CompareNoCase(LPCTSTR lpsz) const   // ignore case (MBCS/Unicode aware)
        {
                return _cstrcmpi(m_pchData, lpsz);
        }

#ifndef _WIN32_WCE
        // CString::Collate is often slower than Compare but is MBSC/Unicode
        //  aware as well as locale-sensitive with respect to sort order.
        int Collate(LPCTSTR lpsz) const   // NLS aware
        {
                return _cstrcoll(m_pchData, lpsz);
        }

        int CollateNoCase(LPCTSTR lpsz) const   // ignore case
        {
                return _cstrcolli(m_pchData, lpsz);
        }
#endif // !_WIN32_WCE

        // simple sub-string extraction
        CString Mid(int nFirst, int nCount) const
        {
                // out-of-bounds requests return sensible things
                if (nFirst < 0)
                        nFirst = 0;
                if (nCount < 0)
                        nCount = 0;

                if (nFirst + nCount > GetData()->nDataLength)
                        nCount = GetData()->nDataLength - nFirst;
                if (nFirst > GetData()->nDataLength)
                        nCount = 0;

                CString dest;
                AllocCopy(dest, nCount, nFirst, 0);
                return dest;
        }

        CString Mid(int nFirst) const
        {
                return Mid(nFirst, GetData()->nDataLength - nFirst);
        }

        CString Left(int nCount) const
        {
                if (nCount < 0)
                        nCount = 0;
                else if (nCount > GetData()->nDataLength)
                        nCount = GetData()->nDataLength;

                CString dest;
                AllocCopy(dest, nCount, 0, 0);
                return dest;
        }

        CString Right(int nCount) const
        {
                if (nCount < 0)
                        nCount = 0;
                else if (nCount > GetData()->nDataLength)
                        nCount = GetData()->nDataLength;

                CString dest;
                AllocCopy(dest, nCount, GetData()->nDataLength-nCount, 0);
                return dest;
        }

        CString SpanIncluding(LPCTSTR lpszCharSet) const   // strspn equivalent
        {
                ATLASSERT(_IsValidString(lpszCharSet));
                return Left(_cstrspn(m_pchData, lpszCharSet));
        }

        CString SpanExcluding(LPCTSTR lpszCharSet) const   // strcspn equivalent
        {
                ATLASSERT(_IsValidString(lpszCharSet));
                return Left(_cstrcspn(m_pchData, lpszCharSet));
        }

        // upper/lower/reverse conversion
        void MakeUpper()
        {
                CopyBeforeWrite();
                CharUpper(m_pchData);
        }

        void MakeLower()
        {
                CopyBeforeWrite();
                CharLower(m_pchData);
        }

        void MakeReverse()
        {
                CopyBeforeWrite();
                _cstrrev(m_pchData);
        }

        // trimming whitespace (either side)
        void TrimRight()
        {
                CopyBeforeWrite();

                // find beginning of trailing spaces by starting at beginning (DBCS aware)
                LPTSTR lpsz = m_pchData;
                LPTSTR lpszLast = NULL;
                while (*lpsz != _T('\0'))
                {
                        if (_cstrisspace(*lpsz))
                        {
                                if (lpszLast == NULL)
                                        lpszLast = lpsz;
                        }
                        else
                        {
                                lpszLast = NULL;
                        }
                        lpsz = ::CharNext(lpsz);
                }

                if (lpszLast != NULL)
                {
                        // truncate at trailing space start
                        *lpszLast = _T('\0');
                        GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
                }
        }

        void TrimLeft()
        {
                CopyBeforeWrite();

                // find first non-space character
                LPCTSTR lpsz = m_pchData;
                while (_cstrisspace(*lpsz))
                        lpsz = ::CharNext(lpsz);

                // fix up data and length
                int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
                SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
                GetData()->nDataLength = nDataLength;
        }

        // remove continuous occurrences of chTarget starting from right
        void TrimRight(TCHAR chTarget)
        {
                // find beginning of trailing matches
                // by starting at beginning (DBCS aware)

                CopyBeforeWrite();
                LPTSTR lpsz = m_pchData;
                LPTSTR lpszLast = NULL;

                while (*lpsz != _T('\0'))
                {
                        if (*lpsz == chTarget)
                        {
                                if (lpszLast == NULL)
                                        lpszLast = lpsz;
                        }
                        else
                                lpszLast = NULL;
                        lpsz = ::CharNext(lpsz);
                }

                if (lpszLast != NULL)
                {
                        // truncate at left-most matching character
                        *lpszLast = _T('\0');
                        GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
                }
        }

        // remove continuous occcurrences of characters in passed string, starting from right
        void TrimRight(LPCTSTR lpszTargetList)
        {
                // find beginning of trailing matches by starting at beginning (DBCS aware)

                CopyBeforeWrite();
                LPTSTR lpsz = m_pchData;
                LPTSTR lpszLast = NULL;

                while (*lpsz != _T('\0'))
                {
                        TCHAR* pNext = ::CharNext(lpsz);
                        if(pNext > lpsz + 1)
                        {
                                if (_cstrchr_db(lpszTargetList, *lpsz, *(lpsz + 1)) != NULL)
                                {
                                        if (lpszLast == NULL)
                                                lpszLast = lpsz;
                                }
                                else
                                {
                                        lpszLast = NULL;
                                }
                        }
                        else
                        {
                                if (_cstrchr(lpszTargetList, *lpsz) != NULL)
                                {
                                        if (lpszLast == NULL)
                                                lpszLast = lpsz;
                                }
                                else
                                {
                                        lpszLast = NULL;
                                }
                        }

                        lpsz = pNext;
                }

                if (lpszLast != NULL)
                {
                        // truncate at left-most matching character
                        *lpszLast = _T('\0');
                        GetData()->nDataLength = (int)(DWORD_PTR)(lpszLast - m_pchData);
                }
        }

        // remove continuous occurrences of chTarget starting from left
        void TrimLeft(TCHAR chTarget)
        {
                // find first non-matching character

                CopyBeforeWrite();
                LPCTSTR lpsz = m_pchData;

                while (chTarget == *lpsz)
                        lpsz = ::CharNext(lpsz);

                if (lpsz != m_pchData)
                {
                        // fix up data and length
                        int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
                        SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
                        GetData()->nDataLength = nDataLength;
                }
        }

        // remove continuous occcurrences of characters in passed string, starting from left
        void TrimLeft(LPCTSTR lpszTargets)
        {
                // if we're not trimming anything, we're not doing any work
                if (SafeStrlen(lpszTargets) == 0)
                        return;

                CopyBeforeWrite();
                LPCTSTR lpsz = m_pchData;

                while (*lpsz != _T('\0'))
                {
                        TCHAR* pNext = ::CharNext(lpsz);
                        if(pNext > lpsz + 1)
                        {
                                if (_cstrchr_db(lpszTargets, *lpsz, *(lpsz + 1)) == NULL)
                                        break;
                        }
                        else
                        {
                                if (_cstrchr(lpszTargets, *lpsz) == NULL)
                                        break;
                        }
                        lpsz = pNext;
                }

                if (lpsz != m_pchData)
                {
                        // fix up data and length
                        int nDataLength = GetData()->nDataLength - (int)(DWORD_PTR)(lpsz - m_pchData);
                        SecureHelper::memmove_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpsz, (nDataLength + 1) * sizeof(TCHAR));
                        GetData()->nDataLength = nDataLength;
                }
        }

        // advanced manipulation
        // replace occurrences of chOld with chNew
        int Replace(TCHAR chOld, TCHAR chNew)
        {
                int nCount = 0;

                // short-circuit the nop case
                if (chOld != chNew)
                {
                        // otherwise modify each character that matches in the string
                        CopyBeforeWrite();
                        LPTSTR psz = m_pchData;
                        LPTSTR pszEnd = psz + GetData()->nDataLength;
                        while (psz < pszEnd)
                        {
                                // replace instances of the specified character only
                                if (*psz == chOld)
                                {
                                        *psz = chNew;
                                        nCount++;
                                }
                                psz = ::CharNext(psz);
                        }
                }
                return nCount;
        }

        // replace occurrences of substring lpszOld with lpszNew;
        // empty lpszNew removes instances of lpszOld
        int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew)
        {
                // can't have empty or NULL lpszOld

                int nSourceLen = SafeStrlen(lpszOld);
                if (nSourceLen == 0)
                        return 0;
                int nReplacementLen = SafeStrlen(lpszNew);

                // loop once to figure out the size of the result string
                int nCount = 0;
                LPTSTR lpszStart = m_pchData;
                LPTSTR lpszEnd = m_pchData + GetData()->nDataLength;
                LPTSTR lpszTarget = NULL;
                while (lpszStart < lpszEnd)
                {
                        while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL)
                        {
                                nCount++;
                                lpszStart = lpszTarget + nSourceLen;
                        }
                        lpszStart += lstrlen(lpszStart) + 1;
                }

                // if any changes were made, make them
                if (nCount > 0)
                {
                        CopyBeforeWrite();

                        // if the buffer is too small, just allocate a new buffer (slow but sure)
                        int nOldLength = GetData()->nDataLength;
                        int nNewLength =  nOldLength + (nReplacementLen - nSourceLen) * nCount;
                        if (GetData()->nAllocLength < nNewLength || GetData()->nRefs > 1)
                        {
                                CStringData* pOldData = GetData();
                                LPTSTR pstr = m_pchData;
                                if(!AllocBuffer(nNewLength))
                                        return -1;
                                SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, pOldData->nDataLength * sizeof(TCHAR));
                                CString::Release(pOldData);
                        }
                        // else, we just do it in-place
                        lpszStart = m_pchData;
                        lpszEnd = m_pchData + GetData()->nDataLength;

                        // loop again to actually do the work
                        while (lpszStart < lpszEnd)
                        {
                                while ((lpszTarget = (TCHAR*)_cstrstr(lpszStart, lpszOld)) != NULL)
                                {
                                        int nBalance = nOldLength - ((int)(DWORD_PTR)(lpszTarget - m_pchData) + nSourceLen);
                                        int cchBuffLen = GetData()->nAllocLength - (int)(DWORD_PTR)(lpszTarget - m_pchData);
                                        SecureHelper::memmove_x(lpszTarget + nReplacementLen, (cchBuffLen - nReplacementLen + 1) * sizeof(TCHAR), lpszTarget + nSourceLen, nBalance * sizeof(TCHAR));
                                        SecureHelper::memcpy_x(lpszTarget, (cchBuffLen + 1) * sizeof(TCHAR), lpszNew, nReplacementLen * sizeof(TCHAR));
                                        lpszStart = lpszTarget + nReplacementLen;
                                        lpszStart[nBalance] = _T('\0');
                                        nOldLength += (nReplacementLen - nSourceLen);
                                }
                                lpszStart += lstrlen(lpszStart) + 1;
                        }
                        ATLASSERT(m_pchData[nNewLength] == _T('\0'));
                        GetData()->nDataLength = nNewLength;
                }

                return nCount;
        }

        // remove occurrences of chRemove
        int Remove(TCHAR chRemove)
        {
                CopyBeforeWrite();

                LPTSTR pstrSource = m_pchData;
                LPTSTR pstrDest = m_pchData;
                LPTSTR pstrEnd = m_pchData + GetData()->nDataLength;

                while (pstrSource < pstrEnd)
                {
                        if (*pstrSource != chRemove)
                        {
                                *pstrDest = *pstrSource;
                                pstrDest = ::CharNext(pstrDest);
                        }
                        pstrSource = ::CharNext(pstrSource);
                }
                *pstrDest = _T('\0');
                int nCount = (int)(DWORD_PTR)(pstrSource - pstrDest);
                GetData()->nDataLength -= nCount;

                return nCount;
        }

        // insert character at zero-based index; concatenates if index is past end of string
        int Insert(int nIndex, TCHAR ch)
        {
                CopyBeforeWrite();

                if (nIndex < 0)
                        nIndex = 0;

                int nNewLength = GetData()->nDataLength;
                if (nIndex > nNewLength)
                        nIndex = nNewLength;
                nNewLength++;

                if (GetData()->nAllocLength < nNewLength)
                {
                        CStringData* pOldData = GetData();
                        LPTSTR pstr = m_pchData;
                        if(!AllocBuffer(nNewLength))
                                return -1;
                        SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
                        CString::Release(pOldData);
                }

                // move existing bytes down
                SecureHelper::memmove_x(m_pchData + nIndex + 1, (GetData()->nAllocLength - nIndex) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex) * sizeof(TCHAR));
                m_pchData[nIndex] = ch;
                GetData()->nDataLength = nNewLength;

                return nNewLength;
        }

        // insert substring at zero-based index; concatenates if index is past end of string
        int Insert(int nIndex, LPCTSTR pstr)
        {
                if (nIndex < 0)
                        nIndex = 0;

                int nInsertLength = SafeStrlen(pstr);
                int nNewLength = GetData()->nDataLength;
                if (nInsertLength > 0)
                {
                        CopyBeforeWrite();
                        if (nIndex > nNewLength)
                                nIndex = nNewLength;
                        nNewLength += nInsertLength;

                        if (GetData()->nAllocLength < nNewLength)
                        {
                                CStringData* pOldData = GetData();
                                LPTSTR pstr = m_pchData;
                                if(!AllocBuffer(nNewLength))
                                        return -1;
                                SecureHelper::memcpy_x(m_pchData, (nNewLength + 1) * sizeof(TCHAR), pstr, (pOldData->nDataLength + 1) * sizeof(TCHAR));
                                CString::Release(pOldData);
                        }

                        // move existing bytes down
                        SecureHelper::memmove_x(m_pchData + nIndex + nInsertLength, (GetData()->nAllocLength + 1 - nIndex - nInsertLength) * sizeof(TCHAR), m_pchData + nIndex, (nNewLength - nIndex - nInsertLength + 1) * sizeof(TCHAR));
                        SecureHelper::memcpy_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), pstr, nInsertLength * sizeof(TCHAR));
                        GetData()->nDataLength = nNewLength;
                }

                return nNewLength;
        }

        // delete nCount characters starting at zero-based index
        int Delete(int nIndex, int nCount = 1)
        {
                if (nIndex < 0)
                        nIndex = 0;
                int nLength = GetData()->nDataLength;
                if (nCount > 0 && nIndex < nLength)
                {
                        if((nIndex + nCount) > nLength)
                                nCount = nLength - nIndex;
                        CopyBeforeWrite();
                        int nBytesToCopy = nLength - (nIndex + nCount) + 1;

                        SecureHelper::memmove_x(m_pchData + nIndex, (GetData()->nAllocLength + 1 - nIndex) * sizeof(TCHAR), m_pchData + nIndex + nCount, nBytesToCopy * sizeof(TCHAR));
                        nLength -= nCount;
                        GetData()->nDataLength = nLength;
                }

                return nLength;
        }

        // searching (return starting index, or -1 if not found)
        // look for a single character match
        int Find(TCHAR ch) const   // like "C" strchr
        {
                return Find(ch, 0);
        }

        int ReverseFind(TCHAR ch) const
        {
                // find last single character
                LPCTSTR lpsz = _cstrrchr(m_pchData, (_TUCHAR)ch);

                // return -1 if not found, distance from beginning otherwise
                return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
        }

        int Find(TCHAR ch, int nStart) const   // starting at index
        {
                int nLength = GetData()->nDataLength;
                if (nStart < 0 || nStart >= nLength)
                        return -1;

                // find first single character
                LPCTSTR lpsz = _cstrchr(m_pchData + nStart, (_TUCHAR)ch);

                // return -1 if not found and index otherwise
                return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
        }

        int FindOneOf(LPCTSTR lpszCharSet) const
        {
                ATLASSERT(_IsValidString(lpszCharSet));
                LPCTSTR lpsz = _cstrpbrk(m_pchData, lpszCharSet);
                return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
        }

        // look for a specific sub-string
        // find a sub-string (like strstr)
        int Find(LPCTSTR lpszSub) const   // like "C" strstr
        {
                return Find(lpszSub, 0);
        }

        int Find(LPCTSTR lpszSub, int nStart) const   // starting at index
        {
                ATLASSERT(_IsValidString(lpszSub));

                int nLength = GetData()->nDataLength;
                if (nStart < 0 || nStart > nLength)
                        return -1;

                // find first matching substring
                LPCTSTR lpsz = _cstrstr(m_pchData + nStart, lpszSub);

                // return -1 for not found, distance from beginning otherwise
                return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData);
        }

        // Concatentation for non strings
        CString& Append(int n)
        {
                const int cchBuff = 12;
                TCHAR szBuffer[cchBuff] = { 0 };
                SecureHelper::wsprintf_x(szBuffer, cchBuff, _T("%d"), n);
                ConcatInPlace(SafeStrlen(szBuffer), szBuffer);
                return *this;
        }

        // simple formatting
        // formatting (using wsprintf style formatting)
        BOOL __cdecl Format(LPCTSTR lpszFormat, ...)
        {
                ATLASSERT(_IsValidString(lpszFormat));

                va_list argList;
                va_start(argList, lpszFormat);
                BOOL bRet = FormatV(lpszFormat, argList);
                va_end(argList);
                return bRet;
        }

        BOOL __cdecl Format(UINT nFormatID, ...)
        {
                CString strFormat;
                BOOL bRet = strFormat.LoadString(nFormatID);
                ATLASSERT(bRet != 0);

                va_list argList;
                va_start(argList, nFormatID);
                bRet = FormatV(strFormat, argList);
                va_end(argList);
                return bRet;
        }

        BOOL FormatV(LPCTSTR lpszFormat, va_list argList)
        {
                ATLASSERT(_IsValidString(lpszFormat));

                enum _FormatModifiers
                {
                        FORCE_ANSI =    0x10000,
                        FORCE_UNICODE = 0x20000,
                        FORCE_INT64 =   0x40000
                };

                va_list argListSave = argList;

                // make a guess at the maximum length of the resulting string
                int nMaxLen = 0;
                for (LPCTSTR lpsz = lpszFormat; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz))
                {
                        // handle '%' character, but watch out for '%%'
                        if (*lpsz != _T('%') || *(lpsz = ::CharNext(lpsz)) == _T('%'))
                        {
                                nMaxLen += (int)(::CharNext(lpsz) - lpsz);
                                continue;
                        }

                        int nItemLen = 0;

                        // handle '%' character with format
                        int nWidth = 0;
                        for (; *lpsz != _T('\0'); lpsz = ::CharNext(lpsz))
                        {
                                // check for valid flags
                                if (*lpsz == _T('#'))
                                        nMaxLen += 2;   // for '0x'
                                else if (*lpsz == _T('*'))
                                        nWidth = va_arg(argList, int);
                                else if (*lpsz == _T('-') || *lpsz == _T('+') || *lpsz == _T('0') || *lpsz == _T(' '))
                                        ;
                                else // hit non-flag character
                                        break;
                        }
                        // get width and skip it
                        if (nWidth == 0)
                        {
                                // width indicated by
                                nWidth = _cstrtoi(lpsz);
                                for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
                                        ;
                        }
                        ATLASSERT(nWidth >= 0);

                        int nPrecision = 0;
                        if (*lpsz == _T('.'))
                        {
                                // skip past '.' separator (width.precision)
                                lpsz = ::CharNext(lpsz);

                                // get precision and skip it
                                if (*lpsz == _T('*'))
                                {
                                        nPrecision = va_arg(argList, int);
                                        lpsz = ::CharNext(lpsz);
                                }
                                else
                                {
                                        nPrecision = _cstrtoi(lpsz);
                                        for (; *lpsz != _T('\0') && _cstrisdigit(*lpsz); lpsz = ::CharNext(lpsz))
                                                ;
                                }
                                ATLASSERT(nPrecision >= 0);
                        }

                        // should be on type modifier or specifier
                        int nModifier = 0;
                        if(lpsz[0] == _T('I') && lpsz[1] == _T('6') && lpsz[2] == _T('4'))
                        {
                                lpsz += 3;
                                nModifier = FORCE_INT64;
                        }
                        else
                        {
                                switch (*lpsz)
                                {
                                // modifiers that affect size
                                case _T('h'):
                                        nModifier = FORCE_ANSI;
                                        lpsz = ::CharNext(lpsz);
                                        break;
                                case _T('l'):
                                        nModifier = FORCE_UNICODE;
                                        lpsz = ::CharNext(lpsz);
                                        break;

                                // modifiers that do not affect size
                                case _T('F'):
                                case _T('N'):
                                case _T('L'):
                                        lpsz = ::CharNext(lpsz);
                                        break;
                                }
                        }

                        // now should be on specifier
                        switch (*lpsz | nModifier)
                        {
                        // single characters
                        case _T('c'):
                        case _T('C'):
                                nItemLen = 2;
                                va_arg(argList, TCHAR);
                                break;
                        case _T('c') | FORCE_ANSI:
                        case _T('C') | FORCE_ANSI:
                                nItemLen = 2;
                                va_arg(argList, char);
                                break;
                        case _T('c') | FORCE_UNICODE:
                        case _T('C') | FORCE_UNICODE:
                                nItemLen = 2;
                                va_arg(argList, WCHAR);
                                break;

                        // strings
                        case _T('s'):
                        {
                                LPCTSTR pstrNextArg = va_arg(argList, LPCTSTR);
                                if (pstrNextArg == NULL)
                                {
                                        nItemLen = 6;  // "(null)"
                                }
                                else
                                {
                                        nItemLen = lstrlen(pstrNextArg);
                                        nItemLen = __max(1, nItemLen);
                                }
                                break;
                        }

                        case _T('S'):
                        {
#ifndef _UNICODE
                                LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
                                if (pstrNextArg == NULL)
                                {
                                        nItemLen = 6;  // "(null)"
                                }
                                else
                                {
                                        nItemLen = (int)wcslen(pstrNextArg);
                                        nItemLen = __max(1, nItemLen);
                                }
#else // _UNICODE
                                LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
                                if (pstrNextArg == NULL)
                                {
                                        nItemLen = 6; // "(null)"
                                }
                                else
                                {
#if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
                                        nItemLen = ATL::lstrlenA(pstrNextArg);
#else
                                        nItemLen = lstrlenA(pstrNextArg);
#endif
                                        nItemLen = __max(1, nItemLen);
                                }
#endif // _UNICODE
                                break;
                        }

                        case _T('s') | FORCE_ANSI:
                        case _T('S') | FORCE_ANSI:
                        {
                                LPCSTR pstrNextArg = va_arg(argList, LPCSTR);
                                if (pstrNextArg == NULL)
                                {
                                        nItemLen = 6; // "(null)"
                                }
                                else
                                {
#if defined(_WIN32_WCE) && (_ATL_VER >= 0x0800)
                                        nItemLen = ATL::lstrlenA(pstrNextArg);
#else
                                        nItemLen = lstrlenA(pstrNextArg);
#endif
                                        nItemLen = __max(1, nItemLen);
                                }
                                break;
                        }

                        case _T('s') | FORCE_UNICODE:
                        case _T('S') | FORCE_UNICODE:
                        {
                                LPWSTR pstrNextArg = va_arg(argList, LPWSTR);
                                if (pstrNextArg == NULL)
                                {
                                        nItemLen = 6; // "(null)"
                                }
                                else
                                {
                                        nItemLen = (int)wcslen(pstrNextArg);
                                        nItemLen = __max(1, nItemLen);
                                }
                                break;
                        }
                        }

                        // adjust nItemLen for strings
                        if (nItemLen != 0)
                        {
                                nItemLen = __max(nItemLen, nWidth);
                                if (nPrecision != 0)
                                        nItemLen = __min(nItemLen, nPrecision);
                        }
                        else
                        {
                                switch (*lpsz)
                                {
                                // integers
                                case _T('d'):
                                case _T('i'):
                                case _T('u'):
                                case _T('x'):
                                case _T('X'):
                                case _T('o'):
                                        if (nModifier & FORCE_INT64)
                                                va_arg(argList, __int64);
                                        else
                                                va_arg(argList, int);
                                        nItemLen = 32;
                                        nItemLen = __max(nItemLen, nWidth + nPrecision);
                                        break;

#ifndef _ATL_USE_CSTRING_FLOAT
                                case _T('e'):
                                case _T('E'):
                                case _T('f'):
                                case _T('g'):
                                case _T('G'):
                                        ATLASSERT(!"Floating point (%%e, %%E, %%f, %%g, and %%G) is not supported by the WTL::CString class.");
#ifndef _DEBUG
                                        ::OutputDebugString(_T("Floating point (%%e, %%f, %%g, and %%G) is not supported by the WTL::CString class."));
#ifndef _WIN32_WCE
                                        ::DebugBreak();
#else // CE specific
                                        DebugBreak();
#endif // _WIN32_WCE
#endif // !_DEBUG
                                        break;
#else // _ATL_USE_CSTRING_FLOAT
                                case _T('e'):
                                case _T('E'):
                                case _T('g'):
                                case _T('G'):
                                        va_arg(argList, double);
                                        nItemLen = 128;
                                        nItemLen = __max(nItemLen, nWidth + nPrecision);
                                        break;
                                case _T('f'):
                                        {
                                                double f = va_arg(argList, double);
                                                // 312 == strlen("-1+(309 zeroes).")
                                                // 309 zeroes == max precision of a double
                                                // 6 == adjustment in case precision is not specified,
                                                //   which means that the precision defaults to 6
                                                int cchLen = __max(nWidth, 312 + nPrecision + 6);
                                                CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
                                                LPTSTR pszTemp = buff.Allocate(cchLen);
                                                if(pszTemp != NULL)
                                                {
                                                        SecureHelper::sprintf_x(pszTemp, cchLen, _T("%*.*f"), nWidth, nPrecision + 6, f);
                                                        nItemLen = (int)_tcslen(pszTemp);
                                                }
                                                else
                                                {
                                                        nItemLen = cchLen;
                                                }
                                        }
                                        break;
#endif // _ATL_USE_CSTRING_FLOAT

                                case _T('p'):
                                        va_arg(argList, void*);
                                        nItemLen = 32;
                                        nItemLen = __max(nItemLen, nWidth + nPrecision);
                                        break;

                                // no output
                                case _T('n'):
                                        va_arg(argList, int*);
                                        break;

                                default:
                                        ATLASSERT(FALSE);  // unknown formatting option
                                }
                        }

                        // adjust nMaxLen for output nItemLen
                        nMaxLen += nItemLen;
                }

                if(GetBuffer(nMaxLen) == NULL)
                        return FALSE;
#ifndef _ATL_USE_CSTRING_FLOAT
                int nRet = SecureHelper::wvsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave);
#else // _ATL_USE_CSTRING_FLOAT
                int nRet = SecureHelper::vsprintf_x(m_pchData, GetAllocLength() + 1, lpszFormat, argListSave);
#endif // _ATL_USE_CSTRING_FLOAT
                nRet;   // ref
                ATLASSERT(nRet <= GetAllocLength());
                ReleaseBuffer();

                va_end(argListSave);
                return TRUE;
        }

        // formatting for localization (uses FormatMessage API)
        // formatting (using FormatMessage style formatting)
        BOOL __cdecl FormatMessage(LPCTSTR lpszFormat, ...)
        {
                // format message into temporary buffer lpszTemp
                va_list argList;
                va_start(argList, lpszFormat);
                LPTSTR lpszTemp;
                BOOL bRet = TRUE;

                if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                lpszFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
                        bRet = FALSE;

                // assign lpszTemp into the resulting string and free the temporary
                *this = lpszTemp;
                LocalFree(lpszTemp);
                va_end(argList);
                return bRet;
        }

        BOOL __cdecl FormatMessage(UINT nFormatID, ...)
        {
                // get format string from string table
                CString strFormat;
                BOOL bRetTmp = strFormat.LoadString(nFormatID);
                bRetTmp;   // ref
                ATLASSERT(bRetTmp != 0);

                // format message into temporary buffer lpszTemp
                va_list argList;
                va_start(argList, nFormatID);
                LPTSTR lpszTemp;
                BOOL bRet = TRUE;

                if (::FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                                strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &argList) == 0 || lpszTemp == NULL)
                        bRet = FALSE;

                // assign lpszTemp into the resulting string and free lpszTemp
                *this = lpszTemp;
                LocalFree(lpszTemp);
                va_end(argList);
                return bRet;
        }

        // Windows support
        BOOL LoadString(UINT nID)   // load from string resource (255 chars max.)
        {
#ifdef _UNICODE
                const int CHAR_FUDGE = 1;   // one TCHAR unused is good enough
#else
                const int CHAR_FUDGE = 2;   // two BYTES unused for case of DBC last char
#endif

                // try fixed buffer first (to avoid wasting space in the heap)
                TCHAR szTemp[256];
                int nCount =  sizeof(szTemp) / sizeof(szTemp[0]);
                int nLen = _LoadString(nID, szTemp, nCount);
                if (nCount - nLen > CHAR_FUDGE)
                {
                        *this = szTemp;
                        return (nLen > 0);
                }

                // try buffer size of 512, then larger size until entire string is retrieved
                int nSize = 256;
                do
                {
                        nSize += 256;
                        LPTSTR lpstr = GetBuffer(nSize - 1);
                        if(lpstr == NULL)
                        {
                                nLen = 0;
                                break;
                        }
                        nLen = _LoadString(nID, lpstr, nSize);
                } while (nSize - nLen <= CHAR_FUDGE);
                ReleaseBuffer();

                return (nLen > 0);
        }

#ifndef _UNICODE
        // ANSI <-> OEM support (convert string in place)
        void AnsiToOem()
        {
                CopyBeforeWrite();
                ::AnsiToOem(m_pchData, m_pchData);
        }

        void OemToAnsi()
        {
                CopyBeforeWrite();
                ::OemToAnsi(m_pchData, m_pchData);
        }
#endif

#ifndef _ATL_NO_COM
        // OLE BSTR support (use for OLE automation)
        BSTR AllocSysString() const
        {
#if defined(_UNICODE) || defined(OLE2ANSI)
                BSTR bstr = ::SysAllocStringLen(m_pchData, GetData()->nDataLength);
#else
                int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
                        GetData()->nDataLength, NULL, NULL);
                BSTR bstr = ::SysAllocStringLen(NULL, nLen);
                if(bstr != NULL)
                        MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, bstr, nLen);
#endif
                return bstr;
        }

        BSTR SetSysString(BSTR* pbstr) const
        {
#if defined(_UNICODE) || defined(OLE2ANSI)
                ::SysReAllocStringLen(pbstr, m_pchData, GetData()->nDataLength);
#else
                int nLen = MultiByteToWideChar(CP_ACP, 0, m_pchData,
                        GetData()->nDataLength, NULL, NULL);
                if(::SysReAllocStringLen(pbstr, NULL, nLen))
                        MultiByteToWideChar(CP_ACP, 0, m_pchData, GetData()->nDataLength, *pbstr, nLen);
#endif
                ATLASSERT(*pbstr != NULL);
                return *pbstr;
        }
#endif // !_ATL_NO_COM

        // Access to string implementation buffer as "C" character array
        LPTSTR GetBuffer(int nMinBufLength)
        {
                ATLASSERT(nMinBufLength >= 0);

                if (GetData()->nRefs > 1 || nMinBufLength > GetData()->nAllocLength)
                {
                        // we have to grow the buffer
                        CStringData* pOldData = GetData();
                        int nOldLen = GetData()->nDataLength;   // AllocBuffer will tromp it
                        if (nMinBufLength < nOldLen)
                                nMinBufLength = nOldLen;

                        if(!AllocBuffer(nMinBufLength))
                                return NULL;

                        SecureHelper::memcpy_x(m_pchData, (nMinBufLength + 1) * sizeof(TCHAR), pOldData->data(), (nOldLen + 1) * sizeof(TCHAR));
                        GetData()->nDataLength = nOldLen;
                        CString::Release(pOldData);
                }
                ATLASSERT(GetData()->nRefs <= 1);

                // return a pointer to the character storage for this string
                ATLASSERT(m_pchData != NULL);
                return m_pchData;
        }

        void ReleaseBuffer(int nNewLength = -1)
        {
                CopyBeforeWrite();   // just in case GetBuffer was not called

                if (nNewLength == -1)
                        nNewLength = lstrlen(m_pchData);   // zero terminated

                ATLASSERT(nNewLength <= GetData()->nAllocLength);
                GetData()->nDataLength = nNewLength;
                m_pchData[nNewLength] = _T('\0');
        }

        LPTSTR GetBufferSetLength(int nNewLength)
        {
                ATLASSERT(nNewLength >= 0);

                if(GetBuffer(nNewLength) == NULL)
                        return NULL;

                GetData()->nDataLength = nNewLength;
                m_pchData[nNewLength] = _T('\0');
                return m_pchData;
        }

        void FreeExtra()
        {
                ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
                if (GetData()->nDataLength != GetData()->nAllocLength)
                {
                        CStringData* pOldData = GetData();
                        if(AllocBuffer(GetData()->nDataLength))
                        {
                                SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pOldData->data(), pOldData->nDataLength * sizeof(TCHAR));
                                ATLASSERT(m_pchData[GetData()->nDataLength] == _T('\0'));
                                CString::Release(pOldData);
                        }
                }
                ATLASSERT(GetData() != NULL);
        }

        // Use LockBuffer/UnlockBuffer to turn refcounting off
        LPTSTR LockBuffer()
        {
                LPTSTR lpsz = GetBuffer(0);
                if(lpsz != NULL)
                        GetData()->nRefs = -1;
                return lpsz;
        }

        void UnlockBuffer()
        {
                ATLASSERT(GetData()->nRefs == -1);
                if (GetData() != _atltmpDataNil)
                        GetData()->nRefs = 1;
        }

// Implementation
public:
        ~CString()   //  free any attached data
        {
                if (GetData() != _atltmpDataNil)
                {
                        if (InterlockedDecrement(&GetData()->nRefs) <= 0)
                                delete[] (BYTE*)GetData();
                }
        }

        int GetAllocLength() const
        {
                return GetData()->nAllocLength;
        }

        static BOOL __stdcall _IsValidString(LPCTSTR lpsz, int /*nLength*/ = -1)
        {
                return (lpsz != NULL) ? TRUE : FALSE;
        }

protected:
        LPTSTR m_pchData;   // pointer to ref counted string data

        // implementation helpers
        CStringData* GetData() const
        {
                ATLASSERT(m_pchData != NULL);
                return ((CStringData*)m_pchData) - 1;
        }

        void Init()
        {
                m_pchData = _GetEmptyString().m_pchData;
        }

        BOOL AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const
        {
                // will clone the data attached to this string
                // allocating 'nExtraLen' characters
                // Places results in uninitialized string 'dest'
                // Will copy the part or all of original data to start of new string

                BOOL bRet = FALSE;
                int nNewLen = nCopyLen + nExtraLen;
                if (nNewLen == 0)
                {
                        dest.Init();
                        bRet = TRUE;
                }
                else if(nNewLen >= nCopyLen)
                {
                        if(dest.AllocBuffer(nNewLen))
                        {
                                SecureHelper::memcpy_x(dest.m_pchData, (nNewLen + 1) * sizeof(TCHAR), m_pchData + nCopyIndex, nCopyLen * sizeof(TCHAR));
                                bRet = TRUE;
                        }
                }

                return bRet;
        }

        // always allocate one extra character for '\0' termination
        // assumes [optimistically] that data length will equal allocation length
        BOOL AllocBuffer(int nLen)
        {
                ATLASSERT(nLen >= 0);
                ATLASSERT(nLen <= INT_MAX - 1);   // max size (enough room for 1 extra)

                if (nLen == 0)
                {
                        Init();
                }
                else
                {
                        CStringData* pData = NULL;
                        ATLTRY(pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen + 1) * sizeof(TCHAR)]);
                        if(pData == NULL)
                                return FALSE;

                        pData->nRefs = 1;
                        pData->data()[nLen] = _T('\0');
                        pData->nDataLength = nLen;
                        pData->nAllocLength = nLen;
                        m_pchData = pData->data();
                }

                return TRUE;
        }

        // Assignment operators
        //  All assign a new value to the string
        //      (a) first see if the buffer is big enough
        //      (b) if enough room, copy on top of old buffer, set size and type
        //      (c) otherwise free old string data, and create a new one
        //
        //  All routines return the new string (but as a 'const CString&' so that
        //      assigning it again will cause a copy, eg: s1 = s2 = "hi there".
        //
        void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData)
        {
                if(AllocBeforeWrite(nSrcLen))
                {
                        SecureHelper::memcpy_x(m_pchData, (nSrcLen + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR));
                        GetData()->nDataLength = nSrcLen;
                        m_pchData[nSrcLen] = _T('\0');
                }
        }

        // Concatenation
        // NOTE: "operator +" is done as friend functions for simplicity
        //      There are three variants:
        //          CString + CString
        // and for ? = TCHAR, LPCTSTR
        //          CString + ?
        //          ? + CString
        BOOL ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data)
        {
                // -- master concatenation routine
                // Concatenate two sources
                // -- assume that 'this' is a new CString object

                BOOL bRet = TRUE;
                int nNewLen = nSrc1Len + nSrc2Len;
                if(nNewLen < nSrc1Len || nNewLen < nSrc2Len)
                {
                        bRet = FALSE;
                }
                else if(nNewLen != 0)
                {
                        bRet = AllocBuffer(nNewLen);
                        if (bRet)
                        {
                                SecureHelper::memcpy_x(m_pchData, (nNewLen + 1) * sizeof(TCHAR), lpszSrc1Data, nSrc1Len * sizeof(TCHAR));
                                SecureHelper::memcpy_x(m_pchData + nSrc1Len, (nNewLen + 1 - nSrc1Len) * sizeof(TCHAR), lpszSrc2Data, nSrc2Len * sizeof(TCHAR));
                        }
                }
                return bRet;
        }

        void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData)
        {
                //  -- the main routine for += operators

                // concatenating an empty string is a no-op!
                if (nSrcLen == 0)
                        return;

                // if the buffer is too small, or we have a width mis-match, just
                //   allocate a new buffer (slow but sure)
                if (GetData()->nRefs > 1 || GetData()->nDataLength + nSrcLen > GetData()->nAllocLength)
                {
                        // we have to grow the buffer, use the ConcatCopy routine
                        CStringData* pOldData = GetData();
                        if (ConcatCopy(GetData()->nDataLength, m_pchData, nSrcLen, lpszSrcData))
                        {
                                ATLASSERT(pOldData != NULL);
                                CString::Release(pOldData);
                        }
                }
                else
                {
                        // fast concatenation when buffer big enough
                        SecureHelper::memcpy_x(m_pchData + GetData()->nDataLength, (GetData()->nAllocLength + 1) * sizeof(TCHAR), lpszSrcData, nSrcLen * sizeof(TCHAR));
                        GetData()->nDataLength += nSrcLen;
                        ATLASSERT(GetData()->nDataLength <= GetData()->nAllocLength);
                        m_pchData[GetData()->nDataLength] = _T('\0');
                }
        }

        void CopyBeforeWrite()
        {
                if (GetData()->nRefs > 1)
                {
                        CStringData* pData = GetData();
                        Release();
                        if(AllocBuffer(pData->nDataLength))
                                SecureHelper::memcpy_x(m_pchData, (GetData()->nAllocLength + 1) * sizeof(TCHAR), pData->data(), (pData->nDataLength + 1) * sizeof(TCHAR));
                }
                ATLASSERT(GetData()->nRefs <= 1);
        }

        BOOL AllocBeforeWrite(int nLen)
        {
                BOOL bRet = TRUE;
                if (GetData()->nRefs > 1 || nLen > GetData()->nAllocLength)
                {
                        Release();
                        bRet = AllocBuffer(nLen);
                }
                ATLASSERT(GetData()->nRefs <= 1);
                return bRet;
        }

        void Release()
        {
                if (GetData() != _atltmpDataNil)
                {
                        ATLASSERT(GetData()->nRefs != 0);
                        if (InterlockedDecrement(&GetData()->nRefs) <= 0)
                                delete[] (BYTE*)GetData();
                        Init();
                }
        }

        static void PASCAL Release(CStringData* pData)
        {
                if (pData != _atltmpDataNil)
                {
                        ATLASSERT(pData->nRefs != 0);
                        if (InterlockedDecrement(&pData->nRefs) <= 0)
                                delete[] (BYTE*)pData;
                }
        }

        static int PASCAL SafeStrlen(LPCTSTR lpsz)
        {
                return (lpsz == NULL) ? 0 : lstrlen(lpsz);
        }

        static int __stdcall _LoadString(UINT nID, LPTSTR lpszBuf, UINT nMaxBuf)
        {
#ifdef _DEBUG
                // LoadString without annoying warning from the Debug kernel if the
                //  segment containing the string is not present
                if (::FindResource(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE((nID >> 4) + 1), RT_STRING) == NULL)
                {
                        lpszBuf[0] = _T('\0');
                        return 0;   // not found
                }
#endif // _DEBUG

                int nLen = ::LoadString(ModuleHelper::GetResourceInstance(), nID, lpszBuf, nMaxBuf);
                if (nLen == 0)
                        lpszBuf[0] = _T('\0');

                return nLen;
        }

        static const CString& __stdcall _GetEmptyString()
        {
                return *(CString*)&_atltmpPchNil;
        }

// CString conversion helpers
        static int __cdecl _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count)
        {
                if (count == 0 && mbstr != NULL)
                        return 0;

                int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, mbstr, (int)count, NULL, NULL);
                ATLASSERT(mbstr == NULL || result <= (int)count);
                if (result > 0)
                        mbstr[result - 1] = 0;
                return result;
        }

        static int __cdecl _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count)
        {
                if (count == 0 && wcstr != NULL)
                        return 0;

                int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, wcstr, (int)count);
                ATLASSERT(wcstr == NULL || result <= (int)count);
                if (result > 0)
                        wcstr[result - 1] = 0;
                return result;
        }

// Helpers to avoid CRT startup code
#ifdef _ATL_MIN_CRT
        static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
        {
                // strchr for '\0' should succeed
                while (*p != 0)
                {
                        if (*p == ch)
                                break;
                        p = ::CharNext(p);
                }
                return (*p == ch) ? p : NULL;
        }

        static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
        {
                const TCHAR* lpsz = NULL;
                while (*p != 0)
                {
                        if (*p == ch)
                                lpsz = p;
                        p = ::CharNext(p);
                }
                return lpsz;
        }

        static TCHAR* _cstrrev(TCHAR* pStr)
        {
                // optimize NULL, zero-length, and single-char case
                if ((pStr == NULL) || (pStr[0] == _T('\0')) || (pStr[1] == _T('\0')))
                        return pStr;

                TCHAR* p = pStr;

                while (*p != 0) 
                {
                        TCHAR* pNext = ::CharNext(p);
                        if(pNext > p + 1)
                        {
                                char p1 = *(char*)p;
                                *(char*)p = *(char*)(p + 1);
                                *(char*)(p + 1) = p1;
                        }
                        p = pNext;
                }

                p--;
                TCHAR* q = pStr;

                while (q < p)
                {
                        TCHAR t = *q;
                        *q = *p;
                        *p = t;
                        q++;
                        p--;
                }
                return pStr;
        }

        static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
        {
                int nLen = lstrlen(pCharSet);
                if (nLen == 0)
                        return (TCHAR*)pStr;

                const TCHAR* pRet = NULL;
                const TCHAR* pCur = pStr;
                while((pCur = _cstrchr(pCur, *pCharSet)) != NULL)
                {
                        if(memcmp(pCur, pCharSet, nLen * sizeof(TCHAR)) == 0)
                        {
                                pRet = pCur;
                                break;
                        }
                        pCur = ::CharNext(pCur);
                }
                return pRet;
        }

        static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
        {
                int nRet = 0;
                const TCHAR* p = pStr;
                while (*p != 0)
                {
                        const TCHAR* pNext = ::CharNext(p);
                        if(pNext > p + 1)
                        {
                                if(_cstrchr_db(pCharSet, *p, *(p + 1)) == NULL)
                                        break;
                                nRet += 2;
                        }
                        else
                        {
                                if(_cstrchr(pCharSet, *p) == NULL)
                                        break;
                                nRet++;
                        }
                        p = pNext;
                }
                return nRet;
        }

        static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
        {
                int nRet = 0;
                TCHAR* p = (TCHAR*)pStr;
                while (*p != 0)
                {
                        TCHAR* pNext = ::CharNext(p);
                        if(pNext > p + 1)
                        {
                                if(_cstrchr_db(pCharSet, *p, *(p + 1)) != NULL)
                                        break;
                                nRet += 2;
                        }
                        else
                        {
                                if(_cstrchr(pCharSet, *p) != NULL)
                                        break;
                                nRet++;
                        }
                        p = pNext;
                }
                return nRet;
        }

        static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
        {
                int n = _cstrcspn(p, lpszCharSet);
                return (p[n] != 0) ? &p[n] : NULL;
        }

        static int _cstrisdigit(TCHAR ch)
        {
                WORD type;
                GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
                return (type & C1_DIGIT) == C1_DIGIT;
        }

        static int _cstrisspace(TCHAR ch)
        {
                WORD type;
                GetStringTypeEx(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
                return (type & C1_SPACE) == C1_SPACE;
        }

        static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                return lstrcmp(pstrOne, pstrOther);
        }

        static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                return lstrcmpi(pstrOne, pstrOther);
        }

        static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                int nRet = CompareString(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1);
                ATLASSERT(nRet != 0);
                return nRet - 2;   // convert to strcmp convention
        }

        static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                int nRet = CompareString(GetThreadLocale(), NORM_IGNORECASE, pstrOne, -1, pstrOther, -1);
                ATLASSERT(nRet != 0);
                return nRet - 2;   // convert to strcmp convention
        }

        static int _cstrtoi(const TCHAR* nptr)
        {
                int c;       // current char
                int total;   // current total
                int sign;    // if '-', then negative, otherwise positive

                while (_cstrisspace(*nptr))
                        ++nptr;

                c = (int)(_TUCHAR)*nptr++;
                sign = c;   // save sign indication
                if (c == _T('-') || c == _T('+'))
                        c = (int)(_TUCHAR)*nptr++;   // skip sign

                total = 0;

                while (_cstrisdigit((TCHAR)c))
                {
                        total = 10 * total + (c - '0');   // accumulate digit
                        c = (int)(_TUCHAR)*nptr++;        // get next char
                }

                if (sign == '-')
                        return -total;
                else
                        return total;   // return result, negated if necessary
        }
#else // !_ATL_MIN_CRT
        static const TCHAR* _cstrchr(const TCHAR* p, TCHAR ch)
        {
                return _tcschr(p, ch);
        }

        static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
        {
                return _tcsrchr(p, ch);
        }

        static TCHAR* _cstrrev(TCHAR* pStr)
        {
                return _tcsrev(pStr);
        }

        static const TCHAR* _cstrstr(const TCHAR* pStr, const TCHAR* pCharSet)
        {
                return _tcsstr(pStr, pCharSet);
        }

        static int _cstrspn(const TCHAR* pStr, const TCHAR* pCharSet)
        {
                return (int)_tcsspn(pStr, pCharSet);
        }

        static int _cstrcspn(const TCHAR* pStr, const TCHAR* pCharSet)
        {
                return (int)_tcscspn(pStr, pCharSet);
        }

        static const TCHAR* _cstrpbrk(const TCHAR* p, const TCHAR* lpszCharSet)
        {
                return _tcspbrk(p, lpszCharSet);
        }

        static int _cstrisdigit(TCHAR ch)
        {
                return _istdigit(ch);
        }

        static int _cstrisspace(TCHAR ch)
        {
                return _istspace((_TUCHAR)ch);
        }

        static int _cstrcmp(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                return _tcscmp(pstrOne, pstrOther);
        }

        static int _cstrcmpi(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                return _tcsicmp(pstrOne, pstrOther);
        }

#ifndef _WIN32_WCE
        static int _cstrcoll(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                return _tcscoll(pstrOne, pstrOther);
        }

        static int _cstrcolli(const TCHAR* pstrOne, const TCHAR* pstrOther)
        {
                return _tcsicoll(pstrOne, pstrOther);
        }
#endif // !_WIN32_WCE

        static int _cstrtoi(const TCHAR* nptr)
        {
                return _ttoi(nptr);
        }
#endif // !_ATL_MIN_CRT

        static const TCHAR* _cstrchr_db(const TCHAR* p, TCHAR ch1, TCHAR ch2)
        {
                const TCHAR* lpsz = NULL;
                while (*p != 0)
                {
                        if (*p == ch1 && *(p + 1) == ch2)
                        {
                                lpsz = p;
                                break;
                        }
                        p = ::CharNext(p);
                }
                return lpsz;
        }
};


// Compare helpers

inline bool __stdcall operator ==(const CString& s1, const CString& s2)
{ return s1.Compare(s2) == 0; }

inline bool __stdcall operator ==(const CString& s1, LPCTSTR s2)
{ return s1.Compare(s2) == 0; }

inline bool __stdcall operator ==(LPCTSTR s1, const CString& s2)
{ return s2.Compare(s1) == 0; }

inline bool __stdcall operator !=(const CString& s1, const CString& s2)
{ return s1.Compare(s2) != 0; }

inline bool __stdcall operator !=(const CString& s1, LPCTSTR s2)
{ return s1.Compare(s2) != 0; }

inline bool __stdcall operator !=(LPCTSTR s1, const CString& s2)
{ return s2.Compare(s1) != 0; }

inline bool __stdcall operator <(const CString& s1, const CString& s2)
{ return s1.Compare(s2) < 0; }

inline bool __stdcall operator <(const CString& s1, LPCTSTR s2)
{ return s1.Compare(s2) < 0; }

inline bool __stdcall operator <(LPCTSTR s1, const CString& s2)
{ return s2.Compare(s1) > 0; }

inline bool __stdcall operator >(const CString& s1, const CString& s2)
{ return s1.Compare(s2) > 0; }

inline bool __stdcall operator >(const CString& s1, LPCTSTR s2)
{ return s1.Compare(s2) > 0; }

inline bool __stdcall operator >(LPCTSTR s1, const CString& s2)
{ return s2.Compare(s1) < 0; }

inline bool __stdcall operator <=(const CString& s1, const CString& s2)
{ return s1.Compare(s2) <= 0; }

inline bool __stdcall operator <=(const CString& s1, LPCTSTR s2)
{ return s1.Compare(s2) <= 0; }

inline bool __stdcall operator <=(LPCTSTR s1, const CString& s2)
{ return s2.Compare(s1) >= 0; }

inline bool __stdcall operator >=(const CString& s1, const CString& s2)
{ return s1.Compare(s2) >= 0; }

inline bool __stdcall operator >=(const CString& s1, LPCTSTR s2)
{ return s1.Compare(s2) >= 0; }

inline bool __stdcall operator >=(LPCTSTR s1, const CString& s2)
{ return s2.Compare(s1) <= 0; }


// CString "operator +" functions

inline CString __stdcall operator +(const CString& string1, const CString& string2)
{
        CString s;
        s.ConcatCopy(string1.GetData()->nDataLength, string1.m_pchData, string2.GetData()->nDataLength, string2.m_pchData);
        return s;
}

inline CString __stdcall operator +(const CString& string, TCHAR ch)
{
        CString s;
        s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, 1, &ch);
        return s;
}

inline CString __stdcall operator +(TCHAR ch, const CString& string)
{
        CString s;
        s.ConcatCopy(1, &ch, string.GetData()->nDataLength, string.m_pchData);
        return s;
}

#ifdef _UNICODE
inline CString __stdcall operator +(const CString& string, char ch)
{
        return string + (TCHAR)ch;
}

inline CString __stdcall operator +(char ch, const CString& string)
{
        return (TCHAR)ch + string;
}
#endif // _UNICODE

inline CString __stdcall operator +(const CString& string, LPCTSTR lpsz)
{
        ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz));
        CString s;
        s.ConcatCopy(string.GetData()->nDataLength, string.m_pchData, CString::SafeStrlen(lpsz), lpsz);
        return s;
}

inline CString __stdcall operator +(LPCTSTR lpsz, const CString& string)
{
        ATLASSERT(lpsz == NULL || CString::_IsValidString(lpsz));
        CString s;
        s.ConcatCopy(CString::SafeStrlen(lpsz), lpsz, string.GetData()->nDataLength, string.m_pchData);
        return s;
}

#endif // !_WTL_NO_CSTRING


///////////////////////////////////////////////////////////////////////////////
// CRecentDocumentList - MRU List Support

#ifndef _WIN32_WCE

#ifndef _WTL_MRUEMPTY_TEXT
  #define _WTL_MRUEMPTY_TEXT    _T("(empty)")
#endif

// forward declaration
inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen);

template <class T, int t_cchItemLen = MAX_PATH, int t_nFirstID = ID_FILE_MRU_FIRST, int t_nLastID = ID_FILE_MRU_LAST>
class CRecentDocumentListBase
{
public:
// Declarations
        struct _DocEntry
        {
                TCHAR szDocName[t_cchItemLen];
                bool operator ==(const _DocEntry& de) const
                { return (lstrcmpi(szDocName, de.szDocName) == 0); }
        };

        enum
        {
                m_nMaxEntries_Min = 2,
                m_nMaxEntries_Max = t_nLastID - t_nFirstID + 1,
                m_cchMaxItemLen_Min = 6,
                m_cchMaxItemLen_Max = t_cchItemLen,
                m_cchItemNameLen = 11
        };

// Data members
        ATL::CSimpleArray<_DocEntry> m_arrDocs;
        int m_nMaxEntries;   // default is 4
        HMENU m_hMenu;

        TCHAR m_szNoEntries[t_cchItemLen];

        int m_cchMaxItemLen;

// Constructor
        CRecentDocumentListBase() : m_hMenu(NULL), m_nMaxEntries(4), m_cchMaxItemLen(-1)
        {
                // These ASSERTs verify values of the template arguments
                ATLASSERT(t_cchItemLen > m_cchMaxItemLen_Min);
                ATLASSERT(m_nMaxEntries_Max > m_nMaxEntries_Min);
        }

// Attributes
        HMENU GetMenuHandle() const
        {
                return m_hMenu;
        }

        void SetMenuHandle(HMENU hMenu)
        {
                ATLASSERT(hMenu == NULL || ::IsMenu(hMenu));
                m_hMenu = hMenu;
                if(m_hMenu == NULL || (::GetMenuString(m_hMenu, t_nFirstID, m_szNoEntries, t_cchItemLen, MF_BYCOMMAND) == 0))
                {
                        T* pT = static_cast<T*>(this);
                        pT;   // avoid level 4 warning
                        SecureHelper::strncpy_x(m_szNoEntries, _countof(m_szNoEntries), pT->GetMRUEmptyText(), _TRUNCATE);
                }
        }

        int GetMaxEntries() const
        {
                return m_nMaxEntries;
        }

        void SetMaxEntries(int nMaxEntries)
        {
                ATLASSERT(nMaxEntries >= m_nMaxEntries_Min && nMaxEntries <= m_nMaxEntries_Max);
                if(nMaxEntries < m_nMaxEntries_Min)
                        nMaxEntries = m_nMaxEntries_Min;
                else if(nMaxEntries > m_nMaxEntries_Max)
                        nMaxEntries = m_nMaxEntries_Max;
                m_nMaxEntries = nMaxEntries;
        }

        int GetMaxItemLength() const
        {
                return m_cchMaxItemLen;
        }

        void SetMaxItemLength(int cchMaxLen)
        {
                ATLASSERT((cchMaxLen >= m_cchMaxItemLen_Min && cchMaxLen <= m_cchMaxItemLen_Max) || cchMaxLen == -1);
                if(cchMaxLen != -1)
                {
                        if(cchMaxLen < m_cchMaxItemLen_Min)
                                cchMaxLen = m_cchMaxItemLen_Min;
                        else if(cchMaxLen > m_cchMaxItemLen_Max)
                                cchMaxLen = m_cchMaxItemLen_Max;
                }
                m_cchMaxItemLen = cchMaxLen;
                T* pT = static_cast<T*>(this);
                pT->UpdateMenu();
        }

// Operations
        BOOL AddToList(LPCTSTR lpstrDocName)
        {
                _DocEntry de;
                errno_t nRet = SecureHelper::strncpy_x(de.szDocName, _countof(de.szDocName), lpstrDocName, _TRUNCATE);
                if(nRet != 0 && nRet != STRUNCATE)
                        return FALSE;

                for(int i = 0; i < m_arrDocs.GetSize(); i++)
                {
                        if(lstrcmpi(m_arrDocs[i].szDocName, lpstrDocName) == 0)
                        {
                                m_arrDocs.RemoveAt(i);
                                break;
                        }
                }

                if(m_arrDocs.GetSize() == m_nMaxEntries)
                        m_arrDocs.RemoveAt(0);

                BOOL bRet = m_arrDocs.Add(de);
                if(bRet)
                {
                        T* pT = static_cast<T*>(this);
                        bRet = pT->UpdateMenu();
                }
                return bRet;
        }

        // This function is deprecated because it is not safe. 
        // Use the version below that accepts the buffer length.
#if (_MSC_VER >= 1300)
        __declspec(deprecated)
#endif
        BOOL GetFromList(int /*nItemID*/, LPTSTR /*lpstrDocName*/)
        {
                ATLASSERT(FALSE);
                return FALSE;
        }

        BOOL GetFromList(int nItemID, LPTSTR lpstrDocName, int cchLength)
        {
                int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
                if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
                        return FALSE;
                if(lstrlen(m_arrDocs[nIndex].szDocName) >= cchLength)
                        return FALSE;
                SecureHelper::strcpy_x(lpstrDocName, cchLength, m_arrDocs[nIndex].szDocName);

                return TRUE;
        }

#if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
        BOOL GetFromList(int nItemID, _CSTRING_NS::CString& strDocName)
        {
                int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
                if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
                        return FALSE;
                strDocName = m_arrDocs[nIndex].szDocName;
                return TRUE;
        }
#endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)

        BOOL RemoveFromList(int nItemID)
        {
                int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
                BOOL bRet = m_arrDocs.RemoveAt(nIndex);
                if(bRet)
                {
                        T* pT = static_cast<T*>(this);
                        bRet = pT->UpdateMenu();
                }
                return bRet;
        }

        BOOL MoveToTop(int nItemID)
        {
                int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
                if(nIndex < 0 || nIndex >= m_arrDocs.GetSize())
                        return FALSE;
                _DocEntry de;
                de = m_arrDocs[nIndex];
                m_arrDocs.RemoveAt(nIndex);
                BOOL bRet = m_arrDocs.Add(de);
                if(bRet)
                {
                        T* pT = static_cast<T*>(this);
                        bRet = pT->UpdateMenu();
                }
                return bRet;
        }

        BOOL ReadFromRegistry(LPCTSTR lpstrRegKey)
        {
                T* pT = static_cast<T*>(this);
                ATL::CRegKey rkParent;
                ATL::CRegKey rk;

                LONG lRet = rkParent.Open(HKEY_CURRENT_USER, lpstrRegKey);
                if(lRet != ERROR_SUCCESS)
                        return FALSE;
                lRet = rk.Open(rkParent, pT->GetRegKeyName());
                if(lRet != ERROR_SUCCESS)
                        return FALSE;

                DWORD dwRet = 0;
#if (_ATL_VER >= 0x0700)
                lRet = rk.QueryDWORDValue(pT->GetRegCountName(), dwRet);
#else
                lRet = rk.QueryValue(dwRet, pT->GetRegCountName());
#endif
                if(lRet != ERROR_SUCCESS)
                        return FALSE;
                SetMaxEntries(dwRet);

                m_arrDocs.RemoveAll();

                TCHAR szRetString[t_cchItemLen] = { 0 };
                _DocEntry de;

                for(int nItem = m_nMaxEntries; nItem > 0; nItem--)
                {
                        TCHAR szBuff[m_cchItemNameLen] = { 0 };
                        SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
#if (_ATL_VER >= 0x0700)
                        ULONG ulCount = t_cchItemLen;
                        lRet = rk.QueryStringValue(szBuff, szRetString, &ulCount);
#else
                        DWORD dwCount = t_cchItemLen * sizeof(TCHAR);
                        lRet = rk.QueryValue(szRetString, szBuff, &dwCount);
#endif
                        if(lRet == ERROR_SUCCESS)
                        {
                                SecureHelper::strcpy_x(de.szDocName, _countof(de.szDocName), szRetString);
                                m_arrDocs.Add(de);
                        }
                }

                rk.Close();
                rkParent.Close();

                return pT->UpdateMenu();
        }

        BOOL WriteToRegistry(LPCTSTR lpstrRegKey)
        {
                T* pT = static_cast<T*>(this);
                pT;   // avoid level 4 warning
                ATL::CRegKey rkParent;
                ATL::CRegKey rk;

                LONG lRet = rkParent.Create(HKEY_CURRENT_USER, lpstrRegKey);
                if(lRet != ERROR_SUCCESS)
                        return FALSE;
                lRet = rk.Create(rkParent, pT->GetRegKeyName());
                if(lRet != ERROR_SUCCESS)
                        return FALSE;

#if (_ATL_VER >= 0x0700)
                lRet = rk.SetDWORDValue(pT->GetRegCountName(), m_nMaxEntries);
#else
                lRet = rk.SetValue(m_nMaxEntries, pT->GetRegCountName());
#endif
                ATLASSERT(lRet == ERROR_SUCCESS);

                // set new values
                int nItem;
                for(nItem = m_arrDocs.GetSize(); nItem > 0; nItem--)
                {
                        TCHAR szBuff[m_cchItemNameLen] = { 0 };
                        SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
                        TCHAR szDocName[t_cchItemLen] = { 0 };
                        GetFromList(t_nFirstID + nItem - 1, szDocName, t_cchItemLen);
#if (_ATL_VER >= 0x0700)
                        lRet = rk.SetStringValue(szBuff, szDocName);
#else
                        lRet = rk.SetValue(szDocName, szBuff);
#endif
                        ATLASSERT(lRet == ERROR_SUCCESS);
                }

                // delete unused keys
                for(nItem = m_arrDocs.GetSize() + 1; nItem < m_nMaxEntries_Max; nItem++)
                {
                        TCHAR szBuff[m_cchItemNameLen] = { 0 };
                        SecureHelper::wsprintf_x(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
                        rk.DeleteValue(szBuff);
                }

                rk.Close();
                rkParent.Close();

                return TRUE;
        }

// Implementation
        BOOL UpdateMenu()
        {
                if(m_hMenu == NULL)
                        return FALSE;
                ATLASSERT(::IsMenu(m_hMenu));

                int nItems = ::GetMenuItemCount(m_hMenu);
                int nInsertPoint;
                for(nInsertPoint = 0; nInsertPoint < nItems; nInsertPoint++)
                {
                        CMenuItemInfo mi;
                        mi.fMask = MIIM_ID;
                        ::GetMenuItemInfo(m_hMenu, nInsertPoint, TRUE, &mi);
                        if (mi.wID == t_nFirstID)
                                break;
                }
                ATLASSERT(nInsertPoint < nItems && "You need a menu item with an ID = t_nFirstID");

                int nItem;
                for(nItem = t_nFirstID; nItem < t_nFirstID + m_nMaxEntries; nItem++)
                {
                        // keep the first one as an insertion point
                        if (nItem != t_nFirstID)
                                ::DeleteMenu(m_hMenu, nItem, MF_BYCOMMAND);
                }

                TCHAR szItemText[t_cchItemLen + 6] = { 0 };   // add space for &, 2 digits, and a space
                int nSize = m_arrDocs.GetSize();
                nItem = 0;
                if(nSize > 0)
                {
                        for(nItem = 0; nItem < nSize; nItem++)
                        {
                                if(m_cchMaxItemLen == -1)
                                {
                                        SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, m_arrDocs[nSize - 1 - nItem].szDocName);
                                }
                                else
                                {
                                        TCHAR szBuff[t_cchItemLen] = { 0 };
                                        T* pT = static_cast<T*>(this);
                                        pT;   // avoid level 4 warning
                                        bool bRet = pT->CompactDocumentName(szBuff, m_arrDocs[nSize - 1 - nItem].szDocName, m_cchMaxItemLen);
                                        bRet;   // avoid level 4 warning
                                        ATLASSERT(bRet);
                                        SecureHelper::wsprintf_x(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, szBuff);
                                }
                                ::InsertMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION | MF_STRING, t_nFirstID + nItem, szItemText);
                        }
                }
                else    // empty
                {
                        ::InsertMenu(m_hMenu, nInsertPoint, MF_BYPOSITION | MF_STRING, t_nFirstID, m_szNoEntries);
                        ::EnableMenuItem(m_hMenu, t_nFirstID, MF_GRAYED);
                        nItem++;
                }
                ::DeleteMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION);

                return TRUE;
        }

// Overrideables
        // override to provide a different method of compacting document names
        static bool CompactDocumentName(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
        {
                return AtlCompactPath(lpstrOut, lpstrIn, cchLen);
        }

        static LPCTSTR GetRegKeyName()
        {
                return _T("Recent Document List");
        }

        static LPCTSTR GetRegCountName()
        {
                return _T("DocumentCount");
        }

        static LPCTSTR GetRegItemName()
        {
                // Note: This string is a format string used with wsprintf().
                // Resulting formatted string must be m_cchItemNameLen or less 
                // characters long, including the terminating null character.
                return _T("Document%i");
        }

        static LPCTSTR GetMRUEmptyText()
        {
                return _WTL_MRUEMPTY_TEXT;
        }
};

class CRecentDocumentList : public CRecentDocumentListBase<CRecentDocumentList>
{
public:
// nothing here
};

#endif // _WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CFindFile - file search helper class

class CFindFile
{
public:
// Data members
        WIN32_FIND_DATA m_fd;
        TCHAR m_lpszRoot[MAX_PATH];
        TCHAR m_chDirSeparator;
        HANDLE m_hFind;
        BOOL m_bFound;

// Constructor/destructor
        CFindFile() : m_hFind(NULL), m_chDirSeparator(_T('\\')), m_bFound(FALSE)
        { }

        ~CFindFile()
        {
                Close();
        }

// Attributes
        ULONGLONG GetFileSize() const
        {
                ATLASSERT(m_hFind != NULL);

                ULARGE_INTEGER nFileSize = { 0 };

                if(m_bFound)
                {
                        nFileSize.LowPart = m_fd.nFileSizeLow;
                        nFileSize.HighPart = m_fd.nFileSizeHigh;
                }
                else
                {
                        nFileSize.QuadPart = 0;
                }

                return nFileSize.QuadPart;
        }

        BOOL GetFileName(LPTSTR lpstrFileName, int cchLength) const
        {
                ATLASSERT(m_hFind != NULL);
                if(lstrlen(m_fd.cFileName) >= cchLength)
                        return FALSE;

                if(m_bFound)
                        SecureHelper::strcpy_x(lpstrFileName, cchLength, m_fd.cFileName);

                return m_bFound;
        }

        BOOL GetFilePath(LPTSTR lpstrFilePath, int cchLength) const
        {
                ATLASSERT(m_hFind != NULL);

                int nLen = lstrlen(m_lpszRoot);
#ifndef _WIN32_WCE
                ATLASSERT(nLen > 0);
                if(nLen == 0)
                        return FALSE;

                bool bAddSep = (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/'));
#else // CE specific
                // allow diskless devices (nLen == 0)
                bool bAddSep = ((nLen == 0) || (m_lpszRoot[nLen - 1] != _T('\\') && m_lpszRoot[nLen - 1] !=_T('/')));
#endif // _WIN32_WCE

                if((lstrlen(m_lpszRoot) + (bAddSep ?  1 : 0)) >= cchLength)
                        return FALSE;

                SecureHelper::strcpy_x(lpstrFilePath, cchLength, m_lpszRoot);

                if(bAddSep)
                {
                        TCHAR szSeparator[2] = { m_chDirSeparator, 0 };
                        SecureHelper::strcat_x(lpstrFilePath, cchLength, szSeparator);
                }

                SecureHelper::strcat_x(lpstrFilePath, cchLength, m_fd.cFileName);

                return TRUE;
        }

#ifndef _WIN32_WCE
        BOOL GetFileTitle(LPTSTR lpstrFileTitle, int cchLength) const
        {
                ATLASSERT(m_hFind != NULL);

                TCHAR szBuff[MAX_PATH] = { 0 };
                if(!GetFileName(szBuff, MAX_PATH))
                        return FALSE;

                if(lstrlen(szBuff) >= cchLength || cchLength < 1)
                        return FALSE;

                // find the last dot
                LPTSTR pstrDot  = (LPTSTR)_cstrrchr(szBuff, _T('.'));
                if(pstrDot != NULL)
                        *pstrDot = 0;

                SecureHelper::strcpy_x(lpstrFileTitle, cchLength, szBuff);

                return TRUE;
        }
#endif // !_WIN32_WCE

        BOOL GetFileURL(LPTSTR lpstrFileURL, int cchLength) const
        {
                ATLASSERT(m_hFind != NULL);

                TCHAR szBuff[MAX_PATH] = { 0 };
                if(!GetFilePath(szBuff, MAX_PATH))
                        return FALSE;
                LPCTSTR lpstrFileURLPrefix = _T("file://");
                if(lstrlen(szBuff) + lstrlen(lpstrFileURLPrefix) >= cchLength)
                        return FALSE;
                SecureHelper::strcpy_x(lpstrFileURL, cchLength, lpstrFileURLPrefix);
                SecureHelper::strcat_x(lpstrFileURL, cchLength, szBuff);

                return TRUE;
        }

        BOOL GetRoot(LPTSTR lpstrRoot, int cchLength) const
        {
                ATLASSERT(m_hFind != NULL);
                if(lstrlen(m_lpszRoot) >= cchLength)
                        return FALSE;

                SecureHelper::strcpy_x(lpstrRoot, cchLength, m_lpszRoot);

                return TRUE;
        }

#if defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)
        _CSTRING_NS::CString GetFileName() const
        {
                ATLASSERT(m_hFind != NULL);

                _CSTRING_NS::CString ret;

                if(m_bFound)
                        ret = m_fd.cFileName;
                return ret;
        }

        _CSTRING_NS::CString GetFilePath() const
        {
                ATLASSERT(m_hFind != NULL);

                _CSTRING_NS::CString strResult = m_lpszRoot;
                int nLen = strResult.GetLength();
#ifndef _WIN32_WCE
                ATLASSERT(nLen > 0);
                if(nLen == 0)
                        return strResult;

                if((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/')))
#else // CE specific
                // allow diskless devices (nLen == 0)
                if((nLen == 0) || ((strResult[nLen - 1] != _T('\\')) && (strResult[nLen - 1] != _T('/'))))
#endif // _WIN32_WCE
                        strResult += m_chDirSeparator;
                strResult += GetFileName();
                return strResult;
        }

#ifndef _WIN32_WCE
        _CSTRING_NS::CString GetFileTitle() const
        {
                ATLASSERT(m_hFind != NULL);

                _CSTRING_NS::CString strResult;
                GetFileTitle(strResult.GetBuffer(MAX_PATH), MAX_PATH);
                strResult.ReleaseBuffer();

                return strResult;
        }
#endif // !_WIN32_WCE

        _CSTRING_NS::CString GetFileURL() const
        {
                ATLASSERT(m_hFind != NULL);

                _CSTRING_NS::CString strResult("file://");
                strResult += GetFilePath();
                return strResult;
        }

        _CSTRING_NS::CString GetRoot() const
        {
                ATLASSERT(m_hFind != NULL);

                _CSTRING_NS::CString str = m_lpszRoot;
                return str;
        }
#endif // defined(_WTL_USE_CSTRING) || defined(__ATLSTR_H__)

        BOOL GetLastWriteTime(FILETIME* pTimeStamp) const
        {
                ATLASSERT(m_hFind != NULL);
                ATLASSERT(pTimeStamp != NULL);

                if(m_bFound && pTimeStamp != NULL)
                {
                        *pTimeStamp = m_fd.ftLastWriteTime;
                        return TRUE;
                }

                return FALSE;
        }

        BOOL GetLastAccessTime(FILETIME* pTimeStamp) const
        {
                ATLASSERT(m_hFind != NULL);
                ATLASSERT(pTimeStamp != NULL);

                if(m_bFound && pTimeStamp != NULL)
                {
                        *pTimeStamp = m_fd.ftLastAccessTime;
                        return TRUE;
                }

                return FALSE;
        }

        BOOL GetCreationTime(FILETIME* pTimeStamp) const
        {
                ATLASSERT(m_hFind != NULL);

                if(m_bFound && pTimeStamp != NULL)
                {
                        *pTimeStamp = m_fd.ftCreationTime;
                        return TRUE;
                }

                return FALSE;
        }

        BOOL MatchesMask(DWORD dwMask) const
        {
                ATLASSERT(m_hFind != NULL);

                if(m_bFound)
                        return ((m_fd.dwFileAttributes & dwMask) != 0);

                return FALSE;
        }

        BOOL IsDots() const
        {
                ATLASSERT(m_hFind != NULL);

                // return TRUE if the file name is "." or ".." and
                // the file is a directory

                BOOL bResult = FALSE;
                if(m_bFound && IsDirectory())
                {
                        if(m_fd.cFileName[0] == _T('.') && (m_fd.cFileName[1] == _T('\0') || (m_fd.cFileName[1] == _T('.') && m_fd.cFileName[2] == _T('\0'))))
                                bResult = TRUE;
                }

                return bResult;
        }

        BOOL IsReadOnly() const
        {
                return MatchesMask(FILE_ATTRIBUTE_READONLY);
        }

        BOOL IsDirectory() const
        {
                return MatchesMask(FILE_ATTRIBUTE_DIRECTORY);
        }

        BOOL IsCompressed() const
        {
                return MatchesMask(FILE_ATTRIBUTE_COMPRESSED);
        }

        BOOL IsSystem() const
        {
                return MatchesMask(FILE_ATTRIBUTE_SYSTEM);
        }

        BOOL IsHidden() const
        {
                return MatchesMask(FILE_ATTRIBUTE_HIDDEN);
        }

        BOOL IsTemporary() const
        {
                return MatchesMask(FILE_ATTRIBUTE_TEMPORARY);
        }

        BOOL IsNormal() const
        {
                return MatchesMask(FILE_ATTRIBUTE_NORMAL);
        }

        BOOL IsArchived() const
        {
                return MatchesMask(FILE_ATTRIBUTE_ARCHIVE);
        }

// Operations
        BOOL FindFile(LPCTSTR pstrName = NULL)
        {
                Close();

                if(pstrName == NULL)
                {
                        pstrName = _T("*.*");
                }
                else if(lstrlen(pstrName) >= MAX_PATH)
                {
                        ATLASSERT(FALSE);
                        return FALSE;
                }

                SecureHelper::strcpy_x(m_fd.cFileName, _countof(m_fd.cFileName), pstrName);

                m_hFind = ::FindFirstFile(pstrName, &m_fd);

                if(m_hFind == INVALID_HANDLE_VALUE)
                        return FALSE;

#ifndef _WIN32_WCE
                bool bFullPath = (::GetFullPathName(pstrName, MAX_PATH, m_lpszRoot, NULL) != 0);
#else // CE specific
                errno_t nRet = SecureHelper::strncpy_x(m_lpszRoot, _countof(m_lpszRoot), pstrName, _TRUNCATE);
                bool bFullPath = (nRet == 0 || nRet == STRUNCATE);
#endif // _WIN32_WCE

                // passed name isn't a valid path but was found by the API
                ATLASSERT(bFullPath);
                if(!bFullPath)
                {
                        Close();
                        ::SetLastError(ERROR_INVALID_NAME);
                        return FALSE;
                }
                else
                {
                        // find the last forward or backward whack
                        LPTSTR pstrBack  = (LPTSTR)_cstrrchr(m_lpszRoot, _T('\\'));
                        LPTSTR pstrFront = (LPTSTR)_cstrrchr(m_lpszRoot, _T('/'));

                        if(pstrFront != NULL || pstrBack != NULL)
                        {
                                if(pstrFront == NULL)
                                        pstrFront = m_lpszRoot;
                                if(pstrBack == NULL)
                                        pstrBack = m_lpszRoot;

                                // from the start to the last whack is the root

                                if(pstrFront >= pstrBack)
                                        *pstrFront = _T('\0');
                                else
                                        *pstrBack = _T('\0');
                        }
                }

                m_bFound = TRUE;

                return TRUE;
        }

        BOOL FindNextFile()
        {
                ATLASSERT(m_hFind != NULL);

                if(m_hFind == NULL)
                        return FALSE;

                if(!m_bFound)
                        return FALSE;

                m_bFound = ::FindNextFile(m_hFind, &m_fd);

                return m_bFound;
        }

        void Close()
        {
                m_bFound = FALSE;

                if(m_hFind != NULL && m_hFind != INVALID_HANDLE_VALUE)
                {
                        ::FindClose(m_hFind);
                        m_hFind = NULL;
                }
        }

// Helper
        static const TCHAR* _cstrrchr(const TCHAR* p, TCHAR ch)
        {
#ifdef _ATL_MIN_CRT
                const TCHAR* lpsz = NULL;
                while (*p != 0)
                {
                        if (*p == ch)
                                lpsz = p;
                        p = ::CharNext(p);
                }
                return lpsz;
#else // !_ATL_MIN_CRT
                return _tcsrchr(p, ch);
#endif // !_ATL_MIN_CRT
        }
};


///////////////////////////////////////////////////////////////////////////////
// Global functions for loading resources

inline HACCEL AtlLoadAccelerators(ATL::_U_STRINGorID table)
{
        return ::LoadAccelerators(ModuleHelper::GetResourceInstance(), table.m_lpstr);
}

inline HMENU AtlLoadMenu(ATL::_U_STRINGorID menu)
{
        return ::LoadMenu(ModuleHelper::GetResourceInstance(), menu.m_lpstr);
}

inline HBITMAP AtlLoadBitmap(ATL::_U_STRINGorID bitmap)
{
        return ::LoadBitmap(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr);
}

#ifdef OEMRESOURCE
inline HBITMAP AtlLoadSysBitmap(ATL::_U_STRINGorID bitmap)
{
#ifdef _DEBUG
        WORD wID = (WORD)bitmap.m_lpstr;
        ATLASSERT(wID >= 32734 && wID <= 32767);
#endif // _DEBUG
        return ::LoadBitmap(NULL, bitmap.m_lpstr);
}
#endif // OEMRESOURCE

inline HCURSOR AtlLoadCursor(ATL::_U_STRINGorID cursor)
{
        return ::LoadCursor(ModuleHelper::GetResourceInstance(), cursor.m_lpstr);
}

inline HCURSOR AtlLoadSysCursor(LPCTSTR lpCursorName)
{
#if (WINVER >= 0x0500)
        ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT ||
                lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE ||
                lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW ||
                lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL ||
                lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP ||
                lpCursorName == IDC_HAND);
#else // !(WINVER >= 0x0500)
        ATLASSERT(lpCursorName == IDC_ARROW || lpCursorName == IDC_IBEAM || lpCursorName == IDC_WAIT ||
                lpCursorName == IDC_CROSS || lpCursorName == IDC_UPARROW || lpCursorName == IDC_SIZE ||
                lpCursorName == IDC_ICON || lpCursorName == IDC_SIZENWSE || lpCursorName == IDC_SIZENESW ||
                lpCursorName == IDC_SIZEWE || lpCursorName == IDC_SIZENS || lpCursorName == IDC_SIZEALL ||
                lpCursorName == IDC_NO || lpCursorName == IDC_APPSTARTING || lpCursorName == IDC_HELP);
#endif // !(WINVER >= 0x0500)
        return ::LoadCursor(NULL, lpCursorName);
}

inline HICON AtlLoadIcon(ATL::_U_STRINGorID icon)
{
        return ::LoadIcon(ModuleHelper::GetResourceInstance(), icon.m_lpstr);
}

#ifndef _WIN32_WCE
inline HICON AtlLoadSysIcon(LPCTSTR lpIconName)
{
#if (WINVER >= 0x0600)
        ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION ||
                  lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO ||
                  lpIconName == IDI_SHIELD);
#else // !(WINVER >= 0x0600)
        ATLASSERT(lpIconName == IDI_APPLICATION || lpIconName == IDI_ASTERISK || lpIconName == IDI_EXCLAMATION ||
                  lpIconName == IDI_HAND || lpIconName == IDI_QUESTION || lpIconName == IDI_WINLOGO);
#endif // !(WINVER >= 0x0600)
        return ::LoadIcon(NULL, lpIconName);
}
#endif // !_WIN32_WCE

inline HBITMAP AtlLoadBitmapImage(ATL::_U_STRINGorID bitmap, UINT fuLoad = LR_DEFAULTCOLOR)
{
        return (HBITMAP)::LoadImage(ModuleHelper::GetResourceInstance(), bitmap.m_lpstr, IMAGE_BITMAP, 0, 0, fuLoad);
}

inline HCURSOR AtlLoadCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
{
        return (HCURSOR)::LoadImage(ModuleHelper::GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
}

inline HICON AtlLoadIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
{
        return (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
}

#ifdef OEMRESOURCE
inline HBITMAP AtlLoadSysBitmapImage(WORD wBitmapID, UINT fuLoad = LR_DEFAULTCOLOR)
{
        ATLASSERT(wBitmapID >= 32734 && wBitmapID <= 32767);
        ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file
        return (HBITMAP)::LoadImage(NULL, MAKEINTRESOURCE(wBitmapID), IMAGE_BITMAP, 0, 0, fuLoad);
}
#endif // OEMRESOURCE

inline HCURSOR AtlLoadSysCursorImage(ATL::_U_STRINGorID cursor, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
{
#ifdef _DEBUG
        WORD wID = (WORD)cursor.m_lpstr;
        ATLASSERT((wID >= 32512 && wID <= 32516) || (wID >= 32640 && wID <= 32648) || (wID == 32650) || (wID == 32651));
        ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file
#endif // _DEBUG
        return (HCURSOR)::LoadImage(NULL, cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
}

inline HICON AtlLoadSysIconImage(ATL::_U_STRINGorID icon, UINT fuLoad = LR_DEFAULTCOLOR | LR_DEFAULTSIZE, int cxDesired = 0, int cyDesired = 0)
{
#ifdef _DEBUG
        WORD wID = (WORD)icon.m_lpstr;
        ATLASSERT(wID >= 32512 && wID <= 32517);
        ATLASSERT((fuLoad & LR_LOADFROMFILE) == 0);   // this one doesn't load from a file
#endif // _DEBUG
        return (HICON)::LoadImage(NULL, icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
}

#if (_ATL_VER < 0x0700)
inline int AtlLoadString(UINT uID, LPTSTR lpBuffer, int nBufferMax)
{
        return ::LoadString(_Module.GetResourceInstance(), uID, lpBuffer, nBufferMax);
}
#endif // (_ATL_VER < 0x0700)

#ifdef _WIN32_WCE // CE only direct access to the resource
inline LPCTSTR AtlLoadString(UINT uID)
{
        LPCTSTR s = (LPCTSTR)::LoadString(ModuleHelper::GetResourceInstance(), uID, NULL, 0);
#ifdef DEBUG // Check for null-termination
        if(s != NULL)
                // Note: RC -n <file.rc> compiles null-terminated resource strings
                ATLASSERT(s[*((WORD*)s -1) - 1] == L'\0');
#endif
        return s;
}
#endif // _WIN32_WCE

inline bool AtlLoadString(UINT uID, BSTR& bstrText)
{
        USES_CONVERSION;
        ATLASSERT(bstrText == NULL);

        LPTSTR lpstrText = NULL;
        int nRes = 0;
        for(int nLen = 256; ; nLen *= 2)
        {
                ATLTRY(lpstrText = new TCHAR[nLen]);
                if(lpstrText == NULL)
                        break;
                nRes = ::LoadString(ModuleHelper::GetResourceInstance(), uID, lpstrText, nLen);
                if(nRes < nLen - 1)
                        break;
                delete [] lpstrText;
                lpstrText = NULL;
        }

        if(lpstrText != NULL)
        {
                if(nRes != 0)
                        bstrText = ::SysAllocString(T2OLE(lpstrText));
                delete [] lpstrText;
        }

        return (bstrText != NULL) ? true : false;
}


///////////////////////////////////////////////////////////////////////////////
// Global functions for stock GDI objects

inline HPEN AtlGetStockPen(int nPen)
{
#if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE)
        ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN || nPen == DC_PEN);
#else
        ATLASSERT(nPen == WHITE_PEN || nPen == BLACK_PEN || nPen == NULL_PEN);
#endif
        return (HPEN)::GetStockObject(nPen);
}

inline HBRUSH AtlGetStockBrush(int nBrush)
{
#if (_WIN32_WINNT >= 0x0500) && !defined(_WIN32_WCE)
        ATLASSERT((nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH) || nBrush == DC_BRUSH);
#else
        ATLASSERT(nBrush >= WHITE_BRUSH && nBrush <= HOLLOW_BRUSH);
#endif
        return (HBRUSH)::GetStockObject(nBrush);
}

inline HFONT AtlGetStockFont(int nFont)
{
#ifndef _WIN32_WCE
        ATLASSERT((nFont >= OEM_FIXED_FONT && nFont <= SYSTEM_FIXED_FONT) || nFont == DEFAULT_GUI_FONT);
#else // CE specific
        ATLASSERT(nFont == SYSTEM_FONT);
#endif // _WIN32_WCE
        return (HFONT)::GetStockObject(nFont);
}

inline HPALETTE AtlGetStockPalette(int nPalette)
{
        ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported
        return (HPALETTE)::GetStockObject(nPalette);
}


///////////////////////////////////////////////////////////////////////////////
// Global function for compacting a path by replacing parts with ellipsis

// helper for multi-byte character sets
inline bool _IsDBCSTrailByte(LPCTSTR lpstr, int nChar)
{
#ifndef _UNICODE
        int i = nChar;
        for( ; i > 0; i--)
        {
                if(!::IsDBCSLeadByte(lpstr[i - 1]))
                        break;
        }
        return ((nChar > 0) && (((nChar - i) & 1) != 0));
#else // _UNICODE
        lpstr; nChar;
        return false;
#endif // _UNICODE
}

inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
{
        ATLASSERT(lpstrOut != NULL);
        ATLASSERT(lpstrIn != NULL);
        ATLASSERT(cchLen > 0);

        LPCTSTR szEllipsis = _T("...");
        const int cchEndEllipsis = 3;
        const int cchMidEllipsis = 4;

        if(lstrlen(lpstrIn) < cchLen)
        {
                SecureHelper::strcpy_x(lpstrOut, cchLen, lpstrIn);
                return true;
        }

        lpstrOut[0] = 0;

        // check if the separator is a slash or a backslash
        TCHAR chSlash = _T('\\');
        for(LPTSTR lpstr = (LPTSTR)lpstrIn; *lpstr != 0; lpstr = ::CharNext(lpstr))
        {
                if((*lpstr == _T('/')) || (*lpstr == _T('\\')))
                        chSlash = *lpstr;
        }

        // find the filename portion of the path
        LPCTSTR lpstrFileName = lpstrIn;
        for(LPCTSTR pPath = lpstrIn; *pPath; pPath = ::CharNext(pPath))
        {
                if((pPath[0] == _T('\\') || pPath[0] == _T(':') || pPath[0] == _T('/'))
                                && pPath[1] && pPath[1] != _T('\\') && pPath[1] != _T('/'))
                        lpstrFileName = pPath + 1;
        }
        int cchFileName = lstrlen(lpstrFileName);

        // handle just the filename without a path
        if(lpstrFileName == lpstrIn && cchLen > cchEndEllipsis)
        {
                bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchLen - cchEndEllipsis - 1) == 0);
                if(bRet)
                {
#ifndef _UNICODE
                        if(_IsDBCSTrailByte(lpstrIn, cchLen - cchEndEllipsis))
                                lpstrOut[cchLen - cchEndEllipsis - 1] = 0;
#endif // _UNICODE
                        SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
                }
                return bRet;
        }

        // handle just ellipsis
        if((cchLen < (cchMidEllipsis + cchEndEllipsis)))
        {
                for(int i = 0; i < cchLen - 1; i++)
                        lpstrOut[i] = ((i + 1) == cchMidEllipsis) ? chSlash : _T('.');
                lpstrOut[cchLen - 1] = 0;
                return true;
        }

        // calc how much we have to copy
        int cchToCopy = cchLen - (cchMidEllipsis + cchFileName) - 1;

        if(cchToCopy < 0)
                cchToCopy = 0;

#ifndef _UNICODE
        if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrIn, cchToCopy))
                cchToCopy--;
#endif // _UNICODE

        bool bRet = (SecureHelper::strncpy_x(lpstrOut, cchLen, lpstrIn, cchToCopy) == 0);
        if(!bRet)
                return false;

        // add ellipsis
        SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
        if(!bRet)
                return false;
        TCHAR szSlash[2] = { chSlash, 0 };
        SecureHelper::strcat_x(lpstrOut, cchLen, szSlash);
        if(!bRet)
                return false;

        // add filename (and ellipsis, if needed)
        if(cchLen > (cchMidEllipsis + cchFileName))
        {
                SecureHelper::strcat_x(lpstrOut, cchLen, lpstrFileName);
        }
        else
        {
                cchToCopy = cchLen - cchMidEllipsis - cchEndEllipsis - 1;
#ifndef _UNICODE
                if(cchToCopy > 0 && _IsDBCSTrailByte(lpstrFileName, cchToCopy))
                        cchToCopy--;
#endif // _UNICODE
                bRet = (SecureHelper::strncpy_x(&lpstrOut[cchMidEllipsis], cchLen - cchMidEllipsis, lpstrFileName, cchToCopy) == 0);
                if(bRet)
                        SecureHelper::strcat_x(lpstrOut, cchLen, szEllipsis);
        }

        return bRet;
}

}; // namespace WTL

#endif // __ATLMISC_H__

/* [<][>][^][v][top][bottom][index][help] */