ATL CString 的代码 整理

从ATL整理的CString代码 可以用于其他项目

#pragma once
#include <assert.h>
#include <mbstring.h>
#include <atlexcept.h>


#define IN_OPT
#define IN_OUT

#ifndef MYATLENSURE_RETURN_VAL
#define MYATLENSURE_RETURN_VAL(expr, val)          \
	do {                                           \
		int __atl_condVal=!!(expr);                \
		assert(__atl_condVal);                     \
		if(!(__atl_condVal)) return val;           \
	} __pragma(warning(suppress:4127)) while (0)
#endif // MYATLENSURE_RETURN_VAL

//向上对齐;下面使用时都是按8字节向上对齐的,两个位运算,第一个是可能带有进位的加法(+nAlign - 1)另一个是抹掉低log2(nAlign)位
//0->0 1->8 2->8 ... 8->8 9->16...
template< typename N >
inline N MyAlignUp(IN N n, IN ULONG nAlign) throw()
{
	return(N(  (n + (nAlign - 1)) & ~(N(nAlign) - 1)   ));
}

//带有运算范围溢出检测的加法
template<typename T>
inline HRESULT MyAtlAdd(_Out_ T* ptResult, IN T tLeft, IN T tRight)
{
	if(::ATL::AtlLimits<T>::_Max - tLeft < tRight)
	{
		return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
	}

	*ptResult = tLeft + tRight;
	return S_OK;
}

//带有运算范围溢出检测的乘法
template<typename T>
inline HRESULT MyAtlMultiply(_Out_ T* ptResult, IN T tLeft, IN T tRight)
{
	/* avoid divide 0 */
	if(tLeft == 0)
	{
		*ptResult = 0;
		return S_OK;
	}

	if(::ATL::AtlLimits<T>::_Max / tLeft < tRight)
	{
		return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
	}

	*ptResult = tLeft * tRight;
	return S_OK;
}

inline HRESULT MyAtlMultiply(_Out_ unsigned long *piResult, IN unsigned long iLeft, IN unsigned long iRight)
{
	unsigned __int64 i64Result = static_cast<unsigned __int64>(iLeft) * static_cast<unsigned __int64>(iRight);

	if(i64Result > ULONG_MAX)
	{
		return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
	}

	*piResult = static_cast<unsigned long>(i64Result);
	return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Verify that a null-terminated string points to valid memory
inline BOOL MyAtlIsValidString(
    _In_reads_z_(nMaxLength) LPCWSTR psz,
    _In_ size_t nMaxLength = INT_MAX)
{
	(nMaxLength);
	return (psz != NULL);
}

// Verify that a null-terminated string points to valid memory
inline BOOL MyAtlIsValidString(
    _In_reads_z_(nMaxLength) LPCSTR psz,
    _In_ size_t nMaxLength = UINT_MAX)
{
	(nMaxLength);
	return (psz != NULL);
}



/***********************************************************************************
*
*  为了避免编译器的bug,自己实现一个CString,从ATL扒代码,简化了一些
*
*************************************************************************************/
struct MyCStringData;//CString 数据指向CStringData* +1处,CStringData用于实现字符串引用数管理

__interface IMyStrMgr
{
public:
	MyCStringData * Allocate(IN int nAllocLength, IN int nCharSize) throw(); // Allocate a new MyCStringData
	void            Free(IN_OUT MyCStringData * pData) throw();// Free an existing MyCStringData
	MyCStringData * Reallocate(IN_OUT MyCStringData * pData, IN int nAllocLength, IN int nCharSize) throw(); // Change the size of an existing MyCStringData
	MyCStringData * GetNilString() throw(); // Get the MyCStringData for a Nil string
	IMyStrMgr *     Clone() throw();
};


struct MyCStringData
{
	IMyStrMgr* pStringMgr;   // String manager for this MyCStringData
	int nDataLength;         // Length of currently used data in XCHARs (not including terminating null)
	int nAllocLength;        // Length of allocated data in XCHARs (not including terminating null)
	/**/volatile long nRefs; // Reference count: negative == locked(原著中没有volatile)
	// XCHAR data[nAllocLength+1]// A MyCStringData is always followed in memory by the actual array of character data

	void* data() throw()
	{
		return (this + 1);
	}

	void AddRef() throw()
	{
		assert(nRefs > 0);
		InterlockedIncrement(&nRefs);
	}
	bool IsLocked() const throw()
	{
		return nRefs < 0;
	}
	bool IsShared() const throw()
	{
		return(nRefs > 1);
	}
	void Lock() throw()
	{
		assert(nRefs <= 1);
		nRefs--;  // Locked buffers can't be shared, so no interlocked operation necessary

		if(nRefs == 0)
		{
			nRefs = -1;
		}
	}
	void Release() throw()
	{
		assert(nRefs != 0);

		if(InterlockedDecrement(&nRefs) <= 0)
		{
			pStringMgr->Free(this);
		}
	}
	void Unlock() throw()
	{
		assert(IsLocked());

		if(IsLocked())
		{
			nRefs++;  // Locked buffers can't be shared, so no interlocked operation necessary

			if(nRefs == 0)
			{
				nRefs = 1;
			}
		}
	}
};

//空的CString都指向一个公共的CStringData(在mgr中的static变量)
class MyNilStringData : public MyCStringData
{
public:
	MyNilStringData() throw()
	{
		pStringMgr = NULL;
		nRefs = 2;  // Never gets freed by IAtlStringMgr(微软用这种办法避免进行指针的判断,是优化的方法,但也是此篇代码解决问题的由来)
		nDataLength = 0;
		nAllocLength = 0;
		achNil[0] = 0;
		achNil[1] = 0;
	}

	void SetManager(IN IMyStrMgr* pMgr) throw()
	{
		assert(pStringMgr == NULL);
		pStringMgr = pMgr;
	}

public:
	wchar_t achNil[2];
};


class MyWin32Heap//分配回收采用Heap
{
public:
	HANDLE m_hHeap;
	bool m_bOwnHeap;

public:
	MyWin32Heap(IN HANDLE hHeap) throw() :
		m_hHeap(hHeap),
		m_bOwnHeap(false)
	{
		assert(hHeap != NULL);
	}

	void Attach(IN HANDLE hHeap, IN bool bTakeOwnership) throw()
	{
		assert(hHeap != NULL);
		assert(m_hHeap == NULL);
		m_hHeap = hHeap;
		m_bOwnHeap = bTakeOwnership;
	}

	HANDLE Detach() throw()
	{
		HANDLE hHeap;
		hHeap = m_hHeap;
		m_hHeap = NULL;
		m_bOwnHeap = false;
		return(hHeap);
	}

	void* Allocate(IN size_t nBytes) throw()
	{
		return(::HeapAlloc(m_hHeap, 0, nBytes));
	}

	virtual void Free(IN_OPT void* p) throw()
	{
		if(p != NULL)
		{
			BOOL bSuccess;
			bSuccess = ::HeapFree(m_hHeap, 0, p);
			assert(bSuccess);
		}
	}

	void* Reallocate(IN_OPT void* p, IN size_t nBytes) throw()
	{
		if(p == NULL)
		{
			return(Allocate(nBytes));
		}

		if(nBytes == 0)
		{
			Free(p);
			return NULL;
		}

		return(::HeapReAlloc(m_hHeap, 0, p, nBytes));
	}

	virtual size_t GetSize(IN_OUT void* p) throw()
	{
		return(::HeapSize(m_hHeap, 0, p));
	}
};

class MyStrMgr : public IMyStrMgr
{
public:
	MyStrMgr(IN_OPT MyWin32Heap* pMemMgr = NULL) throw() :
		m_pMemMgr(pMemMgr)
	{
		m_nil.SetManager(this);
	}
	virtual ~MyStrMgr() throw() = default;
	void SetMemoryManager(IN MyWin32Heap* pMemMgr) throw()
	{
		assert(m_pMemMgr == NULL);
		m_pMemMgr = pMemMgr;
	}

	static IMyStrMgr* GetInstance()//获取管理器,内部2个静态对象
	{
//#pragma warning(push)
//#pragma warning(disable: 4640) // will always be initialized on entry thread by CImageStaticInitializer
		static MyWin32Heap strHeap(::GetProcessHeap());
		static MyStrMgr strMgr(&strHeap);
//#pragma warning(pop)
		return &strMgr;
	}
	// IMyStrMgr
public:
	virtual MyCStringData* Allocate(IN int nChars, IN int nCharSize) throw()
	{
		//nChars 字符数;nCharSize 字符占的字节数
		if(!(nChars >= 0))
			return NULL;

		size_t nTotalSize;//包含StringData的总分配字节
		MyCStringData* pData;
		size_t nDataBytes;//应分配给字符串的字节数

		if(FAILED(MyAtlAdd(&nChars, nChars, 1)))
		{
			return NULL;
		}

		int nAlignedChars = MyAlignUp(nChars, 8);  // Prevent excessive reallocation.  The heap will usually round up anyway.

		if(!(nChars <= nAlignedChars))
		{
			return NULL;//MYATLENSURE_RETURN_VAL(nChars <= nAlignedChars, NULL);
		}

		if(FAILED(MyAtlMultiply(&nDataBytes, static_cast<size_t>(nAlignedChars), static_cast<size_t>(nCharSize))) ||
		        FAILED(MyAtlAdd(&nTotalSize, static_cast<size_t>(sizeof(MyCStringData)), nDataBytes)))
		{
			return NULL;
		}

		pData = static_cast<MyCStringData*>(m_pMemMgr->Allocate(nTotalSize));

		if(pData == NULL)
		{
			return(NULL);
		}

		//分配后设置StringData的内容
		pData->pStringMgr = this;
		pData->nRefs = 1;
		pData->nAllocLength = nAlignedChars - 1;//由于上面+1,这里-1 ,应该是防止可能的越界问题
		pData->nDataLength = 0;
		return(pData);
	}
	
	virtual void Free(IN MyCStringData* pData) throw()
	{
		assert(pData != NULL);
		assert(pData->pStringMgr == this);
		m_pMemMgr->Free(pData);
	}
	
	virtual MyCStringData* Reallocate(IN_OUT  MyCStringData* pData, IN int nChars, IN int nCharSize) throw()
	{
		MYATLENSURE_RETURN_VAL(nChars >= 0, NULL);
		assert(pData->pStringMgr == this);
		MyCStringData* pNewData;
		ULONG nTotalSize;
		ULONG nDataBytes;

		if(FAILED(MyAtlAdd(&nChars, nChars, 1)))
		{
			return NULL;
		}

		int nAlignedChars = MyAlignUp(nChars, 8);  // Prevent excessive reallocation.  The heap will usually round up anyway.
		MYATLENSURE_RETURN_VAL(nChars <= nAlignedChars, NULL);

		if(FAILED(MyAtlMultiply(&nDataBytes, static_cast<ULONG>(nAlignedChars), static_cast<ULONG>(nCharSize))) ||
		        FAILED(MyAtlAdd(&nTotalSize, static_cast<ULONG>(sizeof(MyCStringData)), nDataBytes)))
		{
			return NULL;
		}

		pNewData = static_cast<MyCStringData*>(m_pMemMgr->Reallocate(pData, nTotalSize));

		if(pNewData == NULL)
		{
			return NULL;
		}

		pNewData->nAllocLength = nAlignedChars - 1;
		return pNewData;
	}
	
	virtual MyCStringData* GetNilString() throw()
	{
		//GetNilString 返回一个空字符串,用于CString 的默认初始化,通过返回共享的StringData并增加1个引用数,优化性能
		m_nil.AddRef();
		return &m_nil;
	}
	
	virtual IMyStrMgr* Clone() throw()
	{
		return this;//记住这个Clone函数干甚么了
	}

protected:
	MyWin32Heap* m_pMemMgr;
	MyNilStringData m_nil;
};

//MyStaticString 应该是对静态字符串常量的封装,目前没有用过,需要几个宏来配合(原著中有)
template< typename BaseType, const int t_nSize >
class MyStaticString
{
public:
	MyStaticString(_In_z_ const BaseType* psz) : m_psz(psz)
	{
	}

	operator const BaseType*() const
	{
		return m_psz;
	}

	static int __cdecl GetLength()
	{
		return (t_nSize / sizeof(BaseType)) - 1;
	}

private:
	const BaseType* m_psz;

private:
	MyStaticString(IN const MyStaticString& str) throw();
	MyStaticString& operator=(IN const MyStaticString& str) throw();
};

//MyChTraitsBase 是对字符类型的封装,提供XCHAR 和YCHAR X-Y 是宽窄字符的相对关系,当支持宽窄转换时 就是从X-Y的转换
template< typename BaseType = char >
class MyChTraitsBase
{
public:
	typedef char XCHAR;
	typedef LPSTR PXSTR;
	typedef LPCSTR PCXSTR;
	typedef wchar_t YCHAR;
	typedef LPWSTR PYSTR;
	typedef LPCWSTR PCYSTR;
};

template<>
class MyChTraitsBase< wchar_t >
{
public:
	typedef wchar_t XCHAR;
	typedef LPWSTR PXSTR;
	typedef LPCWSTR PCXSTR;
	typedef char YCHAR;
	typedef LPSTR PYSTR;
	typedef LPCSTR PCYSTR;
};

template< typename BaseType >
class MyCSimpleStringT
{
public:
	typedef typename MyChTraitsBase< BaseType >::XCHAR XCHAR;
	typedef typename MyChTraitsBase< BaseType >::PXSTR PXSTR;
	typedef typename MyChTraitsBase< BaseType >::PCXSTR PCXSTR;
	typedef typename MyChTraitsBase< BaseType >::YCHAR YCHAR;
	typedef typename MyChTraitsBase< BaseType >::PYSTR PYSTR;
	typedef typename MyChTraitsBase< BaseType >::PCYSTR PCYSTR;

public:
	explicit MyCSimpleStringT(IN_OUT IMyStrMgr* pStringMgr)
	{
		//默认构造 data指向NilString
		ATLENSURE(pStringMgr != NULL);
		MyCStringData* pData = pStringMgr->GetNilString();
		Attach(pData);
	}

	MyCSimpleStringT(IN const MyCSimpleStringT& strSrc)
	{
		//拷贝构造 克隆srcData CloneData 内有点逻辑
		MyCStringData* pSrcData = strSrc.GetData();
		MyCStringData* pNewData = CloneData(pSrcData);
		Attach(pNewData);
	}

	MyCSimpleStringT(_In_z_ PCXSTR pszSrc, IN_OUT IMyStrMgr* pStringMgr)
	{
		ATLENSURE(pStringMgr != NULL);
		int nLength = StringLength(pszSrc);
		MyCStringData* pData = pStringMgr->Allocate(nLength, sizeof(XCHAR));

		if(pData == NULL)
		{
			ThrowMemoryException();
		}

		Attach(pData);
		SetLength(nLength);
		CopyChars(m_pszData, nLength, pszSrc, nLength);
	}

	MyCSimpleStringT(_In_reads_(nLength) const XCHAR* pchSrc, IN int nLength, IN_OUT IMyStrMgr* pStringMgr)
	{
		ATLENSURE(pStringMgr != NULL);

		if(pchSrc == NULL && nLength != 0)
			AtlThrow(E_INVALIDARG);

		MyCStringData* pData = pStringMgr->Allocate(nLength, sizeof(XCHAR));

		if(pData == NULL)
		{
			ThrowMemoryException();
		}

		Attach(pData);
		SetLength(nLength);
		CopyChars(m_pszData, nLength, pchSrc, nLength);
	}

	~MyCSimpleStringT() throw()
	{
		MyCStringData* pData = GetData();
		pData->Release();
	}

	operator MyCSimpleStringT < BaseType  > &()
	{
		return *(MyCSimpleStringT < BaseType > *)this;
	}

	MyCSimpleStringT& operator=(IN const MyCSimpleStringT& strSrc)
	{
		MyCStringData* pSrcData = strSrc.GetData();
		MyCStringData* pOldData = GetData();

		if(pSrcData != pOldData)
		{
			if(pOldData->IsLocked() || pSrcData->pStringMgr != pOldData->pStringMgr)
			{
				SetString(strSrc.GetString(), strSrc.GetLength());
			}
			else
			{
				MyCStringData* pNewData = CloneData(pSrcData);
				pOldData->Release();
				Attach(pNewData);
			}
		}

		return(*this);
	}

	MyCSimpleStringT& operator=(_In_opt_z_ PCXSTR pszSrc)
	{
		SetString(pszSrc);
		return(*this);
	}

	MyCSimpleStringT& operator+=(IN const MyCSimpleStringT& strSrc)
	{
		Append(strSrc);
		return(*this);
	}

	MyCSimpleStringT& operator+=(_In_z_ PCXSTR pszSrc)
	{
		Append(pszSrc);
		return(*this);
	}
	template< int t_nSize >
	MyCSimpleStringT& operator+=(IN const MyStaticString< XCHAR, t_nSize >& strSrc)
	{
		Append(static_cast<const XCHAR *>(strSrc), strSrc.GetLength());
		return(*this);
	}
	MyCSimpleStringT& operator+=(IN char ch)
	{
		AppendChar(XCHAR(ch));
		return(*this);
	}
	MyCSimpleStringT& operator+=(IN unsigned char ch)
	{
		AppendChar(XCHAR(ch));
		return(*this);
	}
	MyCSimpleStringT& operator+=(IN wchar_t ch)
	{
		AppendChar(XCHAR(ch));
		return(*this);
	}

	XCHAR operator[](IN int iChar) const
	{
		assert((iChar >= 0) && (iChar <= GetLength()));  // Indexing the '\0' is OK

		if((iChar < 0) || (iChar > GetLength()))
			AtlThrow(E_INVALIDARG);

		return(m_pszData[iChar]);
	}

	operator PCXSTR() const throw()
	{
		return(m_pszData);
	}

	void Append(_In_z_ PCXSTR pszSrc)
	{
		Append(pszSrc, StringLength(pszSrc));
	}
	void Append(_In_reads_(nLength) PCXSTR pszSrc, IN int nLength)
	{
		// See comment in SetString() about why we do this
		UINT_PTR nOffset = pszSrc - GetString();
		UINT nOldLength = GetLength();

		if(nOldLength < 0)
		{
			// protects from underflow
			nOldLength = 0;
		}

		//Make sure the nLength is greater than zero
		ATLENSURE_THROW(nLength >= 0, E_INVALIDARG);
		//Make sure we don't read pass end of the terminating NULL
		nLength = StringLengthN(pszSrc, nLength);
		//Make sure after the string doesn't exceed INT_MAX after appending
		ATLENSURE_THROW(INT_MAX - nLength >= static_cast<int>(nOldLength), E_INVALIDARG);
		int nNewLength = nOldLength + nLength;
		PXSTR pszBuffer = GetBuffer(nNewLength);

		if(nOffset <= nOldLength)
		{
			pszSrc = pszBuffer + nOffset;
			// No need to call CopyCharsOverlapped, since the destination is
			// beyond the end of the original buffer
		}

		CopyChars(pszBuffer + nOldLength, nLength, pszSrc, nLength);
		ReleaseBufferSetLength(nNewLength);
	}

	void AppendChar(IN XCHAR ch)
	{
		UINT nOldLength = GetLength();
		int nNewLength = nOldLength + 1;
		PXSTR pszBuffer = GetBuffer(nNewLength);
		pszBuffer[nOldLength] = ch;
		ReleaseBufferSetLength(nNewLength);
	}

	void Append(IN const MyCSimpleStringT& strSrc)
	{
		Append(strSrc.GetString(), strSrc.GetLength());
	}

	void Empty() throw()
	{
		MyCStringData* pOldData = GetData();
		IMyStrMgr* pStringMgr = pOldData->pStringMgr;

		if(pOldData->nDataLength == 0)
		{
			return;
		}

		if(pOldData->IsLocked())
		{
			// Don't reallocate a locked buffer that's shrinking
			SetLength(0);
		}
		else
		{
			pOldData->Release();
			MyCStringData* pNewData = pStringMgr->GetNilString();
			Attach(pNewData);
		}
	}
	void FreeExtra()
	{
		MyCStringData* pOldData = GetData();
		int nLength = pOldData->nDataLength;
		IMyStrMgr* pStringMgr = pOldData->pStringMgr;

		if(pOldData->nAllocLength == nLength)
		{
			return;
		}

		if(!pOldData->IsLocked())        // Don't reallocate a locked buffer that's shrinking
		{
			MyCStringData* pNewData = pStringMgr->Allocate(nLength, sizeof(XCHAR));

			if(pNewData == NULL)
			{
				SetLength(nLength);
				return;
			}

			CopyChars(PXSTR(pNewData->data()), nLength,
			          PCXSTR(pOldData->data()), nLength);
			pOldData->Release();
			Attach(pNewData);
			SetLength(nLength);
		}
	}

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

	XCHAR GetAt(IN int iChar) const
	{
		assert((iChar >= 0) && (iChar <= GetLength()));  // Indexing the '\0' is OK

		if((iChar < 0) || (iChar > GetLength()))
			AtlThrow(E_INVALIDARG);

		return(m_pszData[iChar]);
	}

	PXSTR GetBuffer()
	{
		MyCStringData* pData = GetData();

		if(pData->IsShared())
		{
			Fork(pData->nDataLength);
		}

		return(m_pszData);
	}

	_Ret_notnull_ _Post_writable_size_(nMinBufferLength + 1) PXSTR GetBuffer(IN int nMinBufferLength)
	{
		return(PrepareWrite(nMinBufferLength));
	}

	_Ret_notnull_ _Post_writable_size_(nLength + 1) PXSTR GetBufferSetLength(IN int nLength)
	{
		PXSTR pszBuffer = GetBuffer(nLength);
		SetLength(nLength);
		return(pszBuffer);
	}

	int GetLength() const throw()
	{
		return(GetData()->nDataLength);
	}

	IMyStrMgr* GetManager() const throw()
	{
		IMyStrMgr* pStringMgr = GetData()->pStringMgr;
		return pStringMgr ? pStringMgr->Clone() : NULL;
	}

	PCXSTR GetString() const throw()
	{
		return(m_pszData);
	}

	bool IsEmpty() const throw()
	{
		return(GetLength() == 0);
	}

	PXSTR LockBuffer()
	{
		MyCStringData* pData = GetData();

		if(pData->IsShared())
		{
			Fork(pData->nDataLength);
			pData = GetData();  // Do it again, because the fork might have changed it
		}

		pData->Lock();
		return(m_pszData);
	}

	void UnlockBuffer() throw()
	{
		MyCStringData* pData = GetData();
		pData->Unlock();
	}

	void Preallocate(IN int nLength)
	{
		PrepareWrite(nLength);
	}

	void ReleaseBuffer(IN int nNewLength = -1)
	{
		if(nNewLength == -1)
		{
			int nAlloc = GetData()->nAllocLength;
			nNewLength = StringLengthN(m_pszData, nAlloc);
		}

		SetLength(nNewLength);
	}

	void ReleaseBufferSetLength(IN int nNewLength)
	{
		assert(nNewLength >= 0);
		SetLength(nNewLength);
	}

	void Truncate(IN int nNewLength)
	{
		assert(nNewLength <= GetLength());
		GetBuffer(nNewLength);
		ReleaseBufferSetLength(nNewLength);
	}

	void SetAt(IN int iChar, IN XCHAR ch)
	{
		assert((iChar >= 0) && (iChar < GetLength()));

		if((iChar < 0) || (iChar >= GetLength()))
			AtlThrow(E_INVALIDARG);

		int nLength = GetLength();
		PXSTR pszBuffer = GetBuffer();
		pszBuffer[iChar] = ch;
		ReleaseBufferSetLength(nLength);
	}

	void SetManager(IN_OUT IMyStrMgr* pStringMgr)
	{
		assert(IsEmpty());
		MyCStringData* pData = GetData();
		pData->Release();
		pData = pStringMgr->GetNilString();
		Attach(pData);
	}

	void SetString(_In_opt_z_ PCXSTR pszSrc)
	{
		SetString(pszSrc, StringLength(pszSrc));
	}

	void SetString(_In_reads_opt_(nLength) PCXSTR pszSrc, IN int nLength)
	{
		if(nLength == 0)
		{
			Empty();
		}
		else
		{
			// It is possible that pszSrc points to a location inside of our
			// buffer.  GetBuffer() might change m_pszData if (1) the buffer
			// is shared or (2) the buffer is too small to hold the new
			// string.  We detect this aliasing, and modify pszSrc to point
			// into the newly allocated buffer instead.
			if(pszSrc == NULL)
				AtlThrow(E_INVALIDARG);

			UINT nOldLength = GetLength();
			UINT_PTR nOffset = pszSrc - GetString();
			// If 0 <= nOffset <= nOldLength, then pszSrc points into our
			// buffer
			PXSTR pszBuffer = GetBuffer(nLength);

			if(nOffset <= nOldLength)
			{
				CopyCharsOverlapped(pszBuffer, GetAllocLength(),
				                    pszBuffer + nOffset, nLength);
			}
			else
			{
				CopyChars(pszBuffer, GetAllocLength(), pszSrc, nLength);
			}

			ReleaseBufferSetLength(nLength);
		}
	}

public:
	friend MyCSimpleStringT operator+(IN const MyCSimpleStringT& str1, IN const MyCSimpleStringT& str2)
	{
		MyCSimpleStringT s(str1.GetManager());
		Concatenate(s, str1, str1.GetLength(), str2, str2.GetLength());
		return(s);
	}

	friend MyCSimpleStringT operator+(IN const MyCSimpleStringT& str1, _In_z_ PCXSTR psz2)
	{
		MyCSimpleStringT s(str1.GetManager());
		Concatenate(s, str1, str1.GetLength(), psz2, StringLength(psz2));
		return(s);
	}

	friend MyCSimpleStringT operator+(_In_z_ PCXSTR psz1, IN const MyCSimpleStringT& str2)
	{
		MyCSimpleStringT s(str2.GetManager());
		Concatenate(s, psz1, StringLength(psz1), str2, str2.GetLength());
		return(s);
	}

	__declspec(deprecated("MyCSimpleStringT::CopyChars must be passed a buffer size"))
	static void __cdecl CopyChars(_Out_writes_(nChars) XCHAR* pchDest,
	                              _In_reads_opt_(nChars) const XCHAR* pchSrc,
	                              IN int nChars) throw()
	{
		if(pchSrc != NULL)
		{
#pragma warning (push)
#pragma warning(disable : 4996)
			memcpy(pchDest, pchSrc, nChars * sizeof(XCHAR));
#pragma warning (pop)
		}
	}
	static void __cdecl CopyChars(
	    _Out_writes_to_(nDestLen, nChars) XCHAR* pchDest,
	    IN size_t nDestLen,
	    _In_reads_opt_(nChars) const XCHAR* pchSrc,
	    IN int nChars) throw()
	{
		memcpy_s(pchDest, nDestLen * sizeof(XCHAR),
		         pchSrc, nChars * sizeof(XCHAR));
	}

	__declspec(deprecated("MyCSimpleStringT::CopyCharsOverlapped must be passed a buffer size"))
	static void __cdecl CopyCharsOverlapped(
	    _Out_writes_(nChars) XCHAR* pchDest,
	    _In_reads_(nChars) const XCHAR* pchSrc,
	    IN int nChars) throw()
	{
#pragma warning (push)
#pragma warning(disable : 4996)
		memmove(pchDest, pchSrc, nChars * sizeof(XCHAR));
#pragma warning (pop)
	}
	static void __cdecl CopyCharsOverlapped(
	    _Out_writes_to_(nDestLen, nDestLen) XCHAR* pchDest,
	    IN size_t nDestLen,
	    _In_reads_(nChars) const XCHAR* pchSrc,
	    IN int nChars) throw()
	{
		memmove_s(pchDest, nDestLen * sizeof(XCHAR),
		          pchSrc, nChars * sizeof(XCHAR));
	}
	static int __cdecl StringLength(_In_opt_z_ const char* psz) throw()
	{
		if(psz == NULL)
		{
			return(0);
		}

		return(int(strlen(psz)));
	}
	static int __cdecl StringLength(_In_opt_z_ const wchar_t* psz) throw()
	{
		if(psz == NULL)
		{
			return(0);
		}

		return(int(wcslen(psz)));
	}
	static int __cdecl StringLengthN(
	    _In_reads_opt_z_(sizeInXChar) const char* psz,
	    IN size_t sizeInXChar) throw()
	{
		if(psz == NULL)
		{
			return(0);
		}

		return(int(strnlen(psz, sizeInXChar)));
	}
	static int __cdecl StringLengthN(
	    _In_reads_opt_z_(sizeInXChar) const wchar_t* psz,
	    IN size_t sizeInXChar) throw()
	{
		if(psz == NULL)
		{
			return(0);
		}

		return(int(wcsnlen(psz, sizeInXChar)));
	}

protected:
	static void __cdecl Concatenate(
	    IN_OUT MyCSimpleStringT& strResult,
	    _In_reads_(nLength1) PCXSTR psz1,
	    IN int nLength1,
	    _In_reads_(nLength2) PCXSTR psz2,
	    IN int nLength2)
	{
		int nNewLength = nLength1 + nLength2;
		PXSTR pszBuffer = strResult.GetBuffer(nNewLength);
		CopyChars(pszBuffer, nLength1, psz1, nLength1);
		CopyChars(pszBuffer + nLength1, nLength2, psz2, nLength2);
		strResult.ReleaseBufferSetLength(nNewLength);
	}
	__declspec(noinline) __declspec(noreturn) static void __cdecl ThrowMemoryException()
	{
		throw ATL::CAtlException(E_OUTOFMEMORY);
	}

	// Implementation
private:
	void Attach(IN_OUT MyCStringData* pData) throw()
	{
		m_pszData = static_cast<PXSTR>(pData->data());
	}
	ATL_NOINLINE void Fork(IN int nLength)
	{
		MyCStringData* pOldData = GetData();
		int nOldLength = pOldData->nDataLength;
		MyCStringData* pNewData = pOldData->pStringMgr->Clone()->Allocate(nLength, sizeof(XCHAR));

		if(pNewData == NULL)
		{
			ThrowMemoryException();
		}

		int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength) + 1;  // Copy '\0'
		CopyChars(PXSTR(pNewData->data()), nCharsToCopy,
		          PCXSTR(pOldData->data()), nCharsToCopy);
		pNewData->nDataLength = nOldLength;
		pOldData->Release();
		Attach(pNewData);
	}
	MyCStringData* GetData() const throw()
	{
		return(reinterpret_cast<MyCStringData*>(m_pszData) - 1);
	}
	PXSTR PrepareWrite(IN int nLength)
	{
		if(nLength < 0)
			AtlThrow(E_INVALIDARG);

		MyCStringData* pOldData = GetData();
		int nShared = 1 - pOldData->nRefs;  // nShared < 0 means true, >= 0 means false
		int nTooShort = pOldData->nAllocLength - nLength;  // nTooShort < 0 means true, >= 0 means false

		if((nShared | nTooShort) < 0)        // If either sign bit is set (i.e. either is less than zero), we need to copy data
		{
			PrepareWrite2(nLength);
		}

		return(m_pszData);
	}
	ATL_NOINLINE void PrepareWrite2(IN int nLength)
	{
		MyCStringData* pOldData = GetData();

		if(pOldData->nDataLength > nLength)
		{
			nLength = pOldData->nDataLength;
		}

		if(pOldData->IsShared())
		{
			Fork(nLength);
		}
		else if(pOldData->nAllocLength < nLength)
		{
			// Grow exponentially, until we hit 1G, then by 1M thereafter.
			int nNewLength = pOldData->nAllocLength;

			if(nNewLength > 1024 * 1024 * 1024)
			{
				nNewLength += 1024 * 1024;
			}
			else
			{
				// Exponential growth factor is 1.5.
				nNewLength = nNewLength + nNewLength / 2;
			}

			if(nNewLength < nLength)
			{
				nNewLength = nLength;
			}

			Reallocate(nNewLength);
		}
	}
	ATL_NOINLINE void Reallocate(IN int nLength)
	{
		MyCStringData* pOldData = GetData();
		assert(pOldData->nAllocLength < nLength);
		IMyStrMgr* pStringMgr = pOldData->pStringMgr;

		if(pOldData->nAllocLength >= nLength || nLength <= 0)
		{
			ThrowMemoryException();
			return;
		}

		MyCStringData* pNewData = pStringMgr->Reallocate(pOldData, nLength, sizeof(XCHAR));

		if(pNewData == NULL)
		{
			ThrowMemoryException();
		}

		Attach(pNewData);
	}

	void SetLength(IN int nLength)
	{
		assert(nLength >= 0);
		assert(nLength <= GetData()->nAllocLength);

		if(nLength < 0 || nLength > GetData()->nAllocLength)
			AtlThrow(E_INVALIDARG);

		GetData()->nDataLength = nLength;
		m_pszData[nLength] = 0;
	}

	static MyCStringData* __cdecl CloneData(IN_OUT MyCStringData* pData)
	{
		MyCStringData* pNewData = NULL;
		IMyStrMgr* pNewStringMgr = pData->pStringMgr->Clone();

		if(!pData->IsLocked() && (pNewStringMgr == pData->pStringMgr))
		{
			pNewData = pData;
			pNewData->AddRef();
		}
		else
		{
			pNewData = pNewStringMgr->Allocate(pData->nDataLength, sizeof(XCHAR));

			if(pNewData == NULL)
			{
				ThrowMemoryException();
			}

			pNewData->nDataLength = pData->nDataLength;
			CopyChars(PXSTR(pNewData->data()), pData->nDataLength + 1,
			          PCXSTR(pData->data()), pData->nDataLength + 1);  // Copy '\0'
		}

		return(pNewData);
	}

private:
	PXSTR m_pszData;
};

/******************************************************************************************
*函数:GetCurrentModule
*功能:获取模块自身的基地址,如果在DLL内使用,就是那个dll的模块基地址,如果在exe内调用,
*则是这个exe的基地址
*返回值:模块基地址,HMODULE,其实是个DWORD的地址值
*******************************************************************************************/
__inline HMODULE MyGetCurrentModule(VOID)
{
	/*没有使用宏判断windows的版本*/
	MEMORY_BASIC_INFORMATION membinfo;
	static int ibase = 0;//这里要是静态变量,否则失败
	VirtualQuery(&ibase, &membinfo, sizeof(membinfo));
	return (HMODULE)membinfo.AllocationBase; //可转换为IMAGE_DOS_HEADER(内存中的),本来就是
}

inline int MyAtlStrLen(_In_opt_z_ const wchar_t *str)
{
	if(str == NULL)
		return 0;

	return static_cast<int>(::wcslen(str));
}

inline int MyAtlStrLen(_In_opt_z_ const char *str)
{
	if(str == NULL)
		return 0;

	return static_cast<int>(::strlen(str));
}

template <typename _CharType  >
class MyStringTraits
{
public:
	static HINSTANCE FindStringResourceInstance(IN UINT nID) throw()
	{
		static HMODULE hBase = MyGetCurrentModule();
		return hBase;
	}
	static IMyStrMgr* GetDefaultManager() throw()
	{
		return MyStrMgr::GetInstance();
	}

	static LPWSTR __cdecl CharNext(_In_ LPCWSTR psz) throw()
	{
		return const_cast<LPWSTR>(psz + 1);
	}

	static int __cdecl IsDigit(_In_ wchar_t ch) throw()
	{
		return iswdigit(static_cast<unsigned short>(ch));
	}

	static int __cdecl IsSpace(_In_ wchar_t ch) throw()
	{
		return iswspace(static_cast<unsigned short>(ch));
	}

	static int __cdecl StringCompare(
	    _In_z_ LPCWSTR pszA,
	    _In_z_ LPCWSTR pszB) throw()
	{
		return wcscmp(pszA, pszB);
	}

	static int __cdecl StringCompareIgnore(
	    _In_z_ LPCWSTR pszA,
	    _In_z_ LPCWSTR pszB) throw()
	{
		return _wcsicmp(pszA, pszB);
	}

	static int __cdecl StringCollate(
	    _In_z_ LPCWSTR pszA,
	    _In_z_ LPCWSTR pszB) throw()
	{
		return wcscoll(pszA, pszB);
	}

	static int __cdecl StringCollateIgnore(
	    _In_z_ LPCWSTR pszA,
	    _In_z_ LPCWSTR pszB) throw()
	{
		return _wcsicoll(pszA, pszB);
	}

	static LPCWSTR __cdecl StringFindString(
	    _In_z_ LPCWSTR pszBlock,
	    _In_z_ LPCWSTR pszMatch) throw()
	{
		return wcsstr(pszBlock, pszMatch);
	}

	static LPWSTR __cdecl StringFindString(
	    _In_z_ LPWSTR pszBlock,
	    _In_z_ LPCWSTR pszMatch) throw()
	{
		return(const_cast<LPWSTR>(StringFindString(const_cast<LPCWSTR>(pszBlock), pszMatch)));
	}

	static LPCWSTR __cdecl StringFindChar(
	    _In_z_ LPCWSTR pszBlock,
	    _In_ wchar_t chMatch) throw()
	{
		return wcschr(pszBlock, chMatch);
	}

	static LPCWSTR __cdecl StringFindCharRev(
	    _In_z_ LPCWSTR psz,
	    _In_ wchar_t ch) throw()
	{
		return wcsrchr(psz, ch);
	}

	static LPCWSTR __cdecl StringScanSet(
	    _In_z_ LPCWSTR pszBlock,
	    _In_z_ LPCWSTR pszMatch) throw()
	{
		return wcspbrk(pszBlock, pszMatch);
	}

	static int __cdecl StringSpanIncluding(
	    _In_z_ LPCWSTR pszBlock,
	    _In_z_ LPCWSTR pszSet) throw()
	{
		return (int)wcsspn(pszBlock, pszSet);
	}

	static int __cdecl StringSpanExcluding(
	    _In_z_ LPCWSTR pszBlock,
	    _In_z_ LPCWSTR pszSet) throw()
	{
		return (int)wcscspn(pszBlock, pszSet);
	}

	static LPWSTR __cdecl StringUppercase(
	    _Inout_updates_(size) LPWSTR psz,
	    _In_ size_t size) throw()
	{
		errno_t err = _wcsupr_s(psz, size);
		return (err == 0) ? psz : NULL;
	}

	static LPWSTR __cdecl StringLowercase(
	    _Inout_updates_(size) LPWSTR psz,
	    _In_ size_t size) throw()
	{
		errno_t err = _wcslwr_s(psz, size);
		return (err == 0) ? psz : NULL;
	}

	static LPWSTR __cdecl StringReverse(_Inout_z_ LPWSTR psz) throw()
	{
		return _wcsrev(psz);
	}

	static int __cdecl GetFormattedLength(
	    _In_z_ _Printf_format_string_ LPCWSTR pszFormat, va_list args) throw()
	{
#if _MSC_VER < 1900
		return _vscwprintf(pszFormat, args);
#else
		// Explicitly request the legacy wide format specifiers mode from the CRT,
		// for compatibility with previous versions.  While the CRT supports two
		// modes, the ATL and MFC functions that accept format strings only support
		// legacy mode format strings.
		int const result = __stdio_common_vswprintf(
		                       _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS |
		                       _CRT_INTERNAL_PRINTF_STANDARD_SNPRINTF_BEHAVIOR |
		                       _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS,
		                       NULL, 0, pszFormat, NULL, args);
		return result < 0 ? -1 : result;
#endif
	}

	static int __cdecl Format(
	    _Out_writes_(nLength) LPWSTR pszBuffer,
	    _In_ size_t nLength,
	    _In_ _Printf_format_string_ LPCWSTR pszFormat, va_list args) throw()
	{
#if _MSC_VER < 1900
		return vswprintf_s(pszBuffer, nLength, pszFormat, args);
#else
		// Explicitly request the legacy wide format specifiers mode from the CRT,
		// for compatibility with previous versions.  While the CRT supports two
		// modes, the ATL and MFC functions that accept format strings only support
		// legacy mode format strings.
		int const result = __stdio_common_vswprintf_s(
		                       _CRT_INTERNAL_LOCAL_PRINTF_OPTIONS |
		                       _CRT_INTERNAL_PRINTF_LEGACY_WIDE_SPECIFIERS,
		                       pszBuffer, nLength, pszFormat, NULL, args);
		return result < 0 ? -1 : result;
#endif
	}

	static int __cdecl GetBaseTypeLength(_In_z_ LPCSTR pszSrc) throw()
	{
		// Returns required buffer size in wchar_ts
		return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, -1, NULL, 0) - 1;
	}

	static int __cdecl GetBaseTypeLength(
	    _In_reads_(nLength) LPCSTR pszSrc,
	    _In_ int nLength) throw()
	{
		// Returns required buffer size in wchar_ts
		return ::MultiByteToWideChar(CP_ACP, 0, pszSrc, nLength, NULL, 0);
	}

	static int __cdecl GetBaseTypeLength(_In_z_ LPCWSTR pszSrc) throw()
	{
		// Returns required buffer size in wchar_ts
		return MyAtlStrLen(pszSrc);
	}

	static int __cdecl GetBaseTypeLength(
	    _In_reads_(nLength) LPCWSTR pszSrc,
	    _In_ int nLength) throw()
	{
		(void)pszSrc;
		// Returns required buffer size in wchar_ts
		return nLength;
	}

	static void __cdecl ConvertToBaseType(
	    _Out_writes_(nDestLength) LPWSTR pszDest,
	    _In_ int nDestLength,
	    _In_ LPCSTR pszSrc,
	    _In_ int nSrcLength = -1) throw()
	{
		// nLen is in wchar_ts
		::MultiByteToWideChar(CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength);
	}

	static void __cdecl ConvertToBaseType(
	    _Out_writes_(nDestLength) LPWSTR pszDest,
	    _In_ int nDestLength,
	    _In_ LPCWSTR pszSrc,
	    _In_ int nSrcLength = -1)
	{
		if(nSrcLength == -1)
		{
			nSrcLength = 1 + GetBaseTypeLength(pszSrc);
		}

		// nLen is in wchar_ts
		wmemcpy_s(pszDest, nDestLength, pszSrc, nSrcLength);
	}

	static void __cdecl FloodCharacters(
	    _In_ wchar_t ch,
	    _In_ int nLength,
	    _Out_writes_all_(nLength) WCHAR *psz) throw()
	{
		// nLength is in XCHARs
		for(int i = 0; i < nLength; i++)
		{
			psz[i] = ch;
		}
	}

	_Ret_maybenull_z_ static BSTR __cdecl AllocSysString(
	    _In_reads_(nDataLength) const wchar_t* pchData,
	    _In_ int nDataLength) throw()
	{
		return ::SysAllocStringLen(pchData, nDataLength);
	}

	static BOOL __cdecl ReAllocSysString(
	    _In_reads_(nDataLength) const wchar_t* pchData,
	    _Inout_ _Deref_post_opt_valid_ _Post_z_ BSTR* pbstr,
	    _In_ int nDataLength) throw()
	{
		return ::SysReAllocStringLen(pbstr, pchData, nDataLength);
	}

	static int __cdecl SafeStringLen(_In_opt_z_ LPCSTR psz) throw()
	{
		// returns length in bytes
		return (psz != NULL) ? (int)strlen(psz) : 0;
	}

	static int __cdecl SafeStringLen(_In_opt_z_ LPCWSTR psz) throw()
	{
		// returns length in wchar_ts
		return (psz != NULL) ? (int)wcslen(psz) : 0;
	}

	static int __cdecl GetCharLen(_In_opt_z_ const wchar_t* pch) throw()
	{
		(void)pch;
		// returns char length
		return 1;
	}

	static int __cdecl GetCharLen(_In_z_ const char* pch) throw()
	{
		// returns char length
		return (int)(_mbclen(reinterpret_cast<const unsigned char*>(pch)));
	}

#ifdef _ATL_USE_WINAPI_FAMILY_DESKTOP_APP

	static void __cdecl ConvertToOem(_In_opt_z_ LPWSTR /*psz*/)
	{
		ATLENSURE(FALSE); // Unsupported Feature
	}

	static void __cdecl ConvertToAnsi(_In_opt_z_ LPWSTR /*psz*/)
	{
		ATLENSURE(FALSE); // Unsupported Feature
	}

	static void __cdecl ConvertToOem(
	    _In_reads_(nLen) LPWSTR /*psz*/,
	    _In_ size_t nLen)
	{
		UNREFERENCED_PARAMETER(nLen);
		ATLENSURE(FALSE); // Unsupported Feature
	}

	static void __cdecl ConvertToAnsi(
	    _In_reads_(nLen) LPWSTR /*psz*/,
	    _In_ size_t nLen)
	{
		UNREFERENCED_PARAMETER(nLen);
		ATLENSURE(FALSE); // Unsupported Feature
	}

#endif // _ATL_USE_WINAPI_FAMILY_DESKTOP_APP


	static char* __cdecl CharNext(IN const char* p) throw()
	{
		return reinterpret_cast<char*>(_mbsinc(reinterpret_cast<const unsigned char*>(p)));
	}

	static int __cdecl IsDigit(IN char ch) throw()
	{
		return _ismbcdigit(ch);
	}

	static int __cdecl IsSpace(IN char ch) throw()
	{
		return _ismbcspace(ch);
	}

	static int __cdecl StringCompare(_In_z_ LPCSTR pszA, _In_z_ LPCSTR pszB) throw()
	{
		return _mbscmp(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
	}

	static int __cdecl StringCompareIgnore(_In_z_ LPCSTR pszA, _In_z_ LPCSTR pszB) throw()
	{
		return _mbsicmp(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
	}

	static int __cdecl StringCollate(
	    _In_z_ LPCSTR pszA,
	    _In_z_ LPCSTR pszB) throw()
	{
		return _mbscoll(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
	}

	static int __cdecl StringCollateIgnore(
	    _In_z_ LPCSTR pszA,
	    _In_z_ LPCSTR pszB) throw()
	{
		return _mbsicoll(reinterpret_cast<const unsigned char*>(pszA), reinterpret_cast<const unsigned char*>(pszB));
	}

	static LPCSTR __cdecl StringFindString(
	    _In_z_ LPCSTR pszBlock,
	    _In_z_ LPCSTR pszMatch) throw()
	{
		return reinterpret_cast<LPCSTR>(_mbsstr(reinterpret_cast<const unsigned char*>(pszBlock),
		                                        reinterpret_cast<const unsigned char*>(pszMatch)));
	}

	static LPSTR __cdecl StringFindString(
	    _In_z_ LPSTR pszBlock,
	    _In_z_ LPCSTR pszMatch) throw()
	{
		return(const_cast<LPSTR>(StringFindString(const_cast<LPCSTR>(pszBlock), pszMatch)));
	}

	static LPCSTR __cdecl StringFindChar(_In_z_ LPCSTR pszBlock, IN char chMatch) throw()
	{
		return reinterpret_cast<LPCSTR>(_mbschr(reinterpret_cast<const unsigned char*>(pszBlock), (unsigned char)chMatch));
	}

	static LPCSTR __cdecl StringFindCharRev(_In_z_ LPCSTR psz, IN char ch) throw()
	{
		return reinterpret_cast<LPCSTR>(_mbsrchr(reinterpret_cast<const unsigned char*>(psz), (unsigned char)ch));
	}

	static LPCSTR __cdecl StringScanSet(_In_z_ LPCSTR pszBlock, _In_z_ LPCSTR pszMatch) throw()
	{
		return reinterpret_cast<LPCSTR>(_mbspbrk(reinterpret_cast<const unsigned char*>(pszBlock),
		                                reinterpret_cast<const unsigned char*>(pszMatch)));
	}

	static int __cdecl StringSpanIncluding(
	    _In_z_ LPCSTR pszBlock,
	    _In_z_ LPCSTR pszSet) throw()
	{
		return (int)_mbsspn(reinterpret_cast<const unsigned char*>(pszBlock), reinterpret_cast<const unsigned char*>(pszSet));
	}

	static int __cdecl StringSpanExcluding(LPCSTR pszBlock, LPCSTR pszSet) throw()
	{
		return (int)_mbscspn(reinterpret_cast<const unsigned char*>(pszBlock), reinterpret_cast<const unsigned char*>(pszSet));
	}

	static LPSTR __cdecl StringUppercase(_Inout_updates_z_(size) LPSTR psz, IN size_t size)
	{
		Checked::mbsupr_s(reinterpret_cast<unsigned char*>(psz), size);
		return psz;
	}

	static LPSTR __cdecl StringLowercase(_Inout_updates_z_(size) LPSTR psz, IN size_t size)
	{
		Checked::mbslwr_s(reinterpret_cast<unsigned char*>(psz), size);
		return psz;
	}

	static LPSTR __cdecl StringReverse(_Inout_z_ LPSTR psz) throw()
	{
		return reinterpret_cast<LPSTR>(_mbsrev(reinterpret_cast<unsigned char*>(psz)));
	}

	static int __cdecl GetFormattedLength(LPCSTR pszFormat, va_list args) throw()
	{
		return _vscprintf(pszFormat, args);
	}

	static int __cdecl Format(
	    _Out_writes_to_(nlength, return) LPSTR pszBuffer,
	    IN size_t nlength,
	    _In_z_ _Printf_format_string_ LPCSTR pszFormat, va_list args) throw()
	{
		return vsprintf_s(pszBuffer, nlength, pszFormat, args);
	}

	static void __cdecl ConvertToBaseType(OUT LPSTR pszDest, IN int nDestLength, _In_z_ LPCSTR pszSrc, IN int nSrcLength = -1)
	{
		if(nSrcLength == -1)
		{
			nSrcLength = 1 + GetBaseTypeLength(pszSrc);
		}

		// nLen is in XCHARs
		Checked::memcpy_s(pszDest, nDestLength * sizeof(char), pszSrc, nSrcLength * sizeof(char));
	}

	static void __cdecl ConvertToBaseType(OUT LPSTR pszDest, IN int nDestLength, IN LPCWSTR pszSrc, IN int nSrcLength = -1) throw()
	{
		// nLen is in XCHARs
		::WideCharToMultiByte(CP_ACP, 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL);
	}

	static void __cdecl FloodCharacters(IN char ch, IN int nLength, _Out_writes_all_(nLength) char* pch) throw()
	{
		// nLength is in XCHARs
		memset(pch, ch, nLength);
	}

	static BSTR __cdecl AllocSysString(_In_reads_(nDataLength) const char* pchData, IN int nDataLength) throw()
	{
		int nLen = ::MultiByteToWideChar(CP_ACP, 0, pchData, nDataLength,
		                                 NULL, NULL);
		BSTR bstr = ::SysAllocStringLen(NULL, nLen);

		if(bstr != NULL)
		{
			::MultiByteToWideChar(CP_ACP, 0, pchData, nDataLength, bstr, nLen);
		}

		return bstr;
	}

	_Success_(return != FALSE)
	static BOOL __cdecl ReAllocSysString(
	    _In_reads_(nDataLength) const char* pchData,
	    IN_OUT _Deref_post_opt_valid_ _Post_z_ BSTR* pbstr,
	    IN int nDataLength) throw()
	{
		int nLen = ::MultiByteToWideChar(CP_ACP, 0, pchData, nDataLength, NULL, NULL);
		BOOL bSuccess = ::SysReAllocStringLen(pbstr, NULL, nLen);

		if(bSuccess && nLen > 0)
		{
			bSuccess = (0 != ::MultiByteToWideChar(CP_ACP, 0, pchData, nDataLength, *pbstr, nLen));
		}

		return bSuccess;
	}

};

#ifdef _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
#define MYSTRING_EXPLICIT explicit
#else
#define MYSTRING_EXPLICIT
#endif


template< typename BaseType >
class MyCStringT :
	public MyCSimpleStringT< BaseType>
{
public:
	typedef MyCSimpleStringT< BaseType> CThisSimpleString;
	typedef MyStringTraits<BaseType> StrTraits;
	typedef typename CThisSimpleString::XCHAR XCHAR;
	typedef typename CThisSimpleString::PXSTR PXSTR;
	typedef typename CThisSimpleString::PCXSTR PCXSTR;
	typedef typename CThisSimpleString::YCHAR YCHAR;
	typedef typename CThisSimpleString::PYSTR PYSTR;
	typedef typename CThisSimpleString::PCYSTR PCYSTR;

public:
	MyCStringT() throw() :
		CThisSimpleString(StrTraits::GetDefaultManager())
	{
	}

	static void __cdecl Construct(IN MyCStringT* pString)
	{
		new(pString) MyCStringT;
	}

	// Copy constructor
	MyCStringT(IN const MyCStringT& strSrc) :
		CThisSimpleString(strSrc)
	{
	}

	// Construct from MyCSimpleStringT
	operator MyCSimpleStringT < BaseType > &()
	{
		return *(MyCSimpleStringT < BaseType> *)this;
	}

	template <bool bMFCDLL>
	MyCStringT(IN const MyCSimpleStringT<BaseType>& strSrc) :
		CThisSimpleString(strSrc)
	{
	}

	MyCStringT(_In_opt_z_ const XCHAR* pszSrc) :
		CThisSimpleString(StrTraits::GetDefaultManager())
	{
		if(!CheckImplicitLoad(pszSrc))
		{
			*this = pszSrc;
		}
	}

	MyCStringT(
	    _In_opt_z_ const XCHAR* pszSrc,
	    IN IMyStrMgr* pStringMgr) :
		CThisSimpleString(pStringMgr)
	{
		if(!CheckImplicitLoad(pszSrc))
		{
			*this = pszSrc;
		}
	}

	MyCStringT(const VARIANT& varSrc);

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	MYSTRING_EXPLICIT MyCStringT(_In_opt_z_ const YCHAR* pszSrc) :
		CThisSimpleString(StrTraits::GetDefaultManager())
	{
		if(!CheckImplicitLoad(pszSrc))
		{
			*this = pszSrc;
		}
	}

	MyCStringT(
	    _In_opt_z_ const YCHAR* pszSrc,
	    IN IMyStrMgr* pStringMgr) :
		CThisSimpleString(pStringMgr)
	{
		if(!CheckImplicitLoad(pszSrc))
		{
			*this = pszSrc;
		}
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	explicit MyCStringT(
	    _In_ IMyStrMgr* pStringMgr) throw() :
		CThisSimpleString(pStringMgr)
	{
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	MYSTRING_EXPLICIT MyCStringT(_In_z_ const unsigned char* pszSrc) :
		CThisSimpleString(StrTraits::GetDefaultManager())
	{
		*this = reinterpret_cast<const char*>(pszSrc);
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION


#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	MyCStringT(
	    _In_opt_z_ const unsigned char* pszSrc,
	    IN IMyStrMgr* pStringMgr) :
		CThisSimpleString(pStringMgr)
	{
		*this = reinterpret_cast<const char*>(pszSrc);
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

#ifdef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
#define _CSTRING_CHAR_T XCHAR
#else // def _CSTRING_NARROW_WIDE_CONVERSION
#define _CSTRING_CHAR_T char
#endif // def _CSTRING_NARROW_WIDE_CONVERSION

	MYSTRING_EXPLICIT MyCStringT(
	    IN _CSTRING_CHAR_T ch,
	    IN int nLength = 1) :
		CThisSimpleString(StrTraits::GetDefaultManager())
	{
		assert(nLength >= 0);

		if(nLength > 0)
		{
			PXSTR pszBuffer = this->GetBuffer(nLength);
			StrTraits::FloodCharacters(XCHAR(ch), nLength, pszBuffer);
			this->ReleaseBufferSetLength(nLength);
		}
	}
#undef _CSTRING_CHAR_T

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	MYSTRING_EXPLICIT MyCStringT(
	    IN wchar_t ch,
	    IN int nLength = 1) :
		CThisSimpleString(StrTraits::GetDefaultManager())
	{
		assert(nLength >= 0);

		if(nLength > 0)
		{
			//Convert ch to the BaseType
			wchar_t pszCh[2] = { ch , 0 };
			int nBaseTypeCharLen = 1;

			if(ch != L'\0')
			{
				nBaseTypeCharLen = StrTraits::GetBaseTypeLength(pszCh);
			}

			CTempBuffer<XCHAR, 10> buffBaseTypeChar;
			buffBaseTypeChar.Allocate(nBaseTypeCharLen + 1);
			StrTraits::ConvertToBaseType(buffBaseTypeChar, nBaseTypeCharLen + 1, pszCh, 1);
			//Allocate enough characters in String and flood (replicate) with the (converted character)*nLength
			PXSTR pszBuffer = this->GetBuffer(nLength * nBaseTypeCharLen);

			if(nBaseTypeCharLen == 1)
			{
				//Optimization for a common case - wide char translates to 1 ansi/wide char.
				StrTraits::FloodCharacters(buffBaseTypeChar[0], nLength, pszBuffer);
			}
			else
			{
				XCHAR* p = pszBuffer;

				for(int i = 0; i < nLength; ++i)
				{
					for(int j = 0; j < nBaseTypeCharLen; ++j)
					{
						*p = buffBaseTypeChar[j];
						++p;
					}
				}
			}

			this->ReleaseBufferSetLength(nLength * nBaseTypeCharLen);
		}
	}
#else
private:
	MYSTRING_EXPLICIT MyCStringT(
	    IN YCHAR ch,
	    IN int nLength = 1);
public:
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	MyCStringT(const XCHAR* pch, int nLength) :
		CThisSimpleString(pch, nLength, StrTraits::GetDefaultManager())
	{
	}

	MyCStringT(
	    _In_reads_(nLength) const XCHAR* pch,
	    IN int nLength,
	    IN IMyStrMgr* pStringMgr) :
		CThisSimpleString(pch, nLength, pStringMgr)
	{
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	MyCStringT(
	    _In_reads_(nLength) const YCHAR* pch,
	    IN int nLength) :
		CThisSimpleString(StrTraits::GetDefaultManager())
	{
		assert(nLength >= 0);

		if(nLength > 0)
		{
			assert(AtlIsValidAddress(pch, nLength * sizeof(YCHAR), FALSE));

			if(pch == NULL)
				AtlThrow(E_INVALIDARG);

			int nDestLength = StrTraits::GetBaseTypeLength(pch, nLength);
			PXSTR pszBuffer = this->GetBuffer(nDestLength);
			StrTraits::ConvertToBaseType(pszBuffer, nDestLength, pch, nLength);
			this->ReleaseBufferSetLength(nDestLength);
		}
	}

	MyCStringT(
	    _In_reads_(nLength) const YCHAR* pch,
	    IN int nLength,
	    IN IMyStrMgr* pStringMgr) :
		CThisSimpleString(pStringMgr)
	{
		assert(nLength >= 0);

		if(nLength > 0)
		{
			assert(AtlIsValidAddress(pch, nLength * sizeof(YCHAR), FALSE));

			if(pch == NULL)
				AtlThrow(E_INVALIDARG);

			int nDestLength = StrTraits::GetBaseTypeLength(pch, nLength);
			PXSTR pszBuffer = this->GetBuffer(nDestLength);
			StrTraits::ConvertToBaseType(pszBuffer, nDestLength, pch, nLength);
			this->ReleaseBufferSetLength(nDestLength);
		}
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	// Destructor
	~MyCStringT() throw()
	{
	}

	// Assignment operators
	MyCStringT& operator=(IN const MyCStringT& strSrc)
	{
		CThisSimpleString::operator=(strSrc);
		return(*this);
	}

	MyCStringT& operator=(IN const MyCSimpleStringT<BaseType>& strSrc)
	{
		CThisSimpleString::operator=(strSrc);
		return(*this);
	}

	MyCStringT& operator=(_In_opt_z_ PCXSTR pszSrc)
	{
		CThisSimpleString::operator=(pszSrc);
		return(*this);
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	MyCStringT& operator=(_In_opt_z_ PCYSTR pszSrc)
	{
		// nDestLength is in XCHARs
		int nDestLength = (pszSrc != NULL) ? StrTraits::GetBaseTypeLength(pszSrc) : 0;

		if(nDestLength > 0)
		{
			PXSTR pszBuffer = this->GetBuffer(nDestLength);
			StrTraits::ConvertToBaseType(pszBuffer, nDestLength, pszSrc);
			this->ReleaseBufferSetLength(nDestLength);
		}
		else
		{
			this->Empty();
		}

		return(*this);
	}

	MyCStringT& operator=(_In_opt_z_ const unsigned char* pszSrc)
	{
		return(operator=(reinterpret_cast<const char*>(pszSrc)));
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	MyCStringT& operator=(IN XCHAR ch)
	{
		XCHAR ach[2] = { ch, 0 };
		return(operator=(ach));
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	MyCStringT& operator=(IN YCHAR ch)
	{
		YCHAR ach[2] = { ch, 0 };
		return(operator=(ach));
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	MyCStringT& operator=(IN const VARIANT& var);

	MyCStringT& operator+=(IN const CThisSimpleString& str)
	{
		CThisSimpleString::operator+=(str);
		return(*this);
	}

	MyCStringT& operator+=(_In_z_ PCXSTR pszSrc)
	{
		CThisSimpleString::operator+=(pszSrc);
		return(*this);
	}
	template< int t_nSize >
	MyCStringT& operator+=(IN const MyStaticString< XCHAR, t_nSize >& strSrc)
	{
		CThisSimpleString::operator+=(strSrc);
		return(*this);
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	MyCStringT& operator+=(_In_opt_z_ PCYSTR pszSrc)
	{
		MyCStringT str(pszSrc, GetManager());
		return(operator+=(str));
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	MyCStringT& operator+=(IN XCHAR ch)
	{
		CThisSimpleString::operator+=(ch);
		return(*this);
	}

#ifdef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
private:
	MyCStringT& operator+=(IN YCHAR ch);
public:
#else
	MyCStringT& operator+=(IN unsigned char ch)
	{
		CThisSimpleString::operator+=(ch);
		return(*this);
	}

	MyCStringT& operator+=(IN YCHAR ch)
	{
		CThisSimpleString::operator+=(ch);
		return(*this);
	}
#endif // def _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	MyCStringT& operator+=(IN const VARIANT& var);

	// Override from base class
	IMyStrMgr* GetManager() const throw()
	{
		IMyStrMgr* pStringMgr = CThisSimpleString::GetManager();

		if(pStringMgr)
		{
			return pStringMgr;
		}

		pStringMgr = StrTraits::GetDefaultManager();
		return pStringMgr->Clone();
	}

	// Comparison

	int Compare(_In_z_ PCXSTR psz) const
	{
		ATLENSURE(MyAtlIsValidString(psz));
		_Analysis_assume_(psz); // MyAtlIsValidString guarantees that psz != NULL
		return(StrTraits::StringCompare(this->GetString(), psz));
	}

	int CompareNoCase(_In_z_ PCXSTR psz) const
	{
		ATLENSURE(MyAtlIsValidString(psz));
		_Analysis_assume_(psz); // MyAtlIsValidString guarantees that psz != NULL
		return(StrTraits::StringCompareIgnore(this->GetString(), psz));
	}

	int Collate(_In_z_ PCXSTR psz) const throw()
	{
		assert(MyAtlIsValidString(psz));
		return(StrTraits::StringCollate(this->GetString(), psz));
	}

	int CollateNoCase(_In_z_ PCXSTR psz) const throw()
	{
		assert(MyAtlIsValidString(psz));
		return(StrTraits::StringCollateIgnore(this->GetString(), psz));
	}

	// Advanced manipulation

	// Delete 'nCount' characters, starting at index 'iIndex'
	int Delete(
	    IN int iIndex,
	    IN int nCount = 1)
	{
		if(iIndex < 0)
			iIndex = 0;

		if(nCount < 0)
			nCount = 0;

		int nLength = this->GetLength();

		if((::ATL::AtlAddThrow(nCount, iIndex)) > nLength)
		{
			nCount = nLength - iIndex;
		}

		if(nCount > 0)
		{
			int nNewLength = nLength - nCount;
			int nXCHARsToCopy = nLength - (iIndex + nCount) + 1;
			PXSTR pszBuffer = this->GetBuffer();
			Checked::memmove_s(pszBuffer + iIndex, nXCHARsToCopy * sizeof(XCHAR),
			                   pszBuffer + iIndex + nCount, nXCHARsToCopy * sizeof(XCHAR));
			this->ReleaseBufferSetLength(nNewLength);
		}

		return(this->GetLength());
	}

	// Insert character 'ch' before index 'iIndex'
	int Insert(
	    IN int iIndex,
	    IN XCHAR ch)
	{
		if(iIndex < 0)
			iIndex = 0;

		if(iIndex > this->GetLength())
		{
			iIndex = this->GetLength();
		}

		int nNewLength = this->GetLength() + 1;
		PXSTR pszBuffer = this->GetBuffer(nNewLength);
		// move existing bytes down
		Checked::memmove_s(pszBuffer + iIndex + 1, (nNewLength - iIndex) * sizeof(XCHAR),
		                   pszBuffer + iIndex, (nNewLength - iIndex) * sizeof(XCHAR));
		pszBuffer[iIndex] = ch;
		this->ReleaseBufferSetLength(nNewLength);
		return(nNewLength);
	}

	// Insert string 'psz' before index 'iIndex'
	int Insert(
	    IN int iIndex,
	    _In_z_ PCXSTR psz)
	{
		if(iIndex < 0)
			iIndex = 0;

		if(iIndex > this->GetLength())
		{
			iIndex = this->GetLength();
		}

		// nInsertLength and nNewLength are in XCHARs
		int nInsertLength = StrTraits::SafeStringLen(psz);
		int nNewLength = this->GetLength();

		if(nInsertLength > 0)
		{
			nNewLength += nInsertLength;
			PXSTR pszBuffer = this->GetBuffer(nNewLength);
			// move existing bytes down
			Checked::memmove_s(pszBuffer + iIndex + nInsertLength, (nNewLength - iIndex - nInsertLength + 1) * sizeof(XCHAR),
			                   pszBuffer + iIndex, (nNewLength - iIndex - nInsertLength + 1) * sizeof(XCHAR));
			Checked::memcpy_s(pszBuffer + iIndex, nInsertLength * sizeof(XCHAR),
			                  psz, nInsertLength * sizeof(XCHAR));
			this->ReleaseBufferSetLength(nNewLength);
		}

		return(nNewLength);
	}

	// Replace all occurrences of character 'chOld' with character 'chNew'
	int Replace(
	    IN XCHAR chOld,
	    IN XCHAR chNew)
	{
		int nCount = 0;

		// short-circuit the nop case
		if(chOld != chNew)
		{
			// otherwise modify each character that matches in the string
			bool bCopied = false;
			PXSTR pszBuffer = const_cast<PXSTR>(this->GetString());  // We don't actually write to pszBuffer until we've called GetBuffer().
			int nLength = this->GetLength();
			int iChar = 0;

			while(iChar < nLength)
			{
				// replace instances of the specified character only
				if(pszBuffer[iChar] == chOld)
				{
					if(!bCopied)
					{
						bCopied = true;
						pszBuffer = this->GetBuffer(nLength);
					}

					pszBuffer[iChar] = chNew;
					nCount++;
				}

				iChar = int(StrTraits::CharNext(pszBuffer + iChar) - pszBuffer);
			}

			if(bCopied)
			{
				this->ReleaseBufferSetLength(nLength);
			}
		}

		return(nCount);
	}

	// Replace all occurrences of string 'pszOld' with string 'pszNew'
	int Replace(
	    _In_z_ PCXSTR pszOld,
	    _In_z_ PCXSTR pszNew)
	{
		// can't have empty or NULL lpszOld
		// nSourceLen is in XCHARs
		int nSourceLen = StrTraits::SafeStringLen(pszOld);

		if(nSourceLen == 0)
			return(0);

		// nReplacementLen is in XCHARs
		int nReplacementLen = StrTraits::SafeStringLen(pszNew);
		// loop once to figure out the size of the result string
		int nCount = 0;
		{
			PCXSTR pszStart = this->GetString();
			PCXSTR pszEnd = pszStart + this->GetLength();

			while(pszStart < pszEnd)
			{
				PCXSTR pszTarget;

				while((pszTarget = StrTraits::StringFindString(pszStart, pszOld)) != NULL)
				{
					nCount++;
					pszStart = pszTarget + nSourceLen;
				}

				pszStart += StrTraits::SafeStringLen(pszStart) + 1;
			}
		}

		// if any changes were made, make them
		if(nCount > 0)
		{
			// if the buffer is too small, just
			//   allocate a new buffer (slow but sure)
			int nOldLength = this->GetLength();
			int nNewLength = nOldLength + (nReplacementLen - nSourceLen) * nCount;
			PXSTR pszBuffer = this->GetBuffer(__max(nNewLength, nOldLength));
			PXSTR pszStart = pszBuffer;
			PXSTR pszEnd = pszStart + nOldLength;

			// loop again to actually do the work
			while(pszStart < pszEnd)
			{
				PXSTR pszTarget;

				while((pszTarget = StrTraits::StringFindString(pszStart, pszOld)) != NULL)
				{
					int nBalance = nOldLength - int(pszTarget - pszBuffer + nSourceLen);
					Checked::memmove_s(pszTarget + nReplacementLen, nBalance * sizeof(XCHAR),
					                   pszTarget + nSourceLen, nBalance * sizeof(XCHAR));
					Checked::memcpy_s(pszTarget, nReplacementLen * sizeof(XCHAR),
					                  pszNew, nReplacementLen * sizeof(XCHAR));
					pszStart = pszTarget + nReplacementLen;
					pszTarget[nReplacementLen + nBalance] = 0;
					nOldLength += (nReplacementLen - nSourceLen);
				}

				pszStart += StrTraits::SafeStringLen(pszStart) + 1;
			}

			assert(pszBuffer[nNewLength] == 0);
			this->ReleaseBufferSetLength(nNewLength);
		}

		return(nCount);
	}

	// Remove all occurrences of character 'chRemove'
	int Remove(IN XCHAR chRemove)
	{
		int nLength = this->GetLength();
		PXSTR pszBuffer = this->GetBuffer(nLength);
		PXSTR pszSource = pszBuffer;
		PXSTR pszDest = pszBuffer;
		PXSTR pszEnd = pszBuffer + nLength;

		while(pszSource < pszEnd)
		{
			PXSTR pszNewSource = StrTraits::CharNext(pszSource);

			if(*pszSource != chRemove)
			{
				// Copy the source to the destination.  Remember to copy all bytes of an MBCS character
				// Copy the source to the destination.  Remember to copy all bytes of an MBCS character
				size_t NewSourceGap = (pszNewSource - pszSource);
				PXSTR pszNewDest = pszDest + NewSourceGap;
				size_t i = 0;

				for(i = 0; pszDest != pszNewDest && i < NewSourceGap; i++)
				{
					*pszDest = *pszSource;
					pszSource++;
					pszDest++;
				}
			}

			pszSource = pszNewSource;
		}

		*pszDest = 0;
		int nCount = int(pszSource - pszDest);
		this->ReleaseBufferSetLength(nLength - nCount);
		return(nCount);
	}

	MyCStringT Tokenize(
	    _In_z_ PCXSTR pszTokens,
	    IN_OUT int& iStart) const
	{
		assert(iStart >= 0);

		if(iStart < 0)
			AtlThrow(E_INVALIDARG);

		if((pszTokens == NULL) || (*pszTokens == (XCHAR)0))
		{
			if(iStart < this->GetLength())
			{
				return(MyCStringT(this->GetString() + iStart, GetManager()));
			}
		}
		else
		{
			PCXSTR pszPlace = this->GetString() + iStart;
			PCXSTR pszEnd = this->GetString() + this->GetLength();

			if(pszPlace < pszEnd)
			{
				int nIncluding = StrTraits::StringSpanIncluding(pszPlace,
				                 pszTokens);

				if((pszPlace + nIncluding) < pszEnd)
				{
					pszPlace += nIncluding;
					int nExcluding = StrTraits::StringSpanExcluding(pszPlace, pszTokens);
					int iFrom = iStart + nIncluding;
					int nUntil = nExcluding;
					iStart = iFrom + nUntil + 1;
					return(Mid(iFrom, nUntil));
				}
			}
		}

		// return empty string, done tokenizing
		iStart = -1;
		return(MyCStringT(GetManager()));
	}

	// find routines

	// Find the first occurrence of character 'ch', starting at index 'iStart'
	int Find(
	    IN XCHAR ch,
	    IN int iStart = 0) const throw()
	{
		// iStart is in XCHARs
		assert(iStart >= 0);
		// nLength is in XCHARs
		int nLength = this->GetLength();

		if(iStart < 0 || iStart >= nLength)
		{
			return(-1);
		}

		// find first single character
		PCXSTR psz = StrTraits::StringFindChar(this->GetString() + iStart, ch);
		// return -1 if not found and index otherwise
		return((psz == NULL) ? -1 : int(psz - this->GetString()));
	}

	// look for a specific sub-string

	// Find the first occurrence of string 'pszSub', starting at index 'iStart'
	int Find(
	    _In_z_ PCXSTR pszSub,
	    IN int iStart = 0) const throw()
	{
		// iStart is in XCHARs
		assert(iStart >= 0);
		assert(MyAtlIsValidString(pszSub));

		if(pszSub == NULL)
		{
			return(-1);
		}

		// nLength is in XCHARs
		int nLength = this->GetLength();

		if(iStart < 0 || iStart > nLength)
		{
			return(-1);
		}

		// find first matching substring
		PCXSTR psz = StrTraits::StringFindString(this->GetString() + iStart, pszSub);
		// return -1 for not found, distance from beginning otherwise
		return((psz == NULL) ? -1 : int(psz - this->GetString()));
	}

	// Find the first occurrence of any of the characters in string 'pszCharSet'
	int FindOneOf(_In_z_ PCXSTR pszCharSet) const throw()
	{
		assert(MyAtlIsValidString(pszCharSet));
		PCXSTR psz = StrTraits::StringScanSet(this->GetString(), pszCharSet);
		return((psz == NULL) ? -1 : int(psz - this->GetString()));
	}

	// Find the last occurrence of character 'ch'
	int ReverseFind(IN XCHAR ch) const throw()
	{
		// find last single character
		PCXSTR psz = StrTraits::StringFindCharRev(this->GetString(), ch);
		// return -1 if not found, distance from beginning otherwise
		return((psz == NULL) ? -1 : int(psz - this->GetString()));
	}

	// manipulation

	// Convert the string to uppercase
	MyCStringT& MakeUpper()
	{
		int nLength = this->GetLength();
		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::StringUppercase(pszBuffer, nLength + 1);
		this->ReleaseBufferSetLength(nLength);
		return(*this);
	}

	// Convert the string to lowercase
	MyCStringT& MakeLower()
	{
		int nLength = this->GetLength();
		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::StringLowercase(pszBuffer, nLength + 1);
		this->ReleaseBufferSetLength(nLength);
		return(*this);
	}

	// Reverse the string
	MyCStringT& MakeReverse()
	{
		int nLength = this->GetLength();
		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::StringReverse(pszBuffer);
		this->ReleaseBufferSetLength(nLength);
		return(*this);
	}

	// trimming

	// Remove all trailing whitespace
	MyCStringT& TrimRight()
	{
		// find beginning of trailing spaces by starting
		// at beginning (DBCS aware)
		PCXSTR psz = this->GetString();
		PCXSTR pszLast = NULL;

		while(*psz != 0)
		{
			if(StrTraits::IsSpace(*psz))
			{
				if(pszLast == NULL)
					pszLast = psz;
			}
			else
			{
				pszLast = NULL;
			}

			psz = StrTraits::CharNext(psz);
		}

		if(pszLast != NULL)
		{
			// truncate at trailing space start
			int iLast = int(pszLast - this->GetString());
			this->Truncate(iLast);
		}

		return(*this);
	}

	// Remove all leading whitespace
	MyCStringT& TrimLeft()
	{
		// find first non-space character
		PCXSTR psz = this->GetString();

		while(StrTraits::IsSpace(*psz))
		{
			psz = StrTraits::CharNext(psz);
		}

		if(psz != this->GetString())
		{
			// fix up data and length
			int iFirst = int(psz - this->GetString());
			PXSTR pszBuffer = this->GetBuffer(this->GetLength());
			psz = pszBuffer + iFirst;
			int nDataLength = this->GetLength() - iFirst;
			memmove_s(pszBuffer, (this->GetLength() + 1) * sizeof(XCHAR),
			          psz, (nDataLength + 1) * sizeof(XCHAR));
			this->ReleaseBufferSetLength(nDataLength);
		}

		return(*this);
	}

	// Remove all leading and trailing whitespace
	MyCStringT& Trim()
	{
		return(TrimRight().TrimLeft());
	}

	// Remove all leading and trailing occurrences of character 'chTarget'
	MyCStringT& Trim(IN XCHAR chTarget)
	{
		return(TrimRight(chTarget).TrimLeft(chTarget));
	}

	// Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
	MyCStringT& Trim(_In_z_ PCXSTR pszTargets)
	{
		return(TrimRight(pszTargets).TrimLeft(pszTargets));
	}

	// trimming anything (either side)

	// Remove all trailing occurrences of character 'chTarget'
	MyCStringT& TrimRight(IN XCHAR chTarget)
	{
		// find beginning of trailing matches
		// by starting at beginning (DBCS aware)
		PCXSTR psz = this->GetString();
		PCXSTR pszLast = NULL;

		while(*psz != 0)
		{
			if(*psz == chTarget)
			{
				if(pszLast == NULL)
				{
					pszLast = psz;
				}
			}
			else
			{
				pszLast = NULL;
			}

			psz = StrTraits::CharNext(psz);
		}

		if(pszLast != NULL)
		{
			// truncate at left-most matching character
			int iLast = int(pszLast - this->GetString());
			this->Truncate(iLast);
		}

		return(*this);
	}

	// Remove all trailing occurrences of any of the characters in string 'pszTargets'
	MyCStringT& TrimRight(_In_z_ PCXSTR pszTargets)
	{
		// if we're not trimming anything, we're not doing any work
		if((pszTargets == NULL) || (*pszTargets == 0))
		{
			return(*this);
		}

		// find beginning of trailing matches
		// by starting at beginning (DBCS aware)
		PCXSTR psz = this->GetString();
		PCXSTR pszLast = NULL;

		while(*psz != 0)
		{
			if(StrTraits::StringFindChar(pszTargets, *psz) != NULL)
			{
				if(pszLast == NULL)
				{
					pszLast = psz;
				}
			}
			else
			{
				pszLast = NULL;
			}

			psz = StrTraits::CharNext(psz);
		}

		if(pszLast != NULL)
		{
			// truncate at left-most matching character
			int iLast = int(pszLast - this->GetString());
			this->Truncate(iLast);
		}

		return(*this);
	}

	// Remove all leading occurrences of character 'chTarget'
	MyCStringT& TrimLeft(IN XCHAR chTarget)
	{
		// find first non-matching character
		PCXSTR psz = this->GetString();

		while(chTarget == *psz)
		{
			psz = StrTraits::CharNext(psz);
		}

		if(psz != this->GetString())
		{
			// fix up data and length
			int iFirst = int(psz - this->GetString());
			PXSTR pszBuffer = this->GetBuffer(this->GetLength());
			psz = pszBuffer + iFirst;
			int nDataLength = this->GetLength() - iFirst;
			Checked::memmove_s(pszBuffer, (this->GetLength() + 1) * sizeof(XCHAR),
			                   psz, (nDataLength + 1) * sizeof(XCHAR));
			this->ReleaseBufferSetLength(nDataLength);
		}

		return(*this);
	}

	// Remove all leading occurrences of any of the characters in string 'pszTargets'
	MyCStringT& TrimLeft(_In_z_ PCXSTR pszTargets)
	{
		// if we're not trimming anything, we're not doing any work
		if((pszTargets == NULL) || (*pszTargets == 0))
		{
			return(*this);
		}

		PCXSTR psz = this->GetString();

		while((*psz != 0) && (StrTraits::StringFindChar(pszTargets, *psz) != NULL))
		{
			psz = StrTraits::CharNext(psz);
		}

		if(psz != this->GetString())
		{
			// fix up data and length
			int iFirst = int(psz - this->GetString());
			PXSTR pszBuffer = this->GetBuffer(this->GetLength());
			psz = pszBuffer + iFirst;
			int nDataLength = this->GetLength() - iFirst;
			Checked::memmove_s(pszBuffer, (this->GetLength() + 1) * sizeof(XCHAR),
			                   psz, (nDataLength + 1) * sizeof(XCHAR));
			this->ReleaseBufferSetLength(nDataLength);
		}

		return(*this);
	}

#ifdef _ATL_USE_WINAPI_FAMILY_DESKTOP_APP
	// Convert the string to the OEM character set
	void AnsiToOem()
	{
		int nLength = this->GetLength();
		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::ConvertToOem(pszBuffer, nLength + 1);
		this->ReleaseBufferSetLength(nLength);
	}

	// Convert the string to the ANSI character set

	void OemToAnsi()
	{
		int nLength = this->GetLength();
		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::ConvertToAnsi(pszBuffer, nLength + 1);
		this->ReleaseBufferSetLength(nLength);
	}
#endif // _ATL_USE_WINAPI_FAMILY_DESKTOP_APP

	// Very simple sub-string extraction

	// Return the substring starting at index 'iFirst'
	MyCStringT Mid(IN int iFirst) const
	{
		return(Mid(iFirst, this->GetLength() - iFirst));
	}

	// Return the substring starting at index 'iFirst', with length 'nCount'
	MyCStringT Mid(
	    IN int iFirst,
	    IN int nCount) const
	{
		// nCount is in XCHARs

		// out-of-bounds requests return sensible things
		if(iFirst < 0)
			iFirst = 0;

		if(nCount < 0)
			nCount = 0;

		if((::ATL::AtlAddThrow(iFirst, nCount)) > this->GetLength())
		{
			nCount = this->GetLength() - iFirst;
		}

		if(iFirst > this->GetLength())
		{
			nCount = 0;
		}

		assert((nCount == 0) || ((iFirst + nCount) <= this->GetLength()));

		// optimize case of returning entire string
		if((iFirst == 0) && ((iFirst + nCount) == this->GetLength()))
		{
			return(*this);
		}

		return(MyCStringT(this->GetString() + iFirst, nCount, GetManager()));
	}

	// Return the substring consisting of the rightmost 'nCount' characters
	MyCStringT Right(IN int nCount) const
	{
		// nCount is in XCHARs
		if(nCount < 0)
			nCount = 0;

		int nLength = this->GetLength();

		if(nCount >= nLength)
		{
			return(*this);
		}

		return(MyCStringT(this->GetString() + nLength - nCount, nCount, GetManager()));
	}

	// Return the substring consisting of the leftmost 'nCount' characters
	MyCStringT Left(IN int nCount) const
	{
		// nCount is in XCHARs
		if(nCount < 0)
			nCount = 0;

		int nLength = this->GetLength();

		if(nCount >= nLength)
		{
			return(*this);
		}

		return(MyCStringT(this->GetString(), nCount, GetManager()));
	}

	// Return the substring consisting of the leftmost characters in the set 'pszCharSet'
	MyCStringT SpanIncluding(_In_z_ PCXSTR pszCharSet) const
	{
		assert(MyAtlIsValidString(pszCharSet));

		if(pszCharSet == NULL)
			AtlThrow(E_INVALIDARG);

		return(Left(StrTraits::StringSpanIncluding(this->GetString(), pszCharSet)));
	}

	// Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
	MyCStringT SpanExcluding(_In_z_ PCXSTR pszCharSet) const
	{
		assert(MyAtlIsValidString(pszCharSet));

		if(pszCharSet == NULL)
			AtlThrow(E_INVALIDARG);

		return(Left(StrTraits::StringSpanExcluding(this->GetString(), pszCharSet)));
	}

	// Format data using format string 'pszFormat'
	void __cdecl Format(_In_z_ _Printf_format_string_ PCXSTR pszFormat, ...);

	// Format data using format string loaded from resource 'nFormatID'
	void __cdecl Format(IN _FormatMessage_format_string_ UINT nFormatID, ...);

	// Append formatted data using format string loaded from resource 'nFormatID'
	void __cdecl AppendFormat(IN _FormatMessage_format_string_ UINT nFormatID, ...);

	// Append formatted data using format string 'pszFormat'
	void __cdecl AppendFormat(_In_z_ _Printf_format_string_ PCXSTR pszFormat, ...);

	void AppendFormatV(
	    _In_z_ _Printf_format_string_ PCXSTR pszFormat,
	    IN va_list args)
	{
		assert(MyAtlIsValidString(pszFormat));

		if(pszFormat == NULL)
			AtlThrow(E_INVALIDARG);

		int nCurrentLength = this->GetLength();
		int nAppendLength = StrTraits::GetFormattedLength(pszFormat, args);

		if(nAppendLength < 0)
			AtlThrow(E_FAIL);

		PXSTR pszBuffer = this->GetBuffer(nCurrentLength + nAppendLength);
		StrTraits::Format(pszBuffer + nCurrentLength,
		                  nAppendLength + 1, pszFormat, args);
		this->ReleaseBufferSetLength(nCurrentLength + nAppendLength);
	}

	void FormatV(
	    _In_z_ _Printf_format_string_ PCXSTR pszFormat,
	    IN va_list args)
	{
		assert(MyAtlIsValidString(pszFormat));

		if(pszFormat == NULL)
			AtlThrow(E_INVALIDARG);

		int nLength = StrTraits::GetFormattedLength(pszFormat, args);

		if(nLength < 0)
			AtlThrow(E_FAIL);

		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::Format(pszBuffer, nLength + 1, pszFormat, args);
		this->ReleaseBufferSetLength(nLength);
	}

	// OLE BSTR support
	// Allocate a BSTR containing a copy of the string
	_Ret_z_ BSTR AllocSysString() const
	{
		BSTR bstrResult = StrTraits::AllocSysString(this->GetString(), this->GetLength());

		if(bstrResult == NULL)
		{
			CThisSimpleString::ThrowMemoryException();
		}

		return(bstrResult);
	}

	_Ret_maybenull_z_ BSTR SetSysString(IN_OUT _Deref_post_opt_valid_ _Post_z_ BSTR* pbstr) const
	{
		assert(AtlIsValidAddress(pbstr, sizeof(BSTR)));

		if(!StrTraits::ReAllocSysString(this->GetString(), pbstr,
		                                this->GetLength()))
		{
			CThisSimpleString::ThrowMemoryException();
		}

		assert(*pbstr != NULL);
		return(*pbstr);
	}

#ifdef _ATL_USE_WINAPI_FAMILY_DESKTOP_APP
	// Set the string to the value of environment variable 'pszVar'

	// Load the string from resource 'nID'
	_Check_return_ BOOL LoadString(IN UINT nID)
	{
		HINSTANCE hInst = StrTraits::FindStringResourceInstance(nID);

		if(hInst == NULL)
		{
			return(FALSE);
		}

		return(LoadString(hInst, nID));
	}


#pragma warning(push)
#pragma warning(disable:4200)
	struct MYSTRINGRESOURCEIMAGE
	{
		WORD nLength;
		WCHAR achString[];
	};
#pragma warning(pop)


	static inline const MYSTRINGRESOURCEIMAGE* _AtlGetStringResourceImage(
	    IN HINSTANCE hInstance,
	    IN HRSRC hResource,
	    IN UINT id) throw()
	{
		const MYSTRINGRESOURCEIMAGE* pImage;
		const MYSTRINGRESOURCEIMAGE* pImageEnd;
		ULONG nResourceSize;
		HGLOBAL hGlobal;
		UINT iIndex;
		hGlobal = ::LoadResource(hInstance, hResource);

		if(hGlobal == NULL)
		{
			return(NULL);
		}

		pImage = (const MYSTRINGRESOURCEIMAGE*)::LockResource(hGlobal);

		if(pImage == NULL)
		{
			return(NULL);
		}

		nResourceSize = ::SizeofResource(hInstance, hResource);
		pImageEnd = (const MYSTRINGRESOURCEIMAGE*)(LPBYTE(pImage) + nResourceSize);
		iIndex = id & 0x000f;

		while((iIndex > 0) && (pImage < pImageEnd))
		{
			pImage = (const MYSTRINGRESOURCEIMAGE*)(LPBYTE(pImage) + (sizeof(MYSTRINGRESOURCEIMAGE) + (pImage->nLength * sizeof(WCHAR))));
			iIndex--;
		}

		if(pImage >= pImageEnd)
		{
			return(NULL);
		}

		if(pImage->nLength == 0)
		{
			return(NULL);
		}

		return(pImage);
	}

	static inline const MYSTRINGRESOURCEIMAGE* AtlGetStringResourceImage(
	    IN HINSTANCE hInstance,
	    IN UINT id) throw()
	{
		HRSRC hResource;
		/*
		The and operation (& static_cast<WORD>(~0)) protects the expression from being greater
		than WORD - this would cause a runtime error when the application is compiled with /RTCc flag.
		*/
		hResource = ::FindResourceW(hInstance, MAKEINTRESOURCEW((((id >> 4) + 1) & static_cast<WORD>(~0))), (LPWSTR)RT_STRING);

		if(hResource == NULL)
		{
			return(NULL);
		}

		return _AtlGetStringResourceImage(hInstance, hResource, id);
	}


	// Load the string from resource 'nID' in module 'hInstance'
	_Check_return_ BOOL LoadString(
	    IN HINSTANCE hInstance,
	    IN UINT nID)
	{
		const MYSTRINGRESOURCEIMAGE* pImage = AtlGetStringResourceImage(hInstance, nID);

		if(pImage == NULL)
		{
			return(FALSE);
		}

		int nLength = StrTraits::GetBaseTypeLength(pImage->achString, pImage->nLength);
		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::ConvertToBaseType(pszBuffer, nLength, (LPCWSTR)pImage->achString, pImage->nLength);
		this->ReleaseBufferSetLength(nLength);
		return(TRUE);
	}

	// Load the string from resource 'nID' in module 'hInstance', using language 'wLanguageID'
	_Check_return_ BOOL LoadString(
	    IN HINSTANCE hInstance,
	    IN UINT nID,
	    IN WORD wLanguageID)
	{
		const MYSTRINGRESOURCEIMAGE* pImage = AtlGetStringResourceImage(hInstance, nID, wLanguageID);

		if(pImage == NULL)
		{
			return(FALSE);
		}

		int nLength = StrTraits::GetBaseTypeLength(pImage->achString, pImage->nLength);
		PXSTR pszBuffer = this->GetBuffer(nLength);
		StrTraits::ConvertToBaseType(pszBuffer, nLength, pImage->achString, pImage->nLength);
		this->ReleaseBufferSetLength(nLength);
		return(TRUE);
	}
#endif  //  _ATL_USE_WINAPI_FAMILY_DESKTOP_APP

	friend MyCStringT operator+(
	    IN const MyCStringT& str1,
	    IN const MyCStringT& str2)
	{
		MyCStringT strResult(str1.GetManager());
		CThisSimpleString::Concatenate(strResult, str1, str1.GetLength(), str2, str2.GetLength());
		return(strResult);
	}

	friend MyCStringT operator+(
	    IN const MyCStringT& str1,
	    _In_z_ PCXSTR psz2)
	{
		MyCStringT strResult(str1.GetManager());
		CThisSimpleString::Concatenate(strResult, str1, str1.GetLength(), psz2, CThisSimpleString::StringLength(psz2));
		return(strResult);
	}

	friend MyCStringT operator+(
	    _In_z_ PCXSTR psz1,
	    IN const MyCStringT& str2)
	{
		MyCStringT strResult(str2.GetManager());
		CThisSimpleString::Concatenate(strResult, psz1, CThisSimpleString::StringLength(psz1), str2, str2.GetLength());
		return(strResult);
	}

#ifdef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
#define _CSTRING_CHAR_T XCHAR
#else // def _CSTRING_NARROW_WIDE_CONVERSION
#define _CSTRING_CHAR_T char
#endif // def _CSTRING_NARROW_WIDE_CONVERSION

	friend MyCStringT operator+(
	    IN const MyCStringT& str1,
	    IN _CSTRING_CHAR_T ch2)
	{
		MyCStringT strResult(str1.GetManager());
		XCHAR chTemp = XCHAR(ch2);
		CThisSimpleString::Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1);
		return(strResult);
	}

	friend MyCStringT operator+(
	    IN _CSTRING_CHAR_T ch1,
	    IN const MyCStringT& str2)
	{
		MyCStringT strResult(str2.GetManager());
		XCHAR chTemp = XCHAR(ch1);
		CThisSimpleString::Concatenate(strResult, &chTemp, 1, str2, str2.GetLength());
		return(strResult);
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	friend MyCStringT operator+(
	    IN const MyCStringT& str1,
	    IN wchar_t ch2)
	{
		MyCStringT strResult(str1.GetManager());
		XCHAR chTemp = XCHAR(ch2);
		CThisSimpleString::Concatenate(strResult, str1, str1.GetLength(), &chTemp, 1);
		return(strResult);
	}

	friend MyCStringT operator+(
	    IN wchar_t ch1,
	    IN const MyCStringT& str2)
	{
		MyCStringT strResult(str2.GetManager());
		XCHAR chTemp = XCHAR(ch1);
		CThisSimpleString::Concatenate(strResult, &chTemp, 1, str2, str2.GetLength());
		return(strResult);
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	friend bool operator==(
	    IN const MyCStringT& str1,
	    IN const MyCStringT& str2) throw()
	{
		return(str1.Compare(str2) == 0);
	}

	friend bool operator==(
	    IN const MyCStringT& str1,
	    _In_z_ PCXSTR psz2) throw()
	{
		return(str1.Compare(psz2) == 0);
	}

	friend bool operator==(
	    _In_z_ PCXSTR psz1,
	    IN const MyCStringT& str2) throw()
	{
		return(str2.Compare(psz1) == 0);
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	friend bool operator==(
	    IN const MyCStringT& str1,
	    _In_z_ PCYSTR psz2) throw(...)
	{
		MyCStringT str2(psz2, str1.GetManager());
		return(str1 == str2);
	}

	friend bool operator==(
	    _In_z_ PCYSTR psz1,
	    IN const MyCStringT& str2) throw(...)
	{
		MyCStringT str1(psz1, str2.GetManager());
		return(str1 == str2);
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	friend bool operator!=(
	    IN const MyCStringT& str1,
	    IN const MyCStringT& str2) throw()
	{
		return(str1.Compare(str2) != 0);
	}

	friend bool operator!=(
	    IN const MyCStringT& str1,
	    _In_z_ PCXSTR psz2) throw()
	{
		return(str1.Compare(psz2) != 0);
	}

	friend bool operator!=(
	    _In_z_ PCXSTR psz1,
	    IN const MyCStringT& str2) throw()
	{
		return(str2.Compare(psz1) != 0);
	}

#ifndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
	friend bool operator!=(
	    IN const MyCStringT& str1,
	    _In_z_ PCYSTR psz2) throw(...)
	{
		MyCStringT str2(psz2, str1.GetManager());
		return(str1 != str2);
	}

	friend bool operator!=(
	    _In_z_ PCYSTR psz1,
	    IN const MyCStringT& str2) throw(...)
	{
		MyCStringT str1(psz1, str2.GetManager());
		return(str1 != str2);
	}
#endif // ndef _CSTRING_DISABLE_NARROW_WIDE_CONVERSION

	friend bool operator<(
	    IN const MyCStringT& str1,
	    IN const MyCStringT& str2) throw()
	{
		return(str1.Compare(str2) < 0);
	}

	friend bool operator<(
	    IN const MyCStringT& str1,
	    _In_z_ PCXSTR psz2) throw()
	{
		return(str1.Compare(psz2) < 0);
	}

	friend bool operator<(
	    _In_z_ PCXSTR psz1,
	    IN const MyCStringT& str2) throw()
	{
		return(str2.Compare(psz1) > 0);
	}

	friend bool operator>(
	    IN const MyCStringT& str1,
	    IN const MyCStringT& str2) throw()
	{
		return(str1.Compare(str2) > 0);
	}

	friend bool operator>(
	    IN const MyCStringT& str1,
	    _In_z_ PCXSTR psz2) throw()
	{
		return(str1.Compare(psz2) > 0);
	}

	friend bool operator>(
	    _In_z_ PCXSTR psz1,
	    IN const MyCStringT& str2) throw()
	{
		return(str2.Compare(psz1) < 0);
	}

	friend bool operator<=(
	    IN const MyCStringT& str1,
	    IN const MyCStringT& str2) throw()
	{
		return(str1.Compare(str2) <= 0);
	}

	friend bool operator<=(
	    IN const MyCStringT& str1,
	    _In_z_ PCXSTR psz2) throw()
	{
		return(str1.Compare(psz2) <= 0);
	}

	friend bool operator<=(
	    _In_z_ PCXSTR psz1,
	    IN const MyCStringT& str2) throw()
	{
		return(str2.Compare(psz1) >= 0);
	}

	friend bool operator>=(
	    IN const MyCStringT& str1,
	    IN const MyCStringT& str2) throw()
	{
		return(str1.Compare(str2) >= 0);
	}

	friend bool operator>=(
	    IN const MyCStringT& str1,
	    _In_z_ PCXSTR psz2) throw()
	{
		return(str1.Compare(psz2) >= 0);
	}

	friend bool operator>=(
	    _In_z_ PCXSTR psz1,
	    IN const MyCStringT& str2) throw()
	{
		return(str2.Compare(psz1) <= 0);
	}

	friend bool operator==(
	    IN XCHAR ch1,
	    IN const MyCStringT& str2) throw()
	{
		return((str2.GetLength() == 1) && (str2[0] == ch1));
	}

	friend bool operator==(
	    IN const MyCStringT& str1,
	    IN XCHAR ch2) throw()
	{
		return((str1.GetLength() == 1) && (str1[0] == ch2));
	}

	friend bool operator!=(
	    IN XCHAR ch1,
	    IN const MyCStringT& str2) throw()
	{
		return((str2.GetLength() != 1) || (str2[0] != ch1));
	}

	friend bool operator!=(
	    IN const MyCStringT& str1,
	    IN XCHAR ch2) throw()
	{
		return((str1.GetLength() != 1) || (str1[0] != ch2));
	}

private:
	bool CheckImplicitLoad(IN_OPT const void* pv)
	{
		bool bRet = false;

		if((pv != NULL) && IS_INTRESOURCE(pv))
		{
			UINT nID = LOWORD(reinterpret_cast<DWORD_PTR>(pv));
			(nID);
#ifdef _ATL_USE_WINAPI_FAMILY_DESKTOP_APP

			if(!LoadString(nID))
			{
				//ATLTRACE(atlTraceString, 2, _T("Warning: implicit LoadString(%u) failed\n"), nID);
			}

			bRet = true;
#else
			//ATLTRACE(atlTraceString, 2, _T("Warning: LoadString(%u) not supported under the current WINAPI_FAMILY.\n"), nID);
#endif
		}

		return(bRet);
	}

};


template< typename BaseType >
MyCStringT<BaseType>::MyCStringT(const VARIANT& varSrc) :
	CThisSimpleString(StrTraits::GetDefaultManager())
{
	CComVariant varResult;
	HRESULT hr = ::VariantChangeType(&varResult, const_cast<VARIANT*>(&varSrc), 0, VT_BSTR);

	if(FAILED(hr))
	{
		AtlThrow(hr);
	}

	*this = V_BSTR(&varResult);
}


#ifndef _CSTRING_BUFFER_SIZE
#define _CSTRING_BUFFER_SIZE(_CStringObj) ((_CStringObj).GetAllocLength() + 1)
#endif


#pragma warning(push)
#pragma warning(disable : 4793)

// Format data using format string 'pszFormat'
template< typename BaseType  >
inline void __cdecl MyCStringT<BaseType >::Format(PCXSTR pszFormat, ...)
{
	assert(MyAtlIsValidString(pszFormat));
	va_list argList;
	va_start(argList, pszFormat);
	FormatV(pszFormat, argList);
	va_end(argList);
}

// Format data using format string loaded from resource 'nFormatID'
template< typename BaseType >
inline void __cdecl MyCStringT<BaseType>::Format(UINT nFormatID, ...)
{
	MyCStringT strFormat(GetManager());
	ATLENSURE(strFormat.LoadString(nFormatID));
	va_list argList;
	va_start(argList, nFormatID);
	FormatV(strFormat, argList);
	va_end(argList);
}

// Append formatted data using format string loaded from resource 'nFormatID'
template< typename BaseType >
inline void __cdecl MyCStringT<BaseType>::AppendFormat(UINT nFormatID, ...)
{
	va_list argList;
	va_start(argList, nFormatID);
	MyCStringT strFormat(GetManager());
	ATLENSURE(strFormat.LoadString(nFormatID));
	AppendFormatV(strFormat, argList);
	va_end(argList);
}


// Append formatted data using format string 'pszFormat'
template< typename BaseType >
inline void __cdecl MyCStringT<BaseType>::AppendFormat(PCXSTR pszFormat, ...)
{
	assert(MyAtlIsValidString(pszFormat));
	va_list argList;
	va_start(argList, pszFormat);
	AppendFormatV(pszFormat, argList);
	va_end(argList);
}


/**************************************************************************
*
* 文件名也得叫做atlstr.h这样通过设置附加包含路径,就可以替换原有的atlstr.h
*  替换后CString CStringA CStringW等定义都发生变化
**************************************************************************/
#define MY_CSTRING
#define _CSTRING_NS
#define MY_STR_DEFINE 0
#if MY_STR_DEFINE
#define CStringW MyCStringT<WCHAR>
#define CStringA MyCStringT<CHAR>
#ifdef _UNICODE
#define CString CStringW;
#else
#define CString CStringA;
#endif
#define CAtlString CString
#else
typedef MyCStringT<WCHAR> CStringW;
typedef MyCStringT<CHAR> CStringA;
#ifdef _UNICODE
typedef CStringW CString;
#else
typedef CStringA CString;
#endif
typedef CString CAtlString;
#endif

猜你喜欢

转载自blog.csdn.net/lif12345/article/details/88050528
今日推荐