summaryrefslogtreecommitdiff
path: root/plugins/!NotAdopted/SmartAutoReplier/wtl/atlmisc.h
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/!NotAdopted/SmartAutoReplier/wtl/atlmisc.h')
-rw-r--r--plugins/!NotAdopted/SmartAutoReplier/wtl/atlmisc.h3817
1 files changed, 3817 insertions, 0 deletions
diff --git a/plugins/!NotAdopted/SmartAutoReplier/wtl/atlmisc.h b/plugins/!NotAdopted/SmartAutoReplier/wtl/atlmisc.h
new file mode 100644
index 0000000000..3519b348fd
--- /dev/null
+++ b/plugins/!NotAdopted/SmartAutoReplier/wtl/atlmisc.h
@@ -0,0 +1,3817 @@
+// Windows Template Library - WTL version 8.1
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//
+// This file is a part of the Windows Template Library.
+// The use and distribution terms for this software are covered by the
+// Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php)
+// which can be found in the file CPL.TXT at the root of this distribution.
+// By using this software in any fashion, you are agreeing to be bound by
+// the terms of this license. You must not remove this notice, or
+// any other, from this software.
+
+#ifndef __ATLMISC_H__
+#define __ATLMISC_H__
+
+#pragma once
+
+#ifndef __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:
+// 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);
+ CRegKeyEx rkParent;
+ CRegKeyEx 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;
+ lRet = rk.QueryDWORDValue(pT->GetRegCountName(), dwRet);
+ 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);
+ ULONG ulCount = t_cchItemLen;
+ lRet = rk.QueryStringValue(szBuff, szRetString, &ulCount);
+ 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
+ CRegKeyEx rkParent;
+ CRegKeyEx 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;
+
+ lRet = rk.SetDWORDValue(pT->GetRegCountName(), m_nMaxEntries);
+ 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);
+ lRet = rk.SetStringValue(szBuff, szDocName);
+ 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 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__