You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1496 lines
40 KiB
1496 lines
40 KiB
/*******************************************************************************
|
|
*
|
|
* (C) COPYRIGHT MICROSOFT CORPORATION, 1998
|
|
*
|
|
* TITLE: SIMSTR.H
|
|
*
|
|
* VERSION: 1.0
|
|
*
|
|
* AUTHOR: ShaunIv
|
|
*
|
|
* DATE: 5/12/1998
|
|
*
|
|
* DESCRIPTION: Simple string classes
|
|
*
|
|
*******************************************************************************/
|
|
#ifndef _SIMSTR_H_INCLUDED
|
|
#define _SIMSTR_H_INCLUDED
|
|
|
|
/*
|
|
* Simple string class.
|
|
*
|
|
* Template class:
|
|
* CSimpleStringBase<CharType>
|
|
* Implementations:
|
|
* CSimpleStringBase<wchar_t> CSimpleStringWide
|
|
* CSimpleStringBase<char> CSimpleStringAnsi
|
|
* CSimpleString = CSimpleString[Ansi|Wide] depending on UNICODE macro
|
|
* Inline functions:
|
|
* CSimpleStringAnsi CSimpleStringConvert::AnsiString(CharType n)
|
|
* CSimpleStringWide CSimpleStringConvert::WideString(CharType n)
|
|
* CSimpleString CSimpleStringConvert::NaturalString(CharType n)
|
|
* Macros:
|
|
* IS_CHAR(CharType)
|
|
* IS_WCHAR(CharType)
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <tchar.h>
|
|
|
|
//
|
|
// Disable the "conditional expression is constant" warning that is caused by
|
|
// the IS_CHAR and IS_WCHAR macros
|
|
//
|
|
#pragma warning( push )
|
|
#pragma warning( disable : 4127 )
|
|
|
|
#define IS_CHAR(x) (sizeof(x) & sizeof(char))
|
|
#define IS_WCHAR(x) (sizeof(x) & sizeof(wchar_t))
|
|
|
|
#ifndef ARRAYSIZE
|
|
#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
|
|
#endif
|
|
|
|
template <class CharType>
|
|
class CSimpleStringBase
|
|
{
|
|
private:
|
|
enum
|
|
{
|
|
c_nDefaultGranularity = 16, // Default number of extra characters to allocate when we have to grow
|
|
c_nMaxLoadStringBuffer = 1024, // Maximum length of .RC string
|
|
c_nMaxAutoDataLength = 128 // Length of non-dynamically allocated string
|
|
};
|
|
|
|
private:
|
|
//
|
|
// If the string is less than c_nMaxAutoDataLength characters, it will be
|
|
// stored here, instead of in a dynamically allocated buffer
|
|
//
|
|
CharType m_pstrAutoData[c_nMaxAutoDataLength];
|
|
|
|
//
|
|
// If we have to allocated data, it will be stored here
|
|
//
|
|
CharType *m_pstrData;
|
|
|
|
//
|
|
// Current maximum buffer size
|
|
//
|
|
UINT m_nMaxSize;
|
|
|
|
//
|
|
// Amount of extra space we allocate when we have to grow the buffer
|
|
//
|
|
UINT m_nGranularity;
|
|
|
|
private:
|
|
|
|
//
|
|
// Min, in case it isn't already defined
|
|
//
|
|
template <class NumberType>
|
|
static NumberType Min( const NumberType &a, const NumberType &b )
|
|
{
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
public:
|
|
|
|
//
|
|
// Replacements (in some cases just wrappers) for strlen, strcpy, ...
|
|
//
|
|
static inline CharType *GenericCopy( CharType *pstrTarget, const CharType *pstrSource );
|
|
static inline CharType *GenericCopyLength( CharType *pstrTarget, const CharType *pstrSource, UINT nSize );
|
|
static inline UINT GenericLength( const CharType *pstrStr );
|
|
static inline CharType *GenericConcatenate( CharType *pstrTarget, const CharType *pstrSource );
|
|
static inline int GenericCompare( const CharType *pstrTarget, const CharType *pstrSource );
|
|
static inline int GenericCompareNoCase( const CharType *pstrStrA, const CharType *pstrStrB );
|
|
static inline int GenericCompareLength( const CharType *pstrTarget, const CharType *pstrSource, UINT nLength );
|
|
static inline CharType *GenericCharNext( const CharType *pszStr );
|
|
|
|
private:
|
|
//
|
|
// Internal only helpers
|
|
//
|
|
bool EnsureLength( UINT nMaxSize );
|
|
void DeleteStorage();
|
|
static inline CharType *CreateStorage( UINT nCount );
|
|
void Destroy();
|
|
|
|
public:
|
|
//
|
|
// Constructors and destructor
|
|
//
|
|
CSimpleStringBase();
|
|
CSimpleStringBase( const CSimpleStringBase & );
|
|
CSimpleStringBase( const CharType *szStr );
|
|
CSimpleStringBase( CharType ch );
|
|
CSimpleStringBase( UINT nResId, HMODULE hModule );
|
|
virtual ~CSimpleStringBase();
|
|
|
|
//
|
|
// Various helpers
|
|
//
|
|
UINT Length() const;
|
|
void Concat( const CSimpleStringBase &other );
|
|
int Resize();
|
|
UINT Truncate( UINT nLen );
|
|
bool Assign( const CharType *szStr );
|
|
bool Assign( const CSimpleStringBase & );
|
|
void SetAt( UINT nIndex, CharType chValue );
|
|
CharType &operator[](int index);
|
|
const CharType &operator[](int index) const;
|
|
|
|
//
|
|
// Handy Win32 wrappers
|
|
//
|
|
CSimpleStringBase &Format( const CharType *strFmt, ... );
|
|
CSimpleStringBase &Format( int nResId, HINSTANCE hInst, ... );
|
|
CSimpleStringBase &GetWindowText( HWND hWnd );
|
|
bool SetWindowText( HWND hWnd );
|
|
bool LoadString( UINT nResId, HMODULE hModule );
|
|
bool Load( HKEY hRegKey, const CharType *pszValueName, const CharType *pszDefault=NULL );
|
|
bool Store( HKEY hRegKey, const CharType *pszValueName, DWORD nType = REG_SZ );
|
|
|
|
//
|
|
// Operators
|
|
//
|
|
CSimpleStringBase &operator=( const CSimpleStringBase &other );
|
|
CSimpleStringBase &operator=( const CharType *other );
|
|
CSimpleStringBase &operator+=( const CSimpleStringBase &other );
|
|
|
|
|
|
//
|
|
// Convert this string and return the converted string
|
|
//
|
|
CSimpleStringBase ToUpper() const;
|
|
CSimpleStringBase ToLower() const;
|
|
|
|
//
|
|
// Convert in place
|
|
//
|
|
CSimpleStringBase &MakeUpper();
|
|
CSimpleStringBase &MakeLower();
|
|
|
|
//
|
|
// Remove leading and trailing spaces
|
|
//
|
|
CSimpleStringBase &TrimRight();
|
|
CSimpleStringBase &TrimLeft();
|
|
CSimpleStringBase &Trim();
|
|
|
|
//
|
|
// Reverse
|
|
//
|
|
CSimpleStringBase &Reverse();
|
|
|
|
//
|
|
// Searching
|
|
//
|
|
int Find( CharType cChar ) const;
|
|
int Find( const CSimpleStringBase &other, UINT nStart=0 ) const;
|
|
int ReverseFind( CharType cChar ) const;
|
|
int ReverseFind( const CSimpleStringBase &other ) const;
|
|
|
|
//
|
|
// Substring copies
|
|
//
|
|
CSimpleStringBase SubStr( int nStart, int nCount=-1 ) const;
|
|
|
|
CSimpleStringBase Left( int nCount ) const
|
|
{
|
|
return SubStr( 0, nCount );
|
|
}
|
|
CSimpleStringBase Right( int nCount ) const
|
|
{
|
|
return SubStr( max(0,(int)Length()-nCount), -1 );
|
|
}
|
|
|
|
//
|
|
// Comparison functions
|
|
//
|
|
int CompareNoCase( const CSimpleStringBase &other, int nLength=-1 ) const;
|
|
int Compare( const CSimpleStringBase &other, int nLength=-1 ) const;
|
|
bool MatchLastCharacter( CharType cChar ) const;
|
|
|
|
//
|
|
// Direct manipulation
|
|
//
|
|
CharType *GetBuffer( int nLength )
|
|
{
|
|
//
|
|
// If the user passed 0, or we are able to allocate a string of the
|
|
// requested length, return a pointer to the actual data.
|
|
//
|
|
if (!nLength || EnsureLength(nLength+1))
|
|
{
|
|
return m_pstrData;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Useful inlines
|
|
//
|
|
const CharType *String() const
|
|
{
|
|
return m_pstrData;
|
|
}
|
|
UINT MaxSize() const
|
|
{
|
|
return m_nMaxSize;
|
|
}
|
|
UINT Granularity( UINT nGranularity )
|
|
{
|
|
if (nGranularity>0)
|
|
{
|
|
m_nGranularity = nGranularity;
|
|
}
|
|
return m_nGranularity;
|
|
}
|
|
UINT Granularity() const
|
|
{
|
|
return m_nGranularity;
|
|
}
|
|
|
|
//
|
|
// Implicit cast operator
|
|
//
|
|
operator const CharType *() const
|
|
{
|
|
return String();
|
|
}
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
bool IsValid() const
|
|
{
|
|
return(NULL != m_pstrData);
|
|
}
|
|
};
|
|
|
|
template <class CharType>
|
|
inline CharType *CSimpleStringBase<CharType>::GenericCopy( CharType *pszDest, const CharType *pszSource )
|
|
{
|
|
CopyMemory( pszDest, pszSource, sizeof(CharType) * (GenericLength(pszSource) + 1) );
|
|
return pszDest;
|
|
}
|
|
|
|
template <class CharType>
|
|
inline CharType *CSimpleStringBase<CharType>::GenericCharNext( const CharType *pszStr )
|
|
{
|
|
if (IS_CHAR(*pszStr))
|
|
return(CharType*)CharNextA((LPCSTR)pszStr);
|
|
else if (!*pszStr)
|
|
return(CharType*)pszStr;
|
|
else return(CharType*)((LPWSTR)pszStr + 1);
|
|
}
|
|
|
|
template <class CharType>
|
|
inline CharType *CSimpleStringBase<CharType>::GenericCopyLength( CharType *pszTarget, const CharType *pszSource, UINT nCount )
|
|
{
|
|
UINT nCopyLen = min( nCount, GenericLength(pszSource) + 1 );
|
|
|
|
CopyMemory( pszTarget, pszSource, nCopyLen * sizeof(CharType) );
|
|
|
|
if (nCopyLen < nCount)
|
|
{
|
|
pszTarget[nCopyLen] = 0;
|
|
}
|
|
return pszTarget;
|
|
}
|
|
|
|
template <class CharType>
|
|
inline UINT CSimpleStringBase<CharType>::GenericLength( const CharType *pszString )
|
|
{
|
|
const CharType *eos = pszString;
|
|
|
|
while (*eos++)
|
|
;
|
|
return((UINT)(eos - pszString - 1));
|
|
}
|
|
|
|
template <class CharType>
|
|
inline CharType*CSimpleStringBase<CharType>::GenericConcatenate( CharType *pszDest, const CharType *pszSource )
|
|
{
|
|
CharType *pCurr = pszDest;
|
|
|
|
while (*pCurr)
|
|
pCurr++;
|
|
|
|
CopyMemory( pCurr, pszSource, sizeof(CharType) * (GenericLength(pszSource) + 1) );
|
|
|
|
return pszDest;
|
|
}
|
|
|
|
|
|
template <class CharType>
|
|
inline int CSimpleStringBase<CharType>::GenericCompare( const CharType *pszSource, const CharType *pszDest )
|
|
{
|
|
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
|
|
if (sizeof(CharType) == sizeof(wchar_t))
|
|
{
|
|
OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
|
|
}
|
|
#endif
|
|
int nRes = IS_CHAR(*pszSource) ?
|
|
CompareStringA( LOCALE_USER_DEFAULT, 0, (LPCSTR)pszSource, -1, (LPCSTR)pszDest, -1 ) :
|
|
CompareStringW( LOCALE_USER_DEFAULT, 0, (LPCWSTR)pszSource, -1, (LPCWSTR)pszDest, -1 );
|
|
switch (nRes)
|
|
{
|
|
case CSTR_LESS_THAN:
|
|
return -1;
|
|
case CSTR_GREATER_THAN:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
template <class CharType>
|
|
inline int CSimpleStringBase<CharType>::GenericCompareNoCase( const CharType *pszSource, const CharType *pszDest )
|
|
{
|
|
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
|
|
if (sizeof(CharType) == sizeof(wchar_t))
|
|
{
|
|
OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
|
|
}
|
|
#endif
|
|
int nRes = IS_CHAR(*pszSource) ?
|
|
CompareStringA( LOCALE_USER_DEFAULT, NORM_IGNORECASE, (LPCSTR)pszSource, -1, (LPCSTR)pszDest, -1 ) :
|
|
CompareStringW( LOCALE_USER_DEFAULT, NORM_IGNORECASE, (LPCWSTR)pszSource, -1, (LPCWSTR)pszDest, -1 );
|
|
switch (nRes)
|
|
{
|
|
case CSTR_LESS_THAN:
|
|
return -1;
|
|
case CSTR_GREATER_THAN:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template <class CharType>
|
|
inline int CSimpleStringBase<CharType>::GenericCompareLength( const CharType *pszStringA, const CharType *pszStringB, UINT nLength )
|
|
{
|
|
#if defined(DBG) && !defined(UNICODE) && !defined(_UNICODE)
|
|
if (sizeof(CharType) == sizeof(wchar_t))
|
|
{
|
|
OutputDebugString(TEXT("CompareStringW is not supported under win9x, so this call is going to fail!"));
|
|
}
|
|
#endif
|
|
if (!nLength)
|
|
return(0);
|
|
int nRes = IS_CHAR(*pszStringA) ?
|
|
CompareStringA( LOCALE_USER_DEFAULT, 0, (LPCSTR)pszStringA, Min(nLength,CSimpleStringBase<CHAR>::GenericLength((LPCSTR)pszStringA)), (LPCSTR)pszStringB, Min(nLength,CSimpleStringBase<CHAR>::GenericLength((LPCSTR)pszStringB)) ) :
|
|
CompareStringW( LOCALE_USER_DEFAULT, 0, (LPWSTR)pszStringA, Min(nLength,CSimpleStringBase<WCHAR>::GenericLength((LPCWSTR)pszStringA)), (LPCWSTR)pszStringB, Min(nLength,CSimpleStringBase<WCHAR>::GenericLength((LPCWSTR)pszStringB)) );
|
|
switch (nRes)
|
|
{
|
|
case CSTR_LESS_THAN:
|
|
return -1;
|
|
case CSTR_GREATER_THAN:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template <class CharType>
|
|
bool CSimpleStringBase<CharType>::EnsureLength( UINT nMaxSize )
|
|
{
|
|
//
|
|
// If the string is already long enough, just return true
|
|
//
|
|
if (m_nMaxSize >= nMaxSize)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Get the new size
|
|
//
|
|
UINT nNewMaxSize = nMaxSize + m_nGranularity;
|
|
|
|
//
|
|
// Allocate the new buffer
|
|
//
|
|
CharType *pszTmp = CreateStorage(nNewMaxSize);
|
|
|
|
//
|
|
// Make sure the allocation succeded
|
|
//
|
|
if (pszTmp)
|
|
{
|
|
//
|
|
// If we have an existing string, copy it and delete it
|
|
//
|
|
if (m_pstrData)
|
|
{
|
|
GenericCopy(pszTmp,m_pstrData);
|
|
DeleteStorage();
|
|
}
|
|
|
|
//
|
|
// Save the new max size
|
|
//
|
|
m_nMaxSize = nNewMaxSize;
|
|
|
|
//
|
|
// Save this new string
|
|
//
|
|
m_pstrData = pszTmp;
|
|
|
|
//
|
|
// Return success
|
|
//
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Couldn't allocate memory
|
|
//
|
|
return false;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::GetWindowText( HWND hWnd )
|
|
{
|
|
Destroy();
|
|
// Assume it didn't work
|
|
bool bSuccess = false;
|
|
int nLen = ::GetWindowTextLength(hWnd);
|
|
if (nLen)
|
|
{
|
|
if (EnsureLength(nLen+1))
|
|
{
|
|
if (::GetWindowText( hWnd, m_pstrData, (nLen+1) ))
|
|
{
|
|
bSuccess = true;
|
|
}
|
|
}
|
|
}
|
|
if (!bSuccess)
|
|
Destroy();
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
bool CSimpleStringBase<CharType>::SetWindowText( HWND hWnd )
|
|
{
|
|
return(::SetWindowText( hWnd, String() ) != FALSE);
|
|
}
|
|
|
|
template <class CharType>
|
|
UINT CSimpleStringBase<CharType>::Truncate( UINT nLen )
|
|
{
|
|
if (Length() < nLen)
|
|
return Length();
|
|
if (!nLen)
|
|
return 0;
|
|
m_pstrData[nLen-1] = 0;
|
|
Resize();
|
|
return Length();
|
|
}
|
|
|
|
template <class CharType>
|
|
int CSimpleStringBase<CharType>::Resize()
|
|
{
|
|
m_nMaxSize = m_pstrData ? GenericLength(m_pstrData) : 0;
|
|
++m_nMaxSize;
|
|
CharType *pszTmp = CreateStorage(m_nMaxSize);
|
|
if (pszTmp)
|
|
{
|
|
if (m_pstrData)
|
|
{
|
|
GenericCopy(pszTmp,m_pstrData);
|
|
DeleteStorage();
|
|
}
|
|
else *pszTmp = 0;
|
|
m_pstrData = pszTmp;
|
|
}
|
|
return Length();
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType>::CSimpleStringBase()
|
|
: m_pstrData(m_pstrAutoData),
|
|
m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),
|
|
m_nGranularity(c_nDefaultGranularity)
|
|
{
|
|
m_pstrAutoData[0] = 0;
|
|
CharType szTmp[1] = { 0};
|
|
Assign(szTmp);
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType>::CSimpleStringBase( const CSimpleStringBase &other )
|
|
: m_pstrData(m_pstrAutoData),
|
|
m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),
|
|
m_nGranularity(c_nDefaultGranularity)
|
|
{
|
|
m_pstrAutoData[0] = 0;
|
|
Assign(other.String());
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType>::CSimpleStringBase( const CharType *szStr )
|
|
: m_pstrData(m_pstrAutoData),
|
|
m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),
|
|
m_nGranularity(c_nDefaultGranularity)
|
|
{
|
|
m_pstrAutoData[0] = 0;
|
|
Assign(szStr);
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType>::CSimpleStringBase( CharType ch )
|
|
: m_pstrData(m_pstrAutoData),
|
|
m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),
|
|
m_nGranularity(c_nDefaultGranularity)
|
|
{
|
|
m_pstrAutoData[0] = 0;
|
|
CharType szTmp[2];
|
|
szTmp[0] = ch;
|
|
szTmp[1] = 0;
|
|
Assign(szTmp);
|
|
}
|
|
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType>::CSimpleStringBase( UINT nResId, HMODULE hModule )
|
|
: m_pstrData(m_pstrAutoData),
|
|
m_nMaxSize(ARRAYSIZE(m_pstrAutoData)),
|
|
m_nGranularity(c_nDefaultGranularity)
|
|
{
|
|
m_pstrAutoData[0] = 0;
|
|
LoadString( nResId, hModule );
|
|
}
|
|
|
|
template <>
|
|
inline CSimpleStringBase<WCHAR> &CSimpleStringBase<WCHAR>::Format( const WCHAR *strFmt, ... )
|
|
{
|
|
WCHAR szTmp[1024] = {0};
|
|
va_list arglist;
|
|
|
|
va_start(arglist, strFmt);
|
|
_vsnwprintf( szTmp, ARRAYSIZE(szTmp)-1, strFmt, arglist );
|
|
va_end(arglist);
|
|
Assign(szTmp);
|
|
return *this;
|
|
}
|
|
|
|
template <>
|
|
inline CSimpleStringBase<CHAR> &CSimpleStringBase<CHAR>::Format( const CHAR *strFmt, ... )
|
|
{
|
|
CHAR szTmp[1024] = {0};
|
|
va_list arglist;
|
|
|
|
va_start(arglist, strFmt);
|
|
_vsnprintf( szTmp, ARRAYSIZE(szTmp)-1, strFmt, arglist );
|
|
va_end(arglist);
|
|
Assign(szTmp);
|
|
return *this;
|
|
}
|
|
|
|
|
|
template <>
|
|
inline CSimpleStringBase<CHAR> &CSimpleStringBase<CHAR>::Format( int nResId, HINSTANCE hInst, ... )
|
|
{
|
|
CSimpleStringBase<CHAR> strFmt;
|
|
if (strFmt.LoadString(nResId,hInst))
|
|
{
|
|
CHAR szTmp[1024] = {0};
|
|
|
|
va_list arglist;
|
|
va_start(arglist, hInst);
|
|
_vsnprintf( szTmp, ARRAYSIZE(szTmp)-1, strFmt, arglist );
|
|
va_end(arglist);
|
|
Assign(szTmp);
|
|
}
|
|
else Assign(NULL);
|
|
return *this;
|
|
}
|
|
|
|
template <>
|
|
inline CSimpleStringBase<WCHAR> &CSimpleStringBase<WCHAR>::Format( int nResId, HINSTANCE hInst, ... )
|
|
{
|
|
CSimpleStringBase<WCHAR> strFmt;
|
|
if (strFmt.LoadString(nResId,hInst))
|
|
{
|
|
WCHAR szTmp[1024] = {0};
|
|
|
|
va_list arglist;
|
|
va_start(arglist, hInst);
|
|
_vsnwprintf( szTmp, ARRAYSIZE(szTmp)-1, strFmt, arglist );
|
|
va_end(arglist);
|
|
Assign(szTmp);
|
|
}
|
|
else Assign(NULL);
|
|
return *this;
|
|
}
|
|
|
|
|
|
template <>
|
|
inline bool CSimpleStringBase<CHAR>::LoadString( UINT nResId, HMODULE hModule )
|
|
{
|
|
if (!hModule)
|
|
{
|
|
hModule = GetModuleHandle(NULL);
|
|
}
|
|
CHAR szTmp[c_nMaxLoadStringBuffer] = {0};
|
|
int nRet = ::LoadStringA( hModule, nResId, szTmp, ARRAYSIZE(szTmp));
|
|
return nRet ? Assign(szTmp) : Assign(NULL);
|
|
}
|
|
|
|
template <>
|
|
inline bool CSimpleStringBase<WCHAR>::LoadString( UINT nResId, HMODULE hModule )
|
|
{
|
|
if (!hModule)
|
|
{
|
|
hModule = GetModuleHandle(NULL);
|
|
}
|
|
WCHAR szTmp[c_nMaxLoadStringBuffer] = {0};
|
|
int nRet = ::LoadStringW( hModule, nResId, szTmp, ARRAYSIZE(szTmp));
|
|
return nRet ? Assign(szTmp) : Assign(NULL);
|
|
}
|
|
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType>::~CSimpleStringBase()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
template <class CharType>
|
|
void CSimpleStringBase<CharType>::DeleteStorage()
|
|
{
|
|
//
|
|
// Only delete the string if it is non-NULL and not pointing to our non-dynamically allocated buffer
|
|
//
|
|
if (m_pstrData && m_pstrData != m_pstrAutoData)
|
|
{
|
|
delete[] m_pstrData;
|
|
}
|
|
m_pstrData = NULL;
|
|
}
|
|
|
|
template <class CharType>
|
|
CharType *CSimpleStringBase<CharType>::CreateStorage( UINT nCount )
|
|
{
|
|
return new CharType[nCount];
|
|
}
|
|
|
|
template <class CharType>
|
|
void CSimpleStringBase<CharType>::Destroy()
|
|
{
|
|
DeleteStorage();
|
|
m_nMaxSize = 0;
|
|
}
|
|
|
|
template <class CharType>
|
|
UINT CSimpleStringBase<CharType>::Length() const
|
|
{
|
|
return(m_pstrData ? GenericLength(m_pstrData) : 0);
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::operator=( const CSimpleStringBase &other )
|
|
{
|
|
if (&other != this)
|
|
{
|
|
Assign(other.String());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::operator=( const CharType *other )
|
|
{
|
|
if (other != String())
|
|
{
|
|
Assign(other);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::operator+=( const CSimpleStringBase &other )
|
|
{
|
|
Concat(other.String());
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
bool CSimpleStringBase<CharType>::Assign( const CharType *szStr )
|
|
{
|
|
if (szStr && EnsureLength(GenericLength(szStr)+1))
|
|
{
|
|
GenericCopy(m_pstrData,szStr);
|
|
}
|
|
else if (EnsureLength(1))
|
|
{
|
|
*m_pstrData = 0;
|
|
}
|
|
else Destroy();
|
|
return(NULL != m_pstrData);
|
|
}
|
|
|
|
template <class CharType>
|
|
bool CSimpleStringBase<CharType>::Assign( const CSimpleStringBase &other )
|
|
{
|
|
return Assign( other.String() );
|
|
}
|
|
|
|
template <class CharType>
|
|
void CSimpleStringBase<CharType>::SetAt( UINT nIndex, CharType chValue )
|
|
{
|
|
//
|
|
// Make sure we don't go off the end of the string or overwrite the '\0'
|
|
//
|
|
if (m_pstrData && Length() > nIndex)
|
|
{
|
|
m_pstrData[nIndex] = chValue;
|
|
}
|
|
}
|
|
|
|
|
|
template <class CharType>
|
|
void CSimpleStringBase<CharType>::Concat( const CSimpleStringBase &other )
|
|
{
|
|
if (EnsureLength( Length() + other.Length() + 1 ))
|
|
{
|
|
GenericConcatenate(m_pstrData,other.String());
|
|
}
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::MakeUpper()
|
|
{
|
|
//
|
|
// Make sure the string is not NULL
|
|
//
|
|
if (m_pstrData)
|
|
{
|
|
IS_CHAR(*m_pstrData) ? CharUpperBuffA( (LPSTR)m_pstrData, Length() ) : CharUpperBuffW( (LPWSTR)m_pstrData, Length() );
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::MakeLower()
|
|
{
|
|
//
|
|
// Make sure the string is not NULL
|
|
//
|
|
if (m_pstrData)
|
|
{
|
|
IS_CHAR(*m_pstrData) ? CharLowerBuffA( (LPSTR)m_pstrData, Length() ) : CharLowerBuffW( (LPWSTR)m_pstrData, Length() );
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> CSimpleStringBase<CharType>::ToUpper() const
|
|
{
|
|
CSimpleStringBase str(*this);
|
|
str.MakeUpper();
|
|
return str;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> CSimpleStringBase<CharType>::ToLower() const
|
|
{
|
|
CSimpleStringBase str(*this);
|
|
str.MakeLower();
|
|
return str;
|
|
}
|
|
|
|
template <class CharType>
|
|
CharType &CSimpleStringBase<CharType>::operator[](int nIndex)
|
|
{
|
|
return m_pstrData[nIndex];
|
|
}
|
|
|
|
template <class CharType>
|
|
const CharType &CSimpleStringBase<CharType>::operator[](int index) const
|
|
{
|
|
return m_pstrData[index];
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::TrimRight()
|
|
{
|
|
CharType *pFirstWhitespaceCharacterInSequence = NULL;
|
|
bool bInWhiteSpace = false;
|
|
CharType *pszPtr = m_pstrData;
|
|
while (pszPtr && *pszPtr)
|
|
{
|
|
if (*pszPtr == L' ' || *pszPtr == L'\t' || *pszPtr == L'\n' || *pszPtr == L'\r')
|
|
{
|
|
if (!bInWhiteSpace)
|
|
{
|
|
pFirstWhitespaceCharacterInSequence = pszPtr;
|
|
bInWhiteSpace = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bInWhiteSpace = false;
|
|
}
|
|
pszPtr = GenericCharNext(pszPtr);
|
|
}
|
|
if (pFirstWhitespaceCharacterInSequence && bInWhiteSpace)
|
|
*pFirstWhitespaceCharacterInSequence = 0;
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::TrimLeft()
|
|
{
|
|
CharType *pszPtr = m_pstrData;
|
|
while (pszPtr && *pszPtr)
|
|
{
|
|
if (*pszPtr == L' ' || *pszPtr == L'\t' || *pszPtr == L'\n' || *pszPtr == L'\r')
|
|
{
|
|
pszPtr = GenericCharNext(pszPtr);
|
|
}
|
|
else break;
|
|
}
|
|
Assign(CSimpleStringBase<CharType>(pszPtr).String());
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
inline CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::Trim()
|
|
{
|
|
TrimLeft();
|
|
TrimRight();
|
|
return *this;
|
|
}
|
|
|
|
//
|
|
// Note that this function WILL NOT WORK CORRECTLY for multi-byte characters in ANSI strings
|
|
//
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> &CSimpleStringBase<CharType>::Reverse()
|
|
{
|
|
UINT nLen = Length();
|
|
for (UINT i = 0;i<nLen/2;i++)
|
|
{
|
|
CharType tmp = m_pstrData[i];
|
|
m_pstrData[i] = m_pstrData[nLen-i-1];
|
|
m_pstrData[nLen-i-1] = tmp;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template <class CharType>
|
|
int CSimpleStringBase<CharType>::Find( CharType cChar ) const
|
|
{
|
|
CharType strTemp[2] = { cChar, 0};
|
|
return Find(strTemp);
|
|
}
|
|
|
|
|
|
template <class CharType>
|
|
int CSimpleStringBase<CharType>::Find( const CSimpleStringBase &other, UINT nStart ) const
|
|
{
|
|
if (!m_pstrData)
|
|
return -1;
|
|
if (nStart > Length())
|
|
return -1;
|
|
CharType *pstrCurr = m_pstrData+nStart, *pstrSrc, *pstrSubStr;
|
|
while (*pstrCurr)
|
|
{
|
|
pstrSrc = pstrCurr;
|
|
pstrSubStr = (CharType *)other.String();
|
|
while (*pstrSrc && *pstrSubStr && *pstrSrc == *pstrSubStr)
|
|
{
|
|
pstrSrc = GenericCharNext(pstrSrc);
|
|
pstrSubStr = GenericCharNext(pstrSubStr);
|
|
}
|
|
if (!*pstrSubStr)
|
|
return static_cast<int>(pstrCurr-m_pstrData);
|
|
pstrCurr = GenericCharNext(pstrCurr);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
template <class CharType>
|
|
int CSimpleStringBase<CharType>::ReverseFind( CharType cChar ) const
|
|
{
|
|
CharType strTemp[2] = { cChar, 0};
|
|
return ReverseFind(strTemp);
|
|
}
|
|
|
|
template <class CharType>
|
|
int CSimpleStringBase<CharType>::ReverseFind( const CSimpleStringBase &srcStr ) const
|
|
{
|
|
int nLastFind = -1, nFind=0;
|
|
while ((nFind = Find( srcStr, nFind )) >= 0)
|
|
{
|
|
nLastFind = nFind;
|
|
++nFind;
|
|
}
|
|
return nLastFind;
|
|
}
|
|
|
|
template <class CharType>
|
|
CSimpleStringBase<CharType> CSimpleStringBase<CharType>::SubStr( int nStart, int nCount ) const
|
|
{
|
|
if (nStart >= (int)Length() || nStart < 0)
|
|
{
|
|
return CSimpleStringBase<CharType>();
|
|
}
|
|
if (nCount < 0)
|
|
{
|
|
nCount = Length() - nStart;
|
|
}
|
|
CSimpleStringBase<CharType> strTmp;
|
|
CharType *pszTmp = CreateStorage(nCount+1);
|
|
if (pszTmp)
|
|
{
|
|
GenericCopyLength( pszTmp, m_pstrData+nStart, nCount+1 );
|
|
pszTmp[nCount] = 0;
|
|
strTmp = pszTmp;
|
|
delete[] pszTmp;
|
|
}
|
|
return strTmp;
|
|
}
|
|
|
|
template <class CharType>
|
|
int CSimpleStringBase<CharType>::CompareNoCase( const CSimpleStringBase &other, int nLength ) const
|
|
{
|
|
if (nLength < 0)
|
|
{
|
|
//
|
|
// Make sure both strings are non-NULL
|
|
//
|
|
if (!String() && !other.String())
|
|
{
|
|
return 0;
|
|
}
|
|
else if (!String())
|
|
{
|
|
return -1;
|
|
}
|
|
else if (!other.String())
|
|
{
|
|
return 1;
|
|
}
|
|
else return GenericCompareNoCase(m_pstrData,other.String());
|
|
}
|
|
CSimpleStringBase<CharType> strSrc(*this);
|
|
CSimpleStringBase<CharType> strTgt(other);
|
|
strSrc.MakeUpper();
|
|
strTgt.MakeUpper();
|
|
//
|
|
// Make sure both strings are non-NULL
|
|
//
|
|
if (!strSrc.String() && !strTgt.String())
|
|
{
|
|
return 0;
|
|
}
|
|
else if (!strSrc.String())
|
|
{
|
|
return -1;
|
|
}
|
|
else if (!strTgt.String())
|
|
{
|
|
return 1;
|
|
}
|
|
else return GenericCompareLength(strSrc.String(),strTgt.String(),nLength);
|
|
}
|
|
|
|
|
|
template <class CharType>
|
|
int CSimpleStringBase<CharType>::Compare( const CSimpleStringBase &other, int nLength ) const
|
|
{
|
|
//
|
|
// Make sure both strings are non-NULL
|
|
//
|
|
if (!String() && !other.String())
|
|
{
|
|
return 0;
|
|
}
|
|
else if (!String())
|
|
{
|
|
return -1;
|
|
}
|
|
else if (!other.String())
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (nLength < 0)
|
|
{
|
|
return GenericCompare(String(),other.String());
|
|
}
|
|
return GenericCompareLength(String(),other.String(),nLength);
|
|
}
|
|
|
|
template <class CharType>
|
|
bool CSimpleStringBase<CharType>::MatchLastCharacter( CharType cChar ) const
|
|
{
|
|
int nFind = ReverseFind(cChar);
|
|
if (nFind < 0)
|
|
return false;
|
|
if (nFind == (int)Length()-1)
|
|
return true;
|
|
else return false;
|
|
}
|
|
|
|
template <class CharType>
|
|
bool CSimpleStringBase<CharType>::Load( HKEY hRegKey, const CharType *pszValueName, const CharType *pszDefault )
|
|
{
|
|
bool bResult = false;
|
|
Assign(pszDefault);
|
|
DWORD nType=0;
|
|
DWORD nSize=0;
|
|
LONG nRet;
|
|
if (IS_CHAR(*m_pstrData))
|
|
nRet = RegQueryValueExA( hRegKey, (LPCSTR)pszValueName, NULL, &nType, NULL, &nSize);
|
|
else nRet = RegQueryValueExW( hRegKey, (LPCWSTR)pszValueName, NULL, &nType, NULL, &nSize);
|
|
if (ERROR_SUCCESS == nRet)
|
|
{
|
|
if ((nType == REG_SZ) || (nType == REG_EXPAND_SZ))
|
|
{
|
|
// Round up to the nearest 2
|
|
nSize = ((nSize + 1) & 0xFFFFFFFE);
|
|
CharType *pstrTemp = CreateStorage(nSize / sizeof(CharType));
|
|
if (pstrTemp)
|
|
{
|
|
if (IS_CHAR(*m_pstrData))
|
|
nRet = RegQueryValueExA( hRegKey, (LPCSTR)pszValueName, NULL, &nType, (PBYTE)pstrTemp, &nSize);
|
|
else nRet = RegQueryValueExW( hRegKey, (LPCWSTR)pszValueName, NULL, &nType, (PBYTE)pstrTemp, &nSize);
|
|
if (ERROR_SUCCESS == nRet)
|
|
{
|
|
Assign(pstrTemp);
|
|
bResult = true;
|
|
}
|
|
delete pstrTemp;
|
|
}
|
|
}
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
template <class CharType>
|
|
bool CSimpleStringBase<CharType>::Store( HKEY hRegKey, const CharType *pszValueName, DWORD nType )
|
|
{
|
|
long nRet;
|
|
if (Length())
|
|
{
|
|
if (IS_CHAR(*m_pstrData))
|
|
{
|
|
nRet = RegSetValueExA( hRegKey, (LPCSTR)pszValueName, 0, nType, (PBYTE)m_pstrData, sizeof(*m_pstrData)*(Length()+1) );
|
|
}
|
|
else
|
|
{
|
|
nRet = RegSetValueExW( hRegKey, (LPCWSTR)pszValueName, 0, nType, (PBYTE)m_pstrData, sizeof(*m_pstrData)*(Length()+1) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CharType strBlank = 0;
|
|
if (IS_CHAR(*m_pstrData))
|
|
{
|
|
nRet = RegSetValueExA( hRegKey, (LPCSTR)pszValueName, 0, nType, (PBYTE)&strBlank, sizeof(CharType) );
|
|
}
|
|
else
|
|
{
|
|
nRet = RegSetValueExW( hRegKey, (LPCWSTR)pszValueName, 0, nType, (PBYTE)&strBlank, sizeof(CharType) );
|
|
}
|
|
}
|
|
return(ERROR_SUCCESS == nRet);
|
|
}
|
|
|
|
|
|
//
|
|
// Two main typedefs
|
|
//
|
|
typedef CSimpleStringBase<char> CSimpleStringAnsi;
|
|
typedef CSimpleStringBase<wchar_t> CSimpleStringWide;
|
|
|
|
//
|
|
// LPCTSTR equivalents
|
|
//
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
typedef CSimpleStringWide CSimpleString;
|
|
#else
|
|
typedef CSimpleStringAnsi CSimpleString;
|
|
#endif
|
|
|
|
//
|
|
// Operators
|
|
//
|
|
inline bool operator<( const CSimpleStringAnsi &a, const CSimpleStringAnsi &b )
|
|
{
|
|
return a.Compare(b) < 0;
|
|
}
|
|
|
|
inline bool operator<( const CSimpleStringWide &a, const CSimpleStringWide &b )
|
|
{
|
|
return a.Compare(b) < 0;
|
|
}
|
|
|
|
inline bool operator<=( const CSimpleStringAnsi &a, const CSimpleStringAnsi &b )
|
|
{
|
|
return a.Compare(b) <= 0;
|
|
}
|
|
|
|
inline bool operator<=( const CSimpleStringWide &a, const CSimpleStringWide &b )
|
|
{
|
|
return a.Compare(b) <= 0;
|
|
}
|
|
|
|
inline bool operator==( const CSimpleStringAnsi &a, const CSimpleStringAnsi &b )
|
|
{
|
|
return a.Compare(b) == 0;
|
|
}
|
|
|
|
inline bool operator==( const CSimpleStringWide &a, const CSimpleStringWide &b )
|
|
{
|
|
return a.Compare(b) == 0;
|
|
}
|
|
|
|
inline bool operator!=( const CSimpleStringAnsi &a, const CSimpleStringAnsi &b )
|
|
{
|
|
return a.Compare(b) != 0;
|
|
}
|
|
|
|
inline bool operator!=( const CSimpleStringWide &a, const CSimpleStringWide &b )
|
|
{
|
|
return a.Compare(b) != 0;
|
|
}
|
|
|
|
inline bool operator>=( const CSimpleStringAnsi &a, const CSimpleStringAnsi &b )
|
|
{
|
|
return a.Compare(b) >= 0;
|
|
}
|
|
|
|
inline bool operator>=( const CSimpleStringWide &a, const CSimpleStringWide &b )
|
|
{
|
|
return a.Compare(b) >= 0;
|
|
}
|
|
|
|
inline bool operator>( const CSimpleStringAnsi &a, const CSimpleStringAnsi &b )
|
|
{
|
|
return a.Compare(b) > 0;
|
|
}
|
|
|
|
inline bool operator>( const CSimpleStringWide &a, const CSimpleStringWide &b )
|
|
{
|
|
return a.Compare(b) > 0;
|
|
}
|
|
|
|
inline CSimpleStringWide operator+( const CSimpleStringWide &a, const CSimpleStringWide &b )
|
|
{
|
|
CSimpleStringWide strResult(a);
|
|
strResult.Concat(b);
|
|
return strResult;
|
|
}
|
|
|
|
inline CSimpleStringAnsi operator+( const CSimpleStringAnsi &a, const CSimpleStringAnsi &b )
|
|
{
|
|
CSimpleStringAnsi strResult(a);
|
|
strResult.Concat(b);
|
|
return strResult;
|
|
}
|
|
|
|
namespace CSimpleStringConvert
|
|
{
|
|
inline
|
|
CSimpleStringWide WideString( const CSimpleStringWide &strSource )
|
|
{
|
|
//
|
|
// Just return the source string
|
|
//
|
|
return strSource;
|
|
}
|
|
|
|
|
|
inline
|
|
CSimpleStringAnsi AnsiString( const CSimpleStringAnsi &strSource )
|
|
{
|
|
//
|
|
// Just return the source string
|
|
//
|
|
return strSource;
|
|
}
|
|
|
|
inline
|
|
CSimpleStringWide WideString( const CSimpleStringAnsi &strSource )
|
|
{
|
|
//
|
|
// Declare the return value. If anything goes wrong, it will contain an empty string
|
|
//
|
|
CSimpleStringWide strResult;
|
|
|
|
//
|
|
// Make sure we have a string
|
|
//
|
|
if (strSource.Length())
|
|
{
|
|
//
|
|
// Find out how long it needs to be
|
|
//
|
|
int nLength = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, strSource.String(), strSource.Length()+1, NULL, 0 );
|
|
if (nLength)
|
|
{
|
|
//
|
|
// Allocate a temporary buffer to hold the converted string
|
|
//
|
|
LPWSTR pwszBuffer = new WCHAR[nLength];
|
|
if (pwszBuffer)
|
|
{
|
|
//
|
|
// Convert the string
|
|
//
|
|
if (MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, strSource.String(), strSource.Length()+1, pwszBuffer, nLength ))
|
|
{
|
|
//
|
|
// Save the result
|
|
//
|
|
strResult = pwszBuffer;
|
|
}
|
|
//
|
|
// Free the temporary buffer
|
|
//
|
|
delete[] pwszBuffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the result
|
|
//
|
|
return strResult;
|
|
}
|
|
|
|
inline
|
|
CSimpleStringAnsi AnsiString( const CSimpleStringWide &strSource )
|
|
{
|
|
//
|
|
// Declare the return value. If anything goes wrong, it will contain an empty string
|
|
//
|
|
CSimpleStringAnsi strResult;
|
|
|
|
//
|
|
// Make sure we have a valid string
|
|
//
|
|
if (strSource.Length())
|
|
{
|
|
//
|
|
// Figure out how long it needs to be
|
|
//
|
|
int nLength = WideCharToMultiByte( CP_ACP, 0, strSource, strSource.Length()+1, NULL, 0, NULL, NULL );
|
|
if (nLength)
|
|
{
|
|
//
|
|
// Allocate a temporary buffer to hold it
|
|
//
|
|
LPSTR pszBuffer = new CHAR[nLength];
|
|
if (pszBuffer)
|
|
{
|
|
//
|
|
// Convert the string
|
|
//
|
|
if (WideCharToMultiByte( CP_ACP, 0, strSource, strSource.Length()+1, pszBuffer, nLength, NULL, NULL ))
|
|
{
|
|
//
|
|
// Save the result
|
|
//
|
|
strResult = pszBuffer;
|
|
}
|
|
|
|
//
|
|
// Save the temporary buffer
|
|
//
|
|
delete[] pszBuffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the result
|
|
//
|
|
return strResult;
|
|
}
|
|
|
|
inline
|
|
CSimpleStringWide FromUtf8( const CSimpleStringAnsi &strSource )
|
|
{
|
|
//
|
|
// Declare the return value. If anything goes wrong, it will contain an empty string
|
|
//
|
|
CSimpleStringWide strResult;
|
|
|
|
//
|
|
// Make sure we have a valid source string
|
|
//
|
|
if (strSource.Length())
|
|
{
|
|
//
|
|
// Find the required target string length
|
|
//
|
|
int nLength = MultiByteToWideChar( CP_UTF8, 0, strSource, strSource.Length()+1, NULL, 0 );
|
|
if (nLength)
|
|
{
|
|
//
|
|
// Allocate a temporary buffer
|
|
//
|
|
LPWSTR pwszBuffer = new WCHAR[nLength];
|
|
if (pwszBuffer)
|
|
{
|
|
//
|
|
// Convert the string
|
|
//
|
|
if (MultiByteToWideChar( CP_UTF8, 0, strSource.String(), strSource.Length()+1, pwszBuffer, nLength ))
|
|
{
|
|
//
|
|
// Save the result
|
|
//
|
|
strResult = pwszBuffer;
|
|
}
|
|
|
|
//
|
|
// Delete the temporary buffer
|
|
//
|
|
delete[] pwszBuffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the result
|
|
//
|
|
return strResult;
|
|
}
|
|
|
|
inline
|
|
CSimpleStringAnsi ToUtf8( const CSimpleStringWide &strSource )
|
|
{
|
|
//
|
|
// Declare the return value. If anything goes wrong, it will contain an empty string
|
|
//
|
|
CSimpleStringAnsi strResult;
|
|
|
|
//
|
|
// Make sure we have a valid source string
|
|
//
|
|
if (strSource.Length())
|
|
{
|
|
int nLength = WideCharToMultiByte( CP_UTF8, 0, strSource, strSource.Length()+1, NULL, 0, NULL, NULL );
|
|
if (nLength)
|
|
{
|
|
//
|
|
// Find the required target string length
|
|
//
|
|
LPSTR pszBuffer = new CHAR[nLength];
|
|
if (pszBuffer)
|
|
{
|
|
//
|
|
// Convert the string
|
|
//
|
|
if (WideCharToMultiByte( CP_UTF8, 0, strSource, strSource.Length()+1, pszBuffer, nLength, NULL, NULL ))
|
|
{
|
|
//
|
|
// Save the result
|
|
//
|
|
strResult = pszBuffer;
|
|
}
|
|
|
|
//
|
|
// Delete the temporary buffer
|
|
//
|
|
delete[] pszBuffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Return the result
|
|
//
|
|
return strResult;
|
|
}
|
|
|
|
#if defined(_UNICODE) || defined(UNICODE)
|
|
template <class CharType>
|
|
CSimpleStringWide NaturalString(const CharType &strSource)
|
|
{
|
|
return WideString(strSource);
|
|
}
|
|
#else
|
|
template <class CharType>
|
|
CSimpleStringAnsi NaturalString(const CharType &strSource)
|
|
{
|
|
return AnsiString(strSource);
|
|
}
|
|
#endif
|
|
|
|
inline CSimpleString NumberToString( int nNumber, LCID Locale=LOCALE_USER_DEFAULT )
|
|
{
|
|
//
|
|
// This turns a string into a number, like so: 3;2;0=32 or 3;0 = 3 or 1;2;3;4;5;6;0 = 123456. Got it?
|
|
//
|
|
TCHAR szDigitGrouping[32] = {0};
|
|
GetLocaleInfo( Locale, LOCALE_SGROUPING, szDigitGrouping, ARRAYSIZE(szDigitGrouping));
|
|
|
|
//
|
|
// Initialize the number format
|
|
//
|
|
NUMBERFMT NumberFormat = {0};
|
|
for (LPTSTR pszCurr = szDigitGrouping; *pszCurr && *pszCurr >= TEXT('1') && *pszCurr <= TEXT('9'); pszCurr += 2)
|
|
{
|
|
NumberFormat.Grouping *= 10;
|
|
NumberFormat.Grouping += (*pszCurr - TEXT('0'));
|
|
}
|
|
|
|
//
|
|
// Get the thousands separator
|
|
//
|
|
TCHAR szThousandsSeparator[32] = {0};
|
|
GetLocaleInfo( Locale, LOCALE_STHOUSAND, szThousandsSeparator, ARRAYSIZE(szThousandsSeparator));
|
|
NumberFormat.lpThousandSep = szThousandsSeparator;
|
|
|
|
//
|
|
// Get the decimal separator
|
|
//
|
|
TCHAR szDecimalSeparator[32] = {0};
|
|
GetLocaleInfo( Locale, LOCALE_SDECIMAL, szDecimalSeparator, ARRAYSIZE(szDecimalSeparator));
|
|
NumberFormat.lpDecimalSep = szDecimalSeparator;
|
|
|
|
//
|
|
// Create the raw number string
|
|
//
|
|
TCHAR szRawNumber[MAX_PATH] = {0};
|
|
_sntprintf( szRawNumber, ARRAYSIZE(szRawNumber)-1, TEXT("%d"), nNumber );
|
|
|
|
//
|
|
// Format the string
|
|
//
|
|
TCHAR szNumberStr[MAX_PATH] = {0};
|
|
if (GetNumberFormat( Locale, 0, szRawNumber, &NumberFormat, szNumberStr, ARRAYSIZE(szNumberStr)))
|
|
{
|
|
return szNumberStr;
|
|
}
|
|
else
|
|
{
|
|
return TEXT("");
|
|
}
|
|
}
|
|
} // End CSimpleStringConvert namespace
|
|
|
|
|
|
//
|
|
// Restore the warning state
|
|
//
|
|
#pragma warning( pop )
|
|
|
|
#endif // ifndef _SIMSTR_H_INCLUDED
|
|
|