mirror of https://github.com/tongzx/nt5src
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.
3258 lines
77 KiB
3258 lines
77 KiB
// This is a part of the Microsoft Foundation Classes C++ library.
|
|
// Copyright (C) 1992-2001 Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This source code is only intended as a supplement to the
|
|
// Microsoft Foundation Classes Reference and related
|
|
// electronic documentation provided with the library.
|
|
// See these sources for detailed information regarding the
|
|
// Microsoft Foundation Classes product.
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CSTRINGT.H - Framework-independent, templateable string class
|
|
|
|
#ifndef __CSTRINGT_H__
|
|
#define __CSTRINGT_H__
|
|
|
|
#pragma once
|
|
|
|
#pragma warning(disable:4786) // avoid 255-character limit warnings
|
|
|
|
#ifdef _MANAGED
|
|
#include <vcclr.h> // For PtrToStringChars
|
|
#endif
|
|
|
|
#include <atlsimpstr.h>
|
|
|
|
#include <objbase.h>
|
|
#include <oleauto.h>
|
|
|
|
#include <stddef.h>
|
|
#ifndef _INC_NEW
|
|
#include <new.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#ifndef _ATL_NO_DEBUG_CRT
|
|
#include <crtdbg.h>
|
|
#endif
|
|
#ifndef _ATL_MIN_CRT
|
|
#include <mbstring.h>
|
|
#endif
|
|
|
|
#ifdef _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
|
|
#define CSTRING_EXPLICIT explicit
|
|
#else
|
|
#define CSTRING_EXPLICIT
|
|
#endif
|
|
|
|
#include <atlconv.h>
|
|
#include <atlmem.h>
|
|
|
|
#pragma push_macro("new")
|
|
#undef new
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Naming conventions:
|
|
// The term "length" can be confusing when dealing with ANSI, Unicode, and
|
|
// MBCS character sets, so this file will use the following naming
|
|
// conventions to differentiate between the different meanings of
|
|
// "length":
|
|
//
|
|
// 'Byte Length' - Length of a buffer in bytes, regardless of character
|
|
// size
|
|
// 'Char Length' - Number of distinct characters in string. For wide-
|
|
// character strings, this is equivalent to half the 'Byte Length'.
|
|
// For ANSI strings, this is equivalent to the 'Byte Length'. For MBCS
|
|
// strings, 'Char Length' counts a lead-byte/trail-byte combination
|
|
// as one character.
|
|
// 'Length' - When neither of the above terms is used, 'Length' refers to
|
|
// length in XCHARs, which is equal to 'Byte Length'/sizeof(XCHAR).
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace ATL
|
|
{
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// inline helpers
|
|
|
|
inline int _wcstombsz(char* mbstr, const wchar_t* wcstr, ULONG count) throw()
|
|
{
|
|
// count is number of bytes
|
|
if (count == 0 && mbstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::WideCharToMultiByte(_AtlGetConversionACP(), 0, wcstr, -1,
|
|
mbstr, count, NULL, NULL);
|
|
ATLASSERT(mbstr == NULL || result <= (int)count);
|
|
return result;
|
|
}
|
|
|
|
inline int _mbstowcsz(wchar_t* wcstr, const char* mbstr, ULONG count) throw()
|
|
{
|
|
// count is number of wchar_t's
|
|
if (count == 0 && wcstr != NULL)
|
|
return 0;
|
|
|
|
int result = ::MultiByteToWideChar(_AtlGetConversionACP(), 0, mbstr, -1,
|
|
wcstr, count);
|
|
ATLASSERT(wcstr == NULL || result <= (int)count);
|
|
if ((result > 0) && (wcstr != NULL))
|
|
wcstr[result-1] = 0;
|
|
return result;
|
|
}
|
|
|
|
#if !defined(_UNICODE) || defined(_CSTRING_ALWAYS_THUNK)
|
|
// Win9x doesn't support Unicode versions of these useful string functions.
|
|
// If the app was built without _UNICODE defined, we thunk at runtime to
|
|
// either the real Unicode implementation (on NT), or a conversion helper
|
|
// (on Win9x).
|
|
|
|
inline void _AtlInstallStringThunk(void** ppThunk, void* pfnWin9x, void* pfnNT) throw()
|
|
{
|
|
static bool s_bWin9x = (::GetVersion()&0x80000000) != 0;
|
|
|
|
void* pfn;
|
|
if (s_bWin9x)
|
|
pfn = pfnWin9x;
|
|
else
|
|
{
|
|
#ifdef _CSTRING_ALWAYS_THUNK
|
|
pfn = pfnWin9x;
|
|
(void)pfnNT;
|
|
#else
|
|
pfn = pfnNT;
|
|
#endif
|
|
}
|
|
InterlockedExchangePointer(ppThunk, pfn);
|
|
}
|
|
|
|
typedef int (WINAPI* ATLCOMPARESTRINGW)(LCID, DWORD, LPCWSTR, int, LPCWSTR, int);
|
|
typedef BOOL (WINAPI* ATLGETSTRINGTYPEEXW)(LCID, DWORD, LPCWSTR, int, LPWORD);
|
|
typedef int (WINAPI* ATLLSTRCMPIW)(LPCWSTR, LPCWSTR);
|
|
typedef LPWSTR (WINAPI* ATLCHARLOWERW)(LPWSTR);
|
|
typedef LPWSTR (WINAPI* ATLCHARUPPERW)(LPWSTR);
|
|
typedef DWORD (WINAPI* ATLGETENVIRONMENTVARIABLEW)(LPCWSTR, LPWSTR, DWORD);
|
|
|
|
struct _AtlStringThunks
|
|
{
|
|
ATLCOMPARESTRINGW pfnCompareStringW;
|
|
ATLGETSTRINGTYPEEXW pfnGetStringTypeExW;
|
|
ATLLSTRCMPIW pfnlstrcmpiW;
|
|
ATLCHARLOWERW pfnCharLowerW;
|
|
ATLCHARUPPERW pfnCharUpperW;
|
|
ATLGETENVIRONMENTVARIABLEW pfnGetEnvironmentVariableW;
|
|
};
|
|
|
|
extern _AtlStringThunks _strthunks;
|
|
|
|
inline DWORD WINAPI GetEnvironmentVariableWFake(LPCWSTR pszName,
|
|
LPWSTR pszBuffer, DWORD nSize)
|
|
{
|
|
USES_CONVERSION;
|
|
ULONG nSizeA;
|
|
ULONG nSizeW;
|
|
LPSTR pszNameA;
|
|
LPSTR pszBufferA;
|
|
|
|
pszNameA = W2A(pszName);
|
|
nSizeA = ::GetEnvironmentVariableA(pszNameA, NULL, 0);
|
|
if (nSizeA == 0)
|
|
return 0;
|
|
|
|
pszBufferA = LPSTR(_alloca(nSizeA*2));
|
|
::GetEnvironmentVariableA(pszNameA, pszBufferA, nSizeA);
|
|
|
|
nSizeW = ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszBufferA, -1, NULL, 0);
|
|
if (nSize == 0)
|
|
return nSizeW;
|
|
ATLASSERT(nSize >= nSizeW);
|
|
::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszBufferA, -1, pszBuffer, nSizeW);
|
|
|
|
return nSizeW;
|
|
}
|
|
|
|
inline DWORD WINAPI GetEnvironmentVariableWThunk(LPCWSTR pszName,
|
|
LPWSTR pszBuffer, DWORD nSize)
|
|
{
|
|
_AtlInstallStringThunk(reinterpret_cast<void**>(&_strthunks.pfnGetEnvironmentVariableW),
|
|
GetEnvironmentVariableWFake, ::GetEnvironmentVariableW);
|
|
|
|
return _strthunks.pfnGetEnvironmentVariableW(pszName, pszBuffer, nSize);
|
|
}
|
|
|
|
inline int WINAPI CompareStringWFake(LCID lcid, DWORD dwFlags,
|
|
LPCWSTR pszString1, int nLength1, LPCWSTR pszString2, int nLength2)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
return ::CompareStringA(lcid, dwFlags, W2A(pszString1), nLength1, W2A(pszString2), nLength2);
|
|
}
|
|
|
|
inline int WINAPI CompareStringWThunk(LCID lcid, DWORD dwFlags,
|
|
LPCWSTR pszString1, int nLength1, LPCWSTR pszString2, int nLength2)
|
|
{
|
|
_AtlInstallStringThunk(reinterpret_cast<void**>(&_strthunks.pfnCompareStringW), CompareStringWFake, ::CompareStringW);
|
|
|
|
return _strthunks.pfnCompareStringW(lcid, dwFlags, pszString1, nLength1, pszString2, nLength2);
|
|
}
|
|
|
|
inline BOOL WINAPI GetStringTypeExWFake(LCID lcid, DWORD dwInfoType, LPCWSTR pszSrc,
|
|
int nLength, LPWORD pwCharType)
|
|
{
|
|
int nLengthA;
|
|
LPSTR pszA;
|
|
|
|
nLengthA = ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nLength, NULL, 0, NULL, NULL);
|
|
pszA = LPSTR(_alloca(nLengthA*sizeof(char)));
|
|
::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nLength, pszA, nLengthA, NULL, NULL);
|
|
|
|
if (nLength == -1)
|
|
nLengthA = -1;
|
|
|
|
return ::GetStringTypeExA(lcid, dwInfoType, pszA, nLengthA, pwCharType);
|
|
}
|
|
|
|
inline BOOL WINAPI GetStringTypeExWThunk(LCID lcid, DWORD dwInfoType, LPCWSTR pszSrc,
|
|
int nLength, LPWORD pwCharType)
|
|
{
|
|
_AtlInstallStringThunk(reinterpret_cast<void**>(&_strthunks.pfnGetStringTypeExW), GetStringTypeExWFake, ::GetStringTypeExW);
|
|
|
|
return _strthunks.pfnGetStringTypeExW(lcid, dwInfoType, pszSrc, nLength, pwCharType);
|
|
}
|
|
|
|
inline int WINAPI lstrcmpiWFake(LPCWSTR psz1, LPCWSTR psz2)
|
|
{
|
|
USES_CONVERSION;
|
|
|
|
return ::lstrcmpiA(W2A(psz1), W2A(psz2));
|
|
}
|
|
|
|
inline int WINAPI lstrcmpiWThunk(LPCWSTR psz1, LPCWSTR psz2)
|
|
{
|
|
_AtlInstallStringThunk(reinterpret_cast<void**>(&_strthunks.pfnlstrcmpiW), lstrcmpiWFake, ::lstrcmpiW);
|
|
|
|
return _strthunks.pfnlstrcmpiW(psz1, psz2);
|
|
}
|
|
|
|
inline LPWSTR WINAPI CharLowerWFake(LPWSTR psz)
|
|
{
|
|
USES_CONVERSION;
|
|
LPSTR pszA;
|
|
|
|
pszA = W2A(psz);
|
|
::CharLowerA(pszA);
|
|
wcscpy(psz, A2W(pszA));
|
|
|
|
return psz;
|
|
}
|
|
|
|
inline LPWSTR WINAPI CharLowerWThunk(LPWSTR psz)
|
|
{
|
|
_AtlInstallStringThunk(reinterpret_cast<void**>(&_strthunks.pfnCharLowerW), CharLowerWFake, ::CharLowerW);
|
|
|
|
return _strthunks.pfnCharLowerW(psz);
|
|
}
|
|
|
|
inline LPWSTR WINAPI CharUpperWFake(LPWSTR psz)
|
|
{
|
|
USES_CONVERSION;
|
|
LPSTR pszA;
|
|
|
|
pszA = W2A(psz);
|
|
::CharUpperA(pszA);
|
|
wcscpy(psz, A2W(pszA));
|
|
|
|
return psz;
|
|
}
|
|
|
|
inline LPWSTR WINAPI CharUpperWThunk(LPWSTR psz)
|
|
{
|
|
_AtlInstallStringThunk(reinterpret_cast<void**>(&_strthunks.pfnCharUpperW), CharUpperWFake, ::CharUpperW);
|
|
|
|
return _strthunks.pfnCharUpperW(psz);
|
|
}
|
|
|
|
__declspec(selectany) _AtlStringThunks _strthunks =
|
|
{
|
|
CompareStringWThunk,
|
|
GetStringTypeExWThunk,
|
|
lstrcmpiWThunk,
|
|
CharLowerWThunk,
|
|
CharUpperWThunk,
|
|
GetEnvironmentVariableWThunk
|
|
};
|
|
|
|
#endif // !_UNICODE
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
#ifndef _ATL_MIN_CRT
|
|
template< typename _CharType = char >
|
|
class ChTraitsCRT :
|
|
public ChTraitsBase< _CharType >
|
|
{
|
|
public:
|
|
static char* CharNext( const char* p ) throw()
|
|
{
|
|
return reinterpret_cast< char* >( _mbsinc( reinterpret_cast< const unsigned char* >( p ) ) );
|
|
}
|
|
|
|
static int IsDigit( char ch ) throw()
|
|
{
|
|
return _ismbcdigit( ch );
|
|
}
|
|
|
|
static int IsSpace( char ch ) throw()
|
|
{
|
|
return _ismbcspace( ch );
|
|
}
|
|
|
|
static int StringCompare( LPCSTR pszA, LPCSTR pszB ) throw()
|
|
{
|
|
return _mbscmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ) );
|
|
}
|
|
|
|
static int StringCompareIgnore( LPCSTR pszA, LPCSTR pszB ) throw()
|
|
{
|
|
return _mbsicmp( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ) );
|
|
}
|
|
|
|
static int StringCollate( LPCSTR pszA, LPCSTR pszB ) throw()
|
|
{
|
|
return _mbscoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ) );
|
|
}
|
|
|
|
static int StringCollateIgnore( LPCSTR pszA, LPCSTR pszB ) throw()
|
|
{
|
|
return _mbsicoll( reinterpret_cast< const unsigned char* >( pszA ), reinterpret_cast< const unsigned char* >( pszB ) );
|
|
}
|
|
|
|
static LPSTR StringFindString( LPCSTR pszBlock, LPCSTR pszMatch ) throw()
|
|
{
|
|
return reinterpret_cast< LPSTR >( _mbsstr( reinterpret_cast< const unsigned char* >( pszBlock ),
|
|
reinterpret_cast< const unsigned char* >( pszMatch ) ) );
|
|
}
|
|
|
|
static LPSTR StringFindChar( LPCSTR pszBlock, char chMatch ) throw()
|
|
{
|
|
return reinterpret_cast< LPSTR >( _mbschr( reinterpret_cast< const unsigned char* >( pszBlock ), chMatch ) );
|
|
}
|
|
|
|
static LPSTR StringFindCharRev( LPCSTR psz, char ch ) throw()
|
|
{
|
|
return reinterpret_cast< LPSTR >( _mbsrchr( reinterpret_cast< const unsigned char* >( psz ), ch ) );
|
|
}
|
|
|
|
static LPSTR StringScanSet( LPCSTR pszBlock, LPCSTR pszMatch ) throw()
|
|
{
|
|
return reinterpret_cast< LPSTR >( _mbspbrk( reinterpret_cast< const unsigned char* >( pszBlock ),
|
|
reinterpret_cast< const unsigned char* >( pszMatch ) ) );
|
|
}
|
|
|
|
static int StringSpanIncluding( LPCSTR pszBlock, LPCSTR pszSet ) throw()
|
|
{
|
|
return (int)_mbsspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ) );
|
|
}
|
|
|
|
static int StringSpanExcluding( LPCSTR pszBlock, LPCSTR pszSet ) throw()
|
|
{
|
|
return (int)_mbscspn( reinterpret_cast< const unsigned char* >( pszBlock ), reinterpret_cast< const unsigned char* >( pszSet ) );
|
|
}
|
|
|
|
static LPSTR StringUppercase( LPSTR psz ) throw()
|
|
{
|
|
return reinterpret_cast< LPSTR >( _mbsupr( reinterpret_cast< unsigned char* >( psz ) ) );
|
|
}
|
|
|
|
static LPSTR StringLowercase( LPSTR psz ) throw()
|
|
{
|
|
return reinterpret_cast< LPSTR >( _mbslwr( reinterpret_cast< unsigned char* >( psz ) ) );
|
|
}
|
|
|
|
static LPSTR StringReverse( LPSTR psz ) throw()
|
|
{
|
|
return reinterpret_cast< LPSTR >( _mbsrev( reinterpret_cast< unsigned char* >( psz ) ) );
|
|
}
|
|
|
|
static int GetFormattedLength( LPCSTR pszFormat, va_list args ) throw()
|
|
{
|
|
return _vscprintf( pszFormat, args );
|
|
}
|
|
|
|
static int Format( LPSTR pszBuffer, LPCSTR pszFormat, va_list args ) throw()
|
|
{
|
|
return vsprintf( pszBuffer, pszFormat, args );
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCSTR pszSrc ) throw()
|
|
{
|
|
// Returns required buffer length in XCHARs
|
|
return int( strlen( pszSrc ) );
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCSTR pszSrc, int nLength ) throw()
|
|
{
|
|
(void)pszSrc;
|
|
// Returns required buffer length in XCHARs
|
|
return nLength;
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCWSTR pszSource ) throw()
|
|
{
|
|
// Returns required buffer length in XCHARs
|
|
return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, -1, NULL, 0, NULL, NULL )-1;
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCWSTR pszSource, int nLength ) throw()
|
|
{
|
|
// Returns required buffer length in XCHARs
|
|
return ::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSource, nLength, NULL, 0, NULL, NULL );
|
|
}
|
|
|
|
static void ConvertToBaseType( LPSTR pszDest, int nDestLength,
|
|
LPCSTR pszSrc, int nSrcLength = -1 ) throw()
|
|
{
|
|
(void)nSrcLength;
|
|
// nLen is in XCHARs
|
|
memcpy( pszDest, pszSrc, nDestLength*sizeof( char ) );
|
|
}
|
|
|
|
static void ConvertToBaseType( LPSTR pszDest, int nDestLength,
|
|
LPCWSTR pszSrc, int nSrcLength = -1 ) throw()
|
|
{
|
|
// nLen is in XCHARs
|
|
::WideCharToMultiByte( _AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL );
|
|
}
|
|
|
|
static void ConvertToOem( LPSTR psz ) throw()
|
|
{
|
|
::AnsiToOem( psz, psz );
|
|
}
|
|
|
|
static void ConvertToAnsi( LPSTR psz ) throw()
|
|
{
|
|
::OemToAnsi( psz, psz );
|
|
}
|
|
|
|
static void FloodCharacters( char ch, int nLength, char* pch ) throw()
|
|
{
|
|
// nLength is in XCHARs
|
|
memset( pch, ch, nLength );
|
|
}
|
|
|
|
static BSTR AllocSysString( const char* pchData, int nDataLength ) throw()
|
|
{
|
|
int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength,
|
|
NULL, NULL );
|
|
BSTR bstr = ::SysAllocStringLen( NULL, nLen );
|
|
if( bstr != NULL )
|
|
{
|
|
::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength,
|
|
bstr, nLen );
|
|
}
|
|
|
|
return bstr;
|
|
}
|
|
|
|
static BOOL ReAllocSysString( const char* pchData, BSTR* pbstr, int nDataLength ) throw()
|
|
{
|
|
int nLen = ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, NULL, NULL );
|
|
BOOL bSuccess = ::SysReAllocStringLen( pbstr, NULL, nLen );
|
|
if( bSuccess )
|
|
{
|
|
::MultiByteToWideChar( _AtlGetConversionACP(), 0, pchData, nDataLength, *pbstr, nLen );
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
static DWORD FormatMessage( DWORD dwFlags, LPCVOID pSource,
|
|
DWORD dwMessageID, DWORD dwLanguageID, LPSTR pszBuffer,
|
|
DWORD nSize, va_list* pArguments ) throw()
|
|
{
|
|
return ::FormatMessageA( dwFlags, pSource, dwMessageID, dwLanguageID,
|
|
pszBuffer, nSize, pArguments );
|
|
}
|
|
|
|
static int SafeStringLen( LPCSTR psz ) throw()
|
|
{
|
|
// returns length in bytes
|
|
return (psz != NULL) ? int( strlen( psz ) ) : 0;
|
|
}
|
|
|
|
static int SafeStringLen( LPCWSTR psz ) throw()
|
|
{
|
|
// returns length in wchar_ts
|
|
return (psz != NULL) ? int( wcslen( psz ) ) : 0;
|
|
}
|
|
|
|
static int GetCharLen( const wchar_t* pch ) throw()
|
|
{
|
|
(void)pch;
|
|
// returns char length
|
|
return 1;
|
|
}
|
|
|
|
static int GetCharLen( const char* pch ) throw()
|
|
{
|
|
// returns char length
|
|
return int( _mbclen( reinterpret_cast< const unsigned char* >( pch ) ) );
|
|
}
|
|
|
|
static DWORD GetEnvironmentVariable( LPCSTR pszVar,
|
|
LPSTR pszBuffer, DWORD dwSize ) throw()
|
|
{
|
|
return ::GetEnvironmentVariableA( pszVar, pszBuffer, dwSize );
|
|
}
|
|
};
|
|
|
|
// specialization for wchar_t
|
|
template<>
|
|
class ChTraitsCRT< wchar_t > :
|
|
public ChTraitsBase< wchar_t >
|
|
{
|
|
#if defined(_UNICODE) && !defined(_CSTRING_ALWAYS_THUNK)
|
|
static DWORD _GetEnvironmentVariableW( LPCWSTR pszName, LPWSTR pszBuffer, DWORD nSize ) throw()
|
|
{
|
|
return ::GetEnvironmentVariableW( pszName, pszBuffer, nSize );
|
|
}
|
|
#else // !_UNICODE
|
|
static DWORD WINAPI _GetEnvironmentVariableW( LPCWSTR pszName,
|
|
LPWSTR pszBuffer, DWORD nSize ) throw()
|
|
{
|
|
return _strthunks.pfnGetEnvironmentVariableW( pszName, pszBuffer, nSize );
|
|
}
|
|
#endif // !_UNICODE
|
|
|
|
public:
|
|
static LPWSTR CharNext( LPCWSTR psz ) throw()
|
|
{
|
|
return const_cast< LPWSTR >( psz+1 );
|
|
}
|
|
|
|
static int IsDigit( wchar_t ch ) throw()
|
|
{
|
|
return iswdigit( ch );
|
|
}
|
|
|
|
static int IsSpace( wchar_t ch ) throw()
|
|
{
|
|
return iswspace( ch );
|
|
}
|
|
|
|
static int StringCompare( LPCWSTR pszA, LPCWSTR pszB ) throw()
|
|
{
|
|
return wcscmp( pszA, pszB );
|
|
}
|
|
|
|
static int StringCompareIgnore( LPCWSTR pszA, LPCWSTR pszB ) throw()
|
|
{
|
|
return _wcsicmp( pszA, pszB );
|
|
}
|
|
|
|
static int StringCollate( LPCWSTR pszA, LPCWSTR pszB ) throw()
|
|
{
|
|
return wcscoll( pszA, pszB );
|
|
}
|
|
|
|
static int StringCollateIgnore( LPCWSTR pszA, LPCWSTR pszB ) throw()
|
|
{
|
|
return _wcsicoll( pszA, pszB );
|
|
}
|
|
|
|
static LPWSTR StringFindString( LPCWSTR pszBlock, LPCWSTR pszMatch ) throw()
|
|
{
|
|
return wcsstr( pszBlock, pszMatch );
|
|
}
|
|
|
|
static LPWSTR StringFindChar( LPCWSTR pszBlock, wchar_t chMatch ) throw()
|
|
{
|
|
return wcschr( pszBlock, chMatch );
|
|
}
|
|
|
|
static LPWSTR StringFindCharRev( LPCWSTR psz, wchar_t ch ) throw()
|
|
{
|
|
return wcsrchr( psz, ch );
|
|
}
|
|
|
|
static LPWSTR StringScanSet( LPCWSTR pszBlock, LPCWSTR pszMatch ) throw()
|
|
{
|
|
return wcspbrk( pszBlock, pszMatch );
|
|
}
|
|
|
|
static int StringSpanIncluding( LPCWSTR pszBlock, LPCWSTR pszSet ) throw()
|
|
{
|
|
return (int)wcsspn( pszBlock, pszSet );
|
|
}
|
|
|
|
static int StringSpanExcluding( LPCWSTR pszBlock, LPCWSTR pszSet ) throw()
|
|
{
|
|
return (int)wcscspn( pszBlock, pszSet );
|
|
}
|
|
|
|
static LPWSTR StringUppercase( LPWSTR psz ) throw()
|
|
{
|
|
return _wcsupr( psz );
|
|
}
|
|
|
|
static LPWSTR StringLowercase( LPWSTR psz ) throw()
|
|
{
|
|
return _wcslwr( psz );
|
|
}
|
|
|
|
static LPWSTR StringReverse( LPWSTR psz ) throw()
|
|
{
|
|
return _wcsrev( psz );
|
|
}
|
|
|
|
static int GetFormattedLength( LPCWSTR pszFormat, va_list args) throw()
|
|
{
|
|
return _vscwprintf( pszFormat, args );
|
|
}
|
|
|
|
static int Format( LPWSTR pszBuffer, LPCWSTR pszFormat, va_list args) throw()
|
|
{
|
|
return vswprintf( pszBuffer, pszFormat, args );
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCSTR pszSrc ) throw()
|
|
{
|
|
// Returns required buffer size in wchar_ts
|
|
return ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pszSrc, -1, NULL, 0 )-1;
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCSTR pszSrc, int nLength ) throw()
|
|
{
|
|
// Returns required buffer size in wchar_ts
|
|
return ::MultiByteToWideChar( _AtlGetConversionACP(), 0, pszSrc, nLength, NULL, 0 );
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCWSTR pszSrc ) throw()
|
|
{
|
|
// Returns required buffer size in wchar_ts
|
|
return (int)wcslen( pszSrc );
|
|
}
|
|
|
|
static int GetBaseTypeLength( LPCWSTR pszSrc, int nLength ) throw()
|
|
{
|
|
(void)pszSrc;
|
|
// Returns required buffer size in wchar_ts
|
|
return nLength;
|
|
}
|
|
|
|
static void ConvertToBaseType( LPWSTR pszDest, int nDestLength,
|
|
LPCSTR pszSrc, int nSrcLength = -1) throw()
|
|
{
|
|
// nLen is in wchar_ts
|
|
::MultiByteToWideChar( _AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength );
|
|
}
|
|
|
|
static void ConvertToBaseType( LPWSTR pszDest, int nDestLength,
|
|
LPCWSTR pszSrc, int nSrcLength = -1) throw()
|
|
{
|
|
(void)nSrcLength;
|
|
// nLen is in wchar_ts
|
|
memcpy( pszDest, pszSrc, nDestLength*sizeof( wchar_t ) );
|
|
}
|
|
|
|
static void FloodCharacters( wchar_t ch, int nLength, LPWSTR psz ) throw()
|
|
{
|
|
// nLength is in XCHARs
|
|
for( int i = 0; i < nLength; i++ )
|
|
{
|
|
psz[i] = ch;
|
|
}
|
|
}
|
|
|
|
static BSTR AllocSysString( const wchar_t* pchData, int nDataLength ) throw()
|
|
{
|
|
return ::SysAllocStringLen( pchData, nDataLength );
|
|
}
|
|
|
|
static BOOL ReAllocSysString( const wchar_t* pchData, BSTR* pbstr, int nDataLength ) throw()
|
|
{
|
|
return ::SysReAllocStringLen( pbstr, pchData, nDataLength );
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
static DWORD FormatMessage( DWORD dwFlags, LPCVOID pSource,
|
|
DWORD dwMessageID, DWORD dwLanguageID, LPWSTR pszBuffer,
|
|
DWORD nSize, va_list* pArguments ) throw()
|
|
{
|
|
return ::FormatMessageW( dwFlags, pSource, dwMessageID, dwLanguageID,
|
|
pszBuffer, nSize, pArguments );
|
|
}
|
|
#endif
|
|
|
|
static int SafeStringLen( LPCSTR psz ) throw()
|
|
{
|
|
// returns length in bytes
|
|
return (psz != NULL) ? (int)strlen( psz ) : 0;
|
|
}
|
|
|
|
static int SafeStringLen( LPCWSTR psz ) throw()
|
|
{
|
|
// returns length in wchar_ts
|
|
return (psz != NULL) ? (int)wcslen( psz ) : 0;
|
|
}
|
|
|
|
static int GetCharLen( const wchar_t* pch ) throw()
|
|
{
|
|
(void)pch;
|
|
// returns char length
|
|
return 1;
|
|
}
|
|
|
|
static int GetCharLen( const char* pch ) throw()
|
|
{
|
|
// returns char length
|
|
return (int)( _mbclen( reinterpret_cast< const unsigned char* >( pch ) ) );
|
|
}
|
|
|
|
static DWORD GetEnvironmentVariable( LPCWSTR pszVar, LPWSTR pszBuffer, DWORD dwSize ) throw()
|
|
{
|
|
return _GetEnvironmentVariableW( pszVar, pszBuffer, dwSize );
|
|
}
|
|
};
|
|
#endif // _ATL_MIN_CRT
|
|
|
|
template< typename _CharType = char >
|
|
class ChTraitsOS :
|
|
public ChTraitsBase< _CharType >
|
|
{
|
|
public:
|
|
static int tclen(const _CharType* p) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
_CharType* pnext = CharNext(p);
|
|
return ((pnext-p)>1) ? 2 : 1;
|
|
}
|
|
static _CharType* strchr(const _CharType* p, _CharType ch) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
//strchr for '\0' should succeed
|
|
do
|
|
{
|
|
if (*p == ch)
|
|
{
|
|
return const_cast< _CharType* >( p );
|
|
}
|
|
p = CharNext(p);
|
|
} while( *p != 0 );
|
|
|
|
return NULL;
|
|
}
|
|
static _CharType* strchr_db(const _CharType* p, char ch1, char ch2) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
while (*p != 0)
|
|
{
|
|
if (*p == ch1 && *(p+1) == ch2)
|
|
{
|
|
return const_cast< _CharType* >( p );
|
|
}
|
|
p = CharNext(p);
|
|
}
|
|
return NULL;
|
|
}
|
|
static _CharType* strrchr(const _CharType* p, _CharType ch) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
const _CharType* pch = NULL;
|
|
while (*p != 0)
|
|
{
|
|
if (*p == ch)
|
|
pch = p;
|
|
p = CharNext(p);
|
|
}
|
|
return const_cast< _CharType* >( pch );
|
|
}
|
|
static _CharType* _strrev(_CharType* psz) throw()
|
|
{
|
|
// Optimize NULL, zero-length, and single-char case.
|
|
if ((psz == NULL) || (psz[0] == '\0') || (psz[1] == '\0'))
|
|
return psz;
|
|
|
|
_CharType* p = psz;
|
|
|
|
while (p[1] != 0)
|
|
{
|
|
_CharType* pNext = CharNext(p);
|
|
if(pNext > p + 1)
|
|
{
|
|
char p1 = *p;
|
|
*p = *(p + 1);
|
|
*(p + 1) = p1;
|
|
}
|
|
p = pNext;
|
|
}
|
|
|
|
_CharType* q = psz;
|
|
|
|
while (q < p)
|
|
{
|
|
_CharType t = *q;
|
|
*q = *p;
|
|
*p = t;
|
|
q++;
|
|
p--;
|
|
}
|
|
return psz;
|
|
}
|
|
static _CharType* strstr(const _CharType* pStr, const _CharType* pCharSet) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
int nLen = lstrlenA(pCharSet);
|
|
if (nLen == 0)
|
|
return const_cast<_CharType*>(pStr);
|
|
|
|
const _CharType* pMatch;
|
|
const _CharType* pStart = pStr;
|
|
while ((pMatch = strchr(pStart, *pCharSet)) != NULL)
|
|
{
|
|
if (memcmp(pMatch, pCharSet, nLen*sizeof(_CharType)) == 0)
|
|
return const_cast<_CharType*>(pMatch);
|
|
pStart = CharNextA(pMatch);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
static int strspn(const _CharType* pStr, const _CharType* pCharSet) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
int nRet = 0;
|
|
_CharType* p = pStr;
|
|
while (*p != 0)
|
|
{
|
|
_CharType* pNext = CharNext(p);
|
|
if(pNext > p + 1)
|
|
{
|
|
if(strchr_db(pCharSet, *p, *(p+1)) == NULL)
|
|
break;
|
|
nRet += 2;
|
|
}
|
|
else
|
|
{
|
|
if(strchr(pCharSet, *p) == NULL)
|
|
break;
|
|
nRet++;
|
|
}
|
|
p = pNext;
|
|
}
|
|
return nRet;
|
|
}
|
|
static int strcspn(const _CharType* pStr, const _CharType* pCharSet) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
int nRet = 0;
|
|
_CharType* p = pStr;
|
|
while (*p != 0)
|
|
{
|
|
_CharType* pNext = CharNext(p);
|
|
if(pNext > p + 1)
|
|
{
|
|
if(strchr_db(pCharSet, *p, *(p+1)) != NULL)
|
|
break;
|
|
nRet += 2;
|
|
}
|
|
else
|
|
{
|
|
if(strchr(pCharSet, *p) != NULL)
|
|
break;
|
|
nRet++;
|
|
}
|
|
p = pNext;
|
|
}
|
|
return nRet;
|
|
}
|
|
static _CharType* strpbrk(const _CharType* p, const _CharType* lpszCharSet) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
while (*p != 0)
|
|
{
|
|
if (strchr(lpszCharSet, *p) != NULL)
|
|
{
|
|
return const_cast< _CharType* >( p );
|
|
}
|
|
p = CharNext(p);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static _CharType* CharNext(const _CharType* p) throw()
|
|
{
|
|
ATLASSERT(p != NULL);
|
|
if (*p == '\0') // ::CharNextA won't increment if we're at a \0 already
|
|
return const_cast<_CharType*>(p+1);
|
|
else
|
|
return ::CharNextA(p);
|
|
}
|
|
|
|
static int IsDigit(_CharType ch) throw()
|
|
{
|
|
WORD type;
|
|
GetStringTypeExA(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
|
|
return (type & C1_DIGIT) == C1_DIGIT;
|
|
}
|
|
|
|
static int IsSpace(_CharType ch) throw()
|
|
{
|
|
WORD type;
|
|
GetStringTypeExA(GetThreadLocale(), CT_CTYPE1, &ch, 1, &type);
|
|
return (type & C1_SPACE) == C1_SPACE;
|
|
}
|
|
|
|
static int StringCompare(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
return lstrcmpA((LPCSTR) pstrOne, (LPCSTR) pstrOther);
|
|
}
|
|
|
|
static int StringCompareIgnore(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
return lstrcmpiA((LPCSTR) pstrOne, (LPCSTR) pstrOther);
|
|
}
|
|
|
|
static int StringCollate(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
int nRet = CompareStringA(GetThreadLocale(), 0, (LPCSTR)pstrOne, -1,
|
|
(LPCSTR)pstrOther, -1);
|
|
ATLASSERT(nRet != 0);
|
|
return nRet-2; // Convert to strcmp convention. This really is documented.
|
|
}
|
|
|
|
static int StringCollateIgnore(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
int nRet = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, (LPCSTR)pstrOne, -1,
|
|
(LPCSTR)pstrOther, -1);
|
|
ATLASSERT(nRet != 0);
|
|
return nRet-2; // Convert to strcmp convention. This really is documented.
|
|
}
|
|
|
|
static _CharType* StringFindString(const _CharType* pstrBlock,
|
|
const _CharType* pstrMatch) throw()
|
|
{
|
|
return strstr(pstrBlock, pstrMatch);
|
|
}
|
|
|
|
static _CharType* StringFindChar(const _CharType* pstrBlock,
|
|
_CharType pstrMatch) throw()
|
|
{
|
|
return strchr(pstrBlock, pstrMatch);
|
|
}
|
|
|
|
static _CharType* StringFindCharRev(const _CharType* pstr, _CharType ch) throw()
|
|
{
|
|
return strrchr(pstr, ch);
|
|
}
|
|
|
|
static _CharType* StringScanSet(const _CharType* pstrBlock,
|
|
const _CharType* pstrMatch) throw()
|
|
{
|
|
return strpbrk(pstrBlock, pstrMatch);
|
|
}
|
|
|
|
static int StringSpanIncluding(const _CharType* pstrBlock,
|
|
const _CharType* pstrSet) throw()
|
|
{
|
|
return strspn(pstrBlock, pstrSet);
|
|
}
|
|
|
|
static int StringSpanExcluding(const _CharType* pstrBlock,
|
|
const _CharType* pstrSet) throw()
|
|
{
|
|
return strcspn(pstrBlock, pstrSet);
|
|
}
|
|
|
|
static _CharType* StringUppercase(_CharType* psz) throw()
|
|
{
|
|
return CharUpperA( psz );
|
|
}
|
|
|
|
static _CharType* StringLowercase(_CharType* psz) throw()
|
|
{
|
|
return CharLowerA( psz );
|
|
}
|
|
|
|
static _CharType* StringReverse(_CharType* psz) throw()
|
|
{
|
|
return _strrev( psz );
|
|
}
|
|
|
|
static int GetFormattedLength(const _CharType* pszFormat, va_list args) throw()
|
|
{
|
|
_CharType szBuffer[1028];
|
|
|
|
// wvsprintf always truncates the output to 1024 character plus
|
|
// the '\0'.
|
|
int nLength = wvsprintfA(szBuffer, pszFormat, args);
|
|
ATLASSERT(nLength >= 0);
|
|
ATLASSERT(nLength <= 1024);
|
|
|
|
return nLength;
|
|
}
|
|
|
|
static int Format(_CharType* pszBuffer, const _CharType* pszFormat,
|
|
va_list args) throw()
|
|
{
|
|
return wvsprintfA(pszBuffer, pszFormat, args);
|
|
}
|
|
|
|
static int GetBaseTypeLength(const char* pszSrc) throw()
|
|
{
|
|
// Returns required buffer length in XCHARs
|
|
return lstrlenA(pszSrc);
|
|
}
|
|
|
|
static int GetBaseTypeLength(const char* pszSrc, int nLength) throw()
|
|
{
|
|
(void)pszSrc;
|
|
// Returns required buffer length in XCHARs
|
|
return nLength;
|
|
}
|
|
|
|
static int GetBaseTypeLength(const wchar_t* pszSrc) throw()
|
|
{
|
|
// Returns required buffer length in XCHARs
|
|
return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, -1, NULL, 0, NULL, NULL)-1;
|
|
}
|
|
|
|
static int GetBaseTypeLength(const wchar_t* pszSrc, int nLength) throw()
|
|
{
|
|
// Returns required buffer length in XCHARs
|
|
return ::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nLength, NULL, 0, NULL, NULL);
|
|
}
|
|
|
|
static void ConvertToBaseType(_CharType* pszDest, int nDestLength,
|
|
const char* pszSrc, int nSrcLength = -1) throw()
|
|
{
|
|
(void)nSrcLength;
|
|
// nLen is in chars
|
|
memcpy(pszDest, pszSrc, nDestLength);
|
|
}
|
|
|
|
static void ConvertToBaseType(_CharType* pszDest, int nDestLength,
|
|
const wchar_t* pszSrc, int nSrcLength = -1) throw()
|
|
{
|
|
// nLen is in XCHARs
|
|
::WideCharToMultiByte(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength, NULL, NULL);
|
|
}
|
|
|
|
static void ConvertToOem(_CharType* pstrString) throw()
|
|
{
|
|
::AnsiToOem(pstrString, pstrString);
|
|
}
|
|
|
|
static void ConvertToAnsi(_CharType* pstrString) throw()
|
|
{
|
|
::OemToAnsi(pstrString, pstrString);
|
|
}
|
|
|
|
static void FloodCharacters(_CharType ch, int nLength, _CharType* pstr) throw()
|
|
{
|
|
// nLength is in XCHARs
|
|
memset(pstr, ch, nLength);
|
|
}
|
|
|
|
static BSTR AllocSysString(const _CharType* pchData, int nDataLength) throw()
|
|
{
|
|
int nLen = MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData, nDataLength,
|
|
NULL, NULL);
|
|
BSTR bstr = ::SysAllocStringLen(NULL, nLen);
|
|
if (bstr != NULL)
|
|
{
|
|
MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData, nDataLength,
|
|
bstr, nLen);
|
|
}
|
|
|
|
return bstr;
|
|
}
|
|
|
|
static BOOL ReAllocSysString(const _CharType* pchData, BSTR* pbstr,
|
|
int nDataLength) throw()
|
|
{
|
|
int nLen = MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData,
|
|
nDataLength, NULL, NULL);
|
|
BOOL bSuccess =::SysReAllocStringLen(pbstr, NULL, nLen);
|
|
if (bSuccess)
|
|
{
|
|
MultiByteToWideChar(_AtlGetConversionACP(), 0, pchData, nDataLength,
|
|
*pbstr, nLen);
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
static DWORD FormatMessage(DWORD dwFlags, LPCVOID lpSource,
|
|
DWORD dwMessageID, DWORD dwLanguageID, char* pstrBuffer,
|
|
DWORD nSize, va_list* pArguments) throw()
|
|
{
|
|
return ::FormatMessageA(dwFlags, lpSource, dwMessageID, dwLanguageID,
|
|
pstrBuffer, nSize, pArguments);
|
|
}
|
|
|
|
static int SafeStringLen(const char* psz) throw()
|
|
{
|
|
// returns length in bytes
|
|
return (psz != NULL) ? lstrlenA(psz) : 0;
|
|
}
|
|
|
|
static int SafeStringLen(const wchar_t* psz) throw()
|
|
{
|
|
// returns length in wchar_ts
|
|
return (psz != NULL) ? lstrlenW(psz) : 0;
|
|
}
|
|
|
|
static int GetCharLen(const wchar_t*) throw()
|
|
{
|
|
// returns char length
|
|
return 1;
|
|
}
|
|
static int GetCharLen(const char* psz) throw()
|
|
{
|
|
const char* p = ::CharNextA(psz);
|
|
return (p - psz);
|
|
}
|
|
|
|
static DWORD GetEnvironmentVariable(const _CharType* pstrVar,
|
|
_CharType* pstrBuffer, DWORD dwSize) throw()
|
|
{
|
|
return ::GetEnvironmentVariableA(pstrVar, pstrBuffer, dwSize);
|
|
}
|
|
};
|
|
|
|
// specialization for wchar_t
|
|
template<>
|
|
class ChTraitsOS< wchar_t > :
|
|
public ChTraitsBase< wchar_t >
|
|
{
|
|
protected:
|
|
#if defined(_UNICODE) && !defined(_CSTRING_ALWAYS_THUNK)
|
|
static int CompareStringW(LCID lcid, DWORD dwFlags,
|
|
LPCWSTR pszString1, int nLength1, LPCWSTR pszString2, int nLength2)
|
|
{
|
|
return ::CompareStringW(lcid, dwFlags, pszString1, nLength1,
|
|
pszString2, nLength2);
|
|
}
|
|
static BOOL GetStringTypeExW(LCID lcid, DWORD dwInfoType, LPCWSTR pszSrc,
|
|
int nLength, LPWORD pwCharType)
|
|
{
|
|
return ::GetStringTypeExW(lcid, dwInfoType, pszSrc, nLength, pwCharType);
|
|
}
|
|
static int lstrcmpiW(LPCWSTR psz1, LPCWSTR psz2)
|
|
{
|
|
return ::lstrcmpiW(psz1, psz2);
|
|
}
|
|
static LPWSTR CharLowerW(LPWSTR psz)
|
|
{
|
|
return ::CharLowerW(psz);
|
|
}
|
|
static LPWSTR CharUpperW(LPWSTR psz)
|
|
{
|
|
return ::CharUpperW(psz);
|
|
}
|
|
static DWORD _GetEnvironmentVariableW(LPCWSTR pszName, LPWSTR pszBuffer, DWORD nSize)
|
|
{
|
|
return ::GetEnvironmentVariableW(pszName, pszBuffer, nSize);
|
|
}
|
|
#else // !_UNICODE
|
|
static int WINAPI CompareStringW(LCID lcid, DWORD dwFlags,
|
|
LPCWSTR pszString1, int nLength1, LPCWSTR pszString2, int nLength2)
|
|
{
|
|
return _strthunks.pfnCompareStringW(lcid, dwFlags, pszString1, nLength1, pszString2, nLength2);
|
|
}
|
|
static BOOL WINAPI GetStringTypeExW(LCID lcid, DWORD dwInfoType, LPCWSTR pszSrc,
|
|
int nLength, LPWORD pwCharType)
|
|
{
|
|
return _strthunks.pfnGetStringTypeExW(lcid, dwInfoType, pszSrc, nLength, pwCharType);
|
|
}
|
|
static int WINAPI lstrcmpiW(LPCWSTR psz1, LPCWSTR psz2)
|
|
{
|
|
return _strthunks.pfnlstrcmpiW(psz1, psz2);
|
|
}
|
|
static LPWSTR WINAPI CharLowerW(LPWSTR psz)
|
|
{
|
|
ATLASSERT(HIWORD(psz) != 0); // No single chars
|
|
return _strthunks.pfnCharLowerW(psz);
|
|
}
|
|
static LPWSTR WINAPI CharUpperW(LPWSTR psz)
|
|
{
|
|
ATLASSERT(HIWORD(psz) != 0); // No single chars
|
|
return _strthunks.pfnCharUpperW(psz);
|
|
}
|
|
static DWORD _GetEnvironmentVariableW(LPCWSTR pszName, LPWSTR pszBuffer, DWORD nSize)
|
|
{
|
|
return _strthunks.pfnGetEnvironmentVariableW(pszName, pszBuffer, nSize);
|
|
}
|
|
#endif // !_UNICODE
|
|
|
|
public:
|
|
static int tclen(const _CharType*) throw()
|
|
{
|
|
return 1;
|
|
}
|
|
static _CharType* strchr(const _CharType* p, _CharType ch) throw()
|
|
{
|
|
//strchr for '\0' should succeed
|
|
while (*p != 0)
|
|
{
|
|
if (*p == ch)
|
|
{
|
|
return const_cast< _CharType* >( p );
|
|
}
|
|
p++;
|
|
}
|
|
return const_cast< _CharType* >((*p == ch) ? p : NULL);
|
|
}
|
|
static _CharType* strrchr(const _CharType* p, _CharType ch) throw()
|
|
{
|
|
const _CharType* pch = p+lstrlenW(p);
|
|
while ((pch != p) && (*pch != ch))
|
|
{
|
|
pch--;
|
|
}
|
|
if (*pch == ch)
|
|
{
|
|
return const_cast<_CharType*>(pch);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
static _CharType* _strrev(_CharType* psz) throw()
|
|
{
|
|
// Optimize NULL, zero-length, and single-char case.
|
|
if ((psz == NULL) || (psz[0] == L'\0') || (psz[1] == L'\0'))
|
|
return psz;
|
|
|
|
_CharType* p = psz+(lstrlenW( psz )-1);
|
|
_CharType* q = psz;
|
|
while(q < p)
|
|
{
|
|
_CharType t = *q;
|
|
*q = *p;
|
|
*p = t;
|
|
q++;
|
|
p--;
|
|
}
|
|
return psz;
|
|
}
|
|
static _CharType* strstr(const _CharType* pStr, const _CharType* pCharSet) throw()
|
|
{
|
|
int nLen = lstrlenW(pCharSet);
|
|
if (nLen == 0)
|
|
return const_cast<_CharType*>(pStr);
|
|
|
|
const _CharType* pMatch;
|
|
const _CharType* pStart = pStr;
|
|
while ((pMatch = strchr(pStart, *pCharSet)) != NULL)
|
|
{
|
|
if (memcmp(pMatch, pCharSet, nLen*sizeof(_CharType)) == 0)
|
|
return const_cast<_CharType*>(pMatch);
|
|
pStart++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
static int strspn(const _CharType* psz, const _CharType* pszCharSet) throw()
|
|
{
|
|
int nRet = 0;
|
|
const _CharType* p = psz;
|
|
while (*p != 0)
|
|
{
|
|
if(strchr(pszCharSet, *p) == NULL)
|
|
break;
|
|
nRet++;
|
|
p++;
|
|
}
|
|
return nRet;
|
|
}
|
|
static int strcspn(const _CharType* psz, const _CharType* pszCharSet) throw()
|
|
{
|
|
int nRet = 0;
|
|
const _CharType* p = psz;
|
|
while (*p != 0)
|
|
{
|
|
if(strchr(pszCharSet, *p) != NULL)
|
|
break;
|
|
nRet++;
|
|
p++;
|
|
}
|
|
return nRet;
|
|
}
|
|
static _CharType* strpbrk(const _CharType* psz, const _CharType* pszCharSet) throw()
|
|
{
|
|
const wchar_t* p = psz;
|
|
while (*p != 0)
|
|
{
|
|
if (strchr(pszCharSet, *p) != NULL)
|
|
return const_cast< wchar_t* >( p );
|
|
p++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static wchar_t* CharNext(const wchar_t* p) throw()
|
|
{
|
|
return const_cast< wchar_t* >( p+1 );
|
|
}
|
|
|
|
static int IsDigit(_CharType ch) throw()
|
|
{
|
|
WORD type;
|
|
GetStringTypeExW(0, CT_CTYPE1, &ch, 1, &type);
|
|
return (type & C1_DIGIT) == C1_DIGIT;
|
|
}
|
|
|
|
static int IsSpace(_CharType ch) throw()
|
|
{
|
|
WORD type;
|
|
GetStringTypeExW(0, CT_CTYPE1, &ch, 1, &type);
|
|
return (type & C1_SPACE) == C1_SPACE;
|
|
}
|
|
|
|
|
|
static int StringCompare(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
return wcscmp(pstrOne, pstrOther);
|
|
}
|
|
|
|
static int StringCompareIgnore(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
return lstrcmpiW(pstrOne, pstrOther);
|
|
}
|
|
|
|
static int StringCollate(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
int nRet;
|
|
|
|
nRet = CompareStringW(GetThreadLocale(), 0, pstrOne, -1, pstrOther, -1);
|
|
ATLASSERT(nRet != 0);
|
|
return nRet-2; // Convert to strcmp convention. This really is documented.
|
|
}
|
|
|
|
static int StringCollateIgnore(const _CharType* pstrOne,
|
|
const _CharType* pstrOther) throw()
|
|
{
|
|
int nRet = CompareStringW(GetThreadLocale(), NORM_IGNORECASE,
|
|
pstrOne, -1, pstrOther, -1);
|
|
ATLASSERT(nRet != 0);
|
|
return nRet-2; // Convert to strcmp convention. This really is documented.
|
|
}
|
|
|
|
static _CharType* StringFindString(const _CharType* pstrBlock,
|
|
const _CharType* pstrMatch) throw()
|
|
{
|
|
return strstr(pstrBlock, pstrMatch);
|
|
}
|
|
|
|
static _CharType* StringFindChar(const _CharType* pstrBlock,
|
|
_CharType pstrMatch) throw()
|
|
{
|
|
return strchr(pstrBlock, pstrMatch);
|
|
}
|
|
|
|
static _CharType* StringFindCharRev(const _CharType* pstr, _CharType ch) throw()
|
|
{
|
|
return strrchr(pstr, ch);
|
|
}
|
|
|
|
static _CharType* StringScanSet(const _CharType* pszBlock,
|
|
const _CharType* pszMatch) throw()
|
|
{
|
|
return strpbrk(pszBlock, pszMatch);
|
|
}
|
|
|
|
static int StringSpanIncluding(const _CharType* pszBlock,
|
|
const _CharType* pszSet) throw()
|
|
{
|
|
return strspn(pszBlock, pszSet);
|
|
}
|
|
|
|
static int StringSpanExcluding(const _CharType* pszBlock,
|
|
const _CharType* pszSet) throw()
|
|
{
|
|
return strcspn(pszBlock, pszSet);
|
|
}
|
|
|
|
static _CharType* StringUppercase(_CharType* psz) throw()
|
|
{
|
|
CharUpperW(psz);
|
|
return psz;
|
|
}
|
|
|
|
static _CharType* StringLowercase(_CharType* psz) throw()
|
|
{
|
|
CharLowerW(psz);
|
|
return psz;
|
|
}
|
|
|
|
static _CharType* StringReverse(_CharType* psz) throw()
|
|
{
|
|
return _strrev(psz);
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
static int GetFormattedLength(const _CharType* pszFormat, va_list args) throw()
|
|
{
|
|
_CharType szBuffer[1028];
|
|
|
|
// wvsprintf always truncates the output to 1024 character plus
|
|
// the '\0'.
|
|
int nLength = wvsprintfW(szBuffer, pszFormat, args);
|
|
ATLASSERT(nLength >= 0);
|
|
ATLASSERT(nLength <= 1024);
|
|
|
|
return nLength;
|
|
}
|
|
|
|
static int Format(_CharType* pszBuffer, const _CharType* pszFormat,
|
|
va_list args) throw()
|
|
{
|
|
return wvsprintfW(pszBuffer, pszFormat, args);
|
|
}
|
|
#endif
|
|
|
|
static int GetBaseTypeLength(const char* pszSrc) throw()
|
|
{
|
|
// Returns required buffer size in wchar_ts
|
|
return ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSrc, -1, NULL, 0)-1;
|
|
}
|
|
|
|
static int GetBaseTypeLength(const char* pszSrc, int nLength) throw()
|
|
{
|
|
// Returns required buffer size in wchar_ts
|
|
return ::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSrc, nLength, NULL, 0);
|
|
}
|
|
|
|
static int GetBaseTypeLength(const wchar_t* pszSrc) throw()
|
|
{
|
|
// Returns required buffer size in wchar_ts
|
|
return lstrlenW(pszSrc);
|
|
}
|
|
|
|
static int GetBaseTypeLength(const wchar_t* pszSrc, int nLength) throw()
|
|
{
|
|
(void)pszSrc;
|
|
// Returns required buffer size in wchar_ts
|
|
return nLength;
|
|
}
|
|
|
|
static void ConvertToBaseType(_CharType* pszDest, int nDestLength,
|
|
const char* pszSrc, int nSrcLength = -1) throw()
|
|
{
|
|
// nLen is in wchar_ts
|
|
::MultiByteToWideChar(_AtlGetConversionACP(), 0, pszSrc, nSrcLength, pszDest, nDestLength);
|
|
}
|
|
|
|
static void ConvertToBaseType(_CharType* pszDest, int nDestLength,
|
|
const wchar_t* pszSrc, int nSrcLength = -1) throw()
|
|
{
|
|
(void)nSrcLength;
|
|
// nLen is in wchar_ts
|
|
memcpy(pszDest, pszSrc, nDestLength*sizeof(wchar_t));
|
|
}
|
|
|
|
// this conversion on Unicode strings makes no sense
|
|
/*
|
|
static void ConvertToOem(_CharType*)
|
|
{
|
|
ATLASSERT(FALSE);
|
|
}
|
|
*/
|
|
|
|
// this conversion on Unicode strings makes no sense
|
|
/*
|
|
static void ConvertToAnsi(_CharType*)
|
|
{
|
|
ATLASSERT(FALSE);
|
|
}
|
|
*/
|
|
|
|
static void FloodCharacters(_CharType ch, int nLength, _CharType* pstr) throw()
|
|
{
|
|
// nLength is in XCHARs
|
|
for (int i = 0; i < nLength; i++)
|
|
pstr[i] = ch;
|
|
}
|
|
|
|
static BSTR AllocSysString(const _CharType* pchData, int nDataLength) throw()
|
|
{
|
|
BSTR bstr = ::SysAllocStringLen(pchData, nDataLength);
|
|
return bstr;
|
|
}
|
|
|
|
static BOOL ReAllocSysString(const _CharType* pchData, BSTR* pbstr,
|
|
int nDataLength) throw()
|
|
{
|
|
return ::SysReAllocStringLen(pbstr, pchData, nDataLength);
|
|
}
|
|
|
|
#ifdef _UNICODE
|
|
static DWORD FormatMessage(DWORD dwFlags, LPCVOID lpSource,
|
|
DWORD dwMessageID, DWORD dwLanguageID, wchar_t* pstrBuffer,
|
|
DWORD nSize, va_list* pArguments) throw()
|
|
{
|
|
return ::FormatMessageW(dwFlags, lpSource, dwMessageID, dwLanguageID,
|
|
pstrBuffer, nSize, pArguments);
|
|
}
|
|
#endif
|
|
static int SafeStringLen(const char* psz) throw()
|
|
{
|
|
// returns length in bytes
|
|
return (psz != NULL) ? lstrlenA(psz) : 0;
|
|
}
|
|
|
|
static int SafeStringLen(const wchar_t* psz) throw()
|
|
{
|
|
// returns length in wchar_ts
|
|
return (psz != NULL) ? lstrlenW(psz) : 0;
|
|
}
|
|
|
|
static int GetCharLen(const wchar_t*) throw()
|
|
{
|
|
// returns char length
|
|
return 1;
|
|
}
|
|
static int GetCharLen(const char* psz) throw()
|
|
{
|
|
LPCSTR p = ::CharNextA( psz );
|
|
return int( p-psz );
|
|
}
|
|
|
|
static DWORD GetEnvironmentVariable(const _CharType* pstrVar,
|
|
_CharType* pstrBuffer, DWORD dwSize) throw()
|
|
{
|
|
return ::GetEnvironmentVariableW(pstrVar, pstrBuffer, dwSize);
|
|
}
|
|
};
|
|
|
|
|
|
template< typename BaseType, class StringTraits >
|
|
class CStringT :
|
|
public CSimpleStringT< BaseType >
|
|
{
|
|
public:
|
|
typedef CSimpleStringT< BaseType > CThisSimpleString;
|
|
typedef StringTraits StrTraits;
|
|
|
|
public:
|
|
CStringT() throw() :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
}
|
|
explicit CStringT( IAtlStringMgr* pStringMgr ) throw() :
|
|
CThisSimpleString( pStringMgr )
|
|
{
|
|
}
|
|
|
|
CStringT( const VARIANT& varSrc ) :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
CComVariant varResult;
|
|
HRESULT hr = ::VariantChangeType( &varResult, const_cast< VARIANT* >( &varSrc ), 0, VT_BSTR );
|
|
if( FAILED( hr ) )
|
|
{
|
|
AtlThrow( hr );
|
|
}
|
|
|
|
*this = V_BSTR( &varResult );
|
|
}
|
|
|
|
CStringT( const VARIANT& varSrc, IAtlStringMgr* pStringMgr ) :
|
|
CThisSimpleString( pStringMgr )
|
|
{
|
|
CComVariant varResult;
|
|
HRESULT hr = ::VariantChangeType( &varResult, const_cast< VARIANT* >( &varSrc ), 0, VT_BSTR );
|
|
if( FAILED( hr ) )
|
|
{
|
|
AtlThrow( hr );
|
|
}
|
|
|
|
*this = V_BSTR( &varResult );
|
|
}
|
|
|
|
static void Construct( CStringT* pString )
|
|
{
|
|
new( pString ) CStringT;
|
|
}
|
|
|
|
// Copy constructor
|
|
CStringT( const CStringT& strSrc ) :
|
|
CThisSimpleString( strSrc )
|
|
{
|
|
}
|
|
|
|
// Construct from CSimpleStringT
|
|
CStringT( const CThisSimpleString& strSrc ) :
|
|
CThisSimpleString( strSrc )
|
|
{
|
|
}
|
|
|
|
CStringT( const XCHAR* pszSrc ) :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
if( !CheckImplicitLoad( pszSrc ) )
|
|
{
|
|
// nDestLength is in XCHARs
|
|
*this = pszSrc;
|
|
}
|
|
}
|
|
|
|
CStringT( LPCSTR pszSrc, IAtlStringMgr* pStringMgr ) :
|
|
CThisSimpleString( pStringMgr )
|
|
{
|
|
if( !CheckImplicitLoad( pszSrc ) )
|
|
{
|
|
// nDestLength is in XCHARs
|
|
*this = pszSrc;
|
|
}
|
|
}
|
|
|
|
CSTRING_EXPLICIT CStringT( const YCHAR* pszSrc ) :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
if( !CheckImplicitLoad( pszSrc ) )
|
|
{
|
|
*this = pszSrc;
|
|
}
|
|
}
|
|
|
|
CStringT( LPCWSTR pszSrc, IAtlStringMgr* pStringMgr ) :
|
|
CThisSimpleString( pStringMgr )
|
|
{
|
|
if( !CheckImplicitLoad( pszSrc ) )
|
|
{
|
|
*this = pszSrc;
|
|
}
|
|
}
|
|
|
|
#ifdef _MANAGED
|
|
CStringT( System::String* pString ) :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
const wchar_t __pin* psz = PtrToStringChars( pString );
|
|
*this = psz;
|
|
}
|
|
#endif
|
|
|
|
CSTRING_EXPLICIT CStringT( const unsigned char* pszSrc ) :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
*this = reinterpret_cast< const char* >( pszSrc );
|
|
}
|
|
|
|
CStringT( const unsigned char* pszSrc, IAtlStringMgr* pStringMgr ) :
|
|
CThisSimpleString( pStringMgr )
|
|
{
|
|
*this = reinterpret_cast< const char* >( pszSrc );
|
|
}
|
|
|
|
CSTRING_EXPLICIT CStringT( wchar_t ch, int nLength = 1 ) :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
ATLASSERT( nLength >= 0 );
|
|
if( nLength > 0 )
|
|
{
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::FloodCharacters( XCHAR( ch ), nLength, pszBuffer );
|
|
ReleaseBuffer( nLength );
|
|
}
|
|
}
|
|
|
|
CStringT( const XCHAR* pch, int nLength ) :
|
|
CThisSimpleString( pch, nLength, StringTraits::GetDefaultManager() )
|
|
{
|
|
}
|
|
|
|
CStringT( const XCHAR* pch, int nLength, IAtlStringMgr* pStringMgr ) :
|
|
CThisSimpleString( pch, nLength, pStringMgr )
|
|
{
|
|
}
|
|
|
|
CStringT( const YCHAR* pch, int nLength ) :
|
|
CThisSimpleString( StringTraits::GetDefaultManager() )
|
|
{
|
|
ATLASSERT( nLength >= 0 );
|
|
if( nLength > 0 )
|
|
{
|
|
ATLASSERT( AtlIsValidAddress( pch, nLength*sizeof( YCHAR ), FALSE ) );
|
|
int nDestLength = StringTraits::GetBaseTypeLength( pch, nLength );
|
|
PXSTR pszBuffer = GetBuffer( nDestLength );
|
|
StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pch, nLength );
|
|
ReleaseBuffer( nDestLength );
|
|
}
|
|
}
|
|
|
|
CStringT( const YCHAR* pch, int nLength, IAtlStringMgr* pStringMgr ) :
|
|
CThisSimpleString( pStringMgr )
|
|
{
|
|
ATLASSERT( nLength >= 0 );
|
|
if( nLength > 0 )
|
|
{
|
|
ATLASSERT( AtlIsValidAddress( pch, nLength*sizeof( YCHAR ), FALSE ) );
|
|
int nDestLength = StringTraits::GetBaseTypeLength( pch, nLength );
|
|
PXSTR pszBuffer = GetBuffer( nDestLength );
|
|
StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pch, nLength );
|
|
ReleaseBuffer( nDestLength );
|
|
}
|
|
}
|
|
|
|
// Destructor
|
|
~CStringT() throw()
|
|
{
|
|
}
|
|
|
|
// Assignment operators
|
|
CStringT& operator=( const CStringT& strSrc )
|
|
{
|
|
CThisSimpleString::operator=( strSrc );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator=( const CThisSimpleString& strSrc )
|
|
{
|
|
CThisSimpleString::operator=( strSrc );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator=( PCXSTR pszSrc )
|
|
{
|
|
CThisSimpleString::operator=( pszSrc );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator=( PCYSTR pszSrc )
|
|
{
|
|
// nDestLength is in XCHARs
|
|
int nDestLength = (pszSrc != NULL) ? StringTraits::GetBaseTypeLength( pszSrc ) : 0;
|
|
if( nDestLength > 0 )
|
|
{
|
|
PXSTR pszBuffer = GetBuffer( nDestLength );
|
|
StringTraits::ConvertToBaseType( pszBuffer, nDestLength, pszSrc );
|
|
ReleaseBuffer( nDestLength );
|
|
}
|
|
else
|
|
{
|
|
Empty();
|
|
}
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator=( const unsigned char* pszSrc )
|
|
{
|
|
return( operator=( reinterpret_cast< const char* >( pszSrc ) ) );
|
|
}
|
|
|
|
CStringT& operator=( char ch )
|
|
{
|
|
char ach[2] = { ch, 0 };
|
|
|
|
return( operator=( ach ) );
|
|
}
|
|
|
|
CStringT& operator=( wchar_t ch )
|
|
{
|
|
wchar_t ach[2] = { ch, 0 };
|
|
|
|
return( operator=( ach ) );
|
|
}
|
|
|
|
CStringT& operator=( const VARIANT& var )
|
|
{
|
|
CComVariant varResult;
|
|
HRESULT hr = ::VariantChangeType( &varResult, const_cast< VARIANT* >( &var ), 0, VT_BSTR );
|
|
if( FAILED( hr ) )
|
|
{
|
|
AtlThrow( hr );
|
|
}
|
|
|
|
*this = V_BSTR( &varResult );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator+=( const CThisSimpleString& str )
|
|
{
|
|
CThisSimpleString::operator+=( str );
|
|
|
|
return( *this );
|
|
}
|
|
CStringT& operator+=( PCXSTR pszSrc )
|
|
{
|
|
CThisSimpleString::operator+=( pszSrc );
|
|
|
|
return( *this );
|
|
}
|
|
template< int t_nSize >
|
|
CStringT& operator+=( const CStaticString< XCHAR, t_nSize >& strSrc )
|
|
{
|
|
CThisSimpleString::operator+=( strSrc );
|
|
|
|
return( *this );
|
|
}
|
|
CStringT& operator+=( PCYSTR psz )
|
|
{
|
|
CStringT str( psz, GetManager() );
|
|
|
|
return( operator+=( str ) );
|
|
}
|
|
|
|
CStringT& operator+=( char ch )
|
|
{
|
|
CThisSimpleString::operator+=( ch );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator+=( unsigned char ch )
|
|
{
|
|
CThisSimpleString::operator+=( ch );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator+=( wchar_t ch )
|
|
{
|
|
CThisSimpleString::operator+=( ch );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
CStringT& operator+=( const VARIANT& var )
|
|
{
|
|
CComVariant varResult;
|
|
HRESULT hr = ::VariantChangeType( &varResult, const_cast< VARIANT* >( &var ), 0, VT_BSTR );
|
|
if( FAILED( hr ) )
|
|
{
|
|
AtlThrow( hr );
|
|
}
|
|
|
|
*this += V_BSTR( &varResult );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Comparison
|
|
|
|
int Compare( PCXSTR psz ) const throw()
|
|
{
|
|
ATLASSERT( AtlIsValidString( psz ) );
|
|
return( StringTraits::StringCompare( GetString(), psz ) );
|
|
}
|
|
|
|
int CompareNoCase( PCXSTR psz ) const throw()
|
|
{
|
|
ATLASSERT( AtlIsValidString( psz ) );
|
|
return( StringTraits::StringCompareIgnore( GetString(), psz ) );
|
|
}
|
|
|
|
int Collate( PCXSTR psz ) const throw()
|
|
{
|
|
ATLASSERT( AtlIsValidString( psz ) );
|
|
return( StringTraits::StringCollate( GetString(), psz ) );
|
|
}
|
|
|
|
int CollateNoCase( PCXSTR psz ) const throw()
|
|
{
|
|
ATLASSERT( AtlIsValidString( psz ) );
|
|
return( StringTraits::StringCollateIgnore( GetString(), psz ) );
|
|
}
|
|
|
|
// Advanced manipulation
|
|
|
|
// Delete 'nCount' characters, starting at index 'iIndex'
|
|
int Delete( int iIndex, int nCount = 1 )
|
|
{
|
|
ATLASSERT( iIndex >= 0 );
|
|
ATLASSERT( nCount >= 0 );
|
|
int nLength = GetLength();
|
|
if( (nCount+iIndex) > nLength )
|
|
{
|
|
nCount = nLength-iIndex;
|
|
}
|
|
if( nCount > 0 )
|
|
{
|
|
int nNewLength = nLength-nCount;
|
|
int nXCHARsToCopy = nLength-(iIndex+nCount)+1;
|
|
PXSTR pszBuffer = GetBuffer();
|
|
memmove( pszBuffer+iIndex, pszBuffer+iIndex+nCount, nXCHARsToCopy*sizeof( XCHAR ) );
|
|
ReleaseBuffer( nNewLength );
|
|
}
|
|
|
|
return( GetLength() );
|
|
}
|
|
|
|
// Insert character 'ch' before index 'iIndex'
|
|
int Insert( int iIndex, XCHAR ch )
|
|
{
|
|
ATLASSERT( iIndex >= 0 );
|
|
if( iIndex > GetLength() )
|
|
{
|
|
iIndex = GetLength();
|
|
}
|
|
int nNewLength = GetLength()+1;
|
|
|
|
PXSTR pszBuffer = GetBuffer( nNewLength );
|
|
|
|
// move existing bytes down
|
|
memmove( pszBuffer+iIndex+1, pszBuffer+iIndex, (nNewLength-iIndex)*sizeof( XCHAR ) );
|
|
pszBuffer[iIndex] = ch;
|
|
|
|
ReleaseBuffer( nNewLength );
|
|
|
|
return( nNewLength );
|
|
}
|
|
|
|
// Insert string 'psz' before index 'iIndex'
|
|
int Insert( int iIndex, PCXSTR psz )
|
|
{
|
|
ATLASSERT( iIndex >= 0 );
|
|
if( iIndex > GetLength() )
|
|
{
|
|
iIndex = GetLength();
|
|
}
|
|
|
|
// nInsertLength and nNewLength are in XCHARs
|
|
int nInsertLength = StringTraits::SafeStringLen( psz );
|
|
int nNewLength = GetLength();
|
|
if( nInsertLength > 0 )
|
|
{
|
|
nNewLength += nInsertLength;
|
|
|
|
PXSTR pszBuffer = GetBuffer( nNewLength );
|
|
// move existing bytes down
|
|
memmove( pszBuffer+iIndex+nInsertLength,
|
|
pszBuffer+iIndex, (nNewLength-iIndex-nInsertLength+1)*sizeof( XCHAR ) );
|
|
memcpy( pszBuffer+iIndex, psz, nInsertLength*sizeof( XCHAR ) );
|
|
ReleaseBuffer( nNewLength );
|
|
}
|
|
|
|
return( nNewLength );
|
|
}
|
|
|
|
// Replace all occurrences of character 'chOld' with character 'chNew'
|
|
int Replace( XCHAR chOld, 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 >( GetString() ); // We don't actually write to pszBuffer until we've called GetBuffer().
|
|
|
|
int nLength = GetLength();
|
|
int iChar = 0;
|
|
while( iChar < nLength )
|
|
{
|
|
// replace instances of the specified character only
|
|
if( pszBuffer[iChar] == chOld )
|
|
{
|
|
if( !bCopied )
|
|
{
|
|
bCopied = true;
|
|
pszBuffer = GetBuffer( nLength );
|
|
}
|
|
pszBuffer[iChar] = chNew;
|
|
nCount++;
|
|
}
|
|
iChar = int( StringTraits::CharNext( pszBuffer+iChar )-pszBuffer );
|
|
}
|
|
if( bCopied )
|
|
{
|
|
ReleaseBuffer( nLength );
|
|
}
|
|
}
|
|
|
|
return( nCount );
|
|
}
|
|
|
|
// Replace all occurrences of string 'pszOld' with string 'pszNew'
|
|
int Replace( PCXSTR pszOld, PCXSTR pszNew )
|
|
{
|
|
// can't have empty or NULL lpszOld
|
|
|
|
// nSourceLen is in XCHARs
|
|
int nSourceLen = StringTraits::SafeStringLen( pszOld );
|
|
if( nSourceLen == 0 )
|
|
return( 0 );
|
|
// nReplacementLen is in XCHARs
|
|
int nReplacementLen = StringTraits::SafeStringLen( pszNew );
|
|
|
|
// loop once to figure out the size of the result string
|
|
int nCount = 0;
|
|
{
|
|
PCXSTR pszStart = GetString();
|
|
PCXSTR pszEnd = pszStart+GetLength();
|
|
while( pszStart < pszEnd )
|
|
{
|
|
PCXSTR pszTarget;
|
|
while( (pszTarget = StringTraits::StringFindString( pszStart, pszOld ) ) != NULL)
|
|
{
|
|
nCount++;
|
|
pszStart = pszTarget+nSourceLen;
|
|
}
|
|
pszStart += StringTraits::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 = GetLength();
|
|
int nNewLength = nOldLength+(nReplacementLen-nSourceLen)*nCount;
|
|
|
|
PXSTR pszBuffer = 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 = StringTraits::StringFindString( pszStart, pszOld ) ) != NULL )
|
|
{
|
|
int nBalance = nOldLength-int(pszTarget-pszBuffer+nSourceLen);
|
|
memmove( pszTarget+nReplacementLen, pszTarget+nSourceLen, nBalance*sizeof( XCHAR ) );
|
|
|
|
memcpy( pszTarget, pszNew, nReplacementLen*sizeof( XCHAR ) );
|
|
pszStart = pszTarget+nReplacementLen;
|
|
pszTarget[nReplacementLen+nBalance] = 0;
|
|
nOldLength += (nReplacementLen-nSourceLen);
|
|
}
|
|
pszStart += StringTraits::SafeStringLen( pszStart )+1;
|
|
}
|
|
ATLASSERT( pszBuffer[nNewLength] == 0 );
|
|
ReleaseBuffer( nNewLength );
|
|
}
|
|
|
|
return( nCount );
|
|
}
|
|
|
|
// Remove all occurrences of character 'chRemove'
|
|
int Remove( XCHAR chRemove )
|
|
{
|
|
int nLength = GetLength();
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
|
|
PXSTR pszSource = pszBuffer;
|
|
PXSTR pszDest = pszBuffer;
|
|
PXSTR pszEnd = pszBuffer+nLength;
|
|
|
|
while( pszSource < pszEnd )
|
|
{
|
|
if( *pszSource != chRemove )
|
|
{
|
|
*pszDest = *pszSource;
|
|
pszDest = StringTraits::CharNext( pszDest );
|
|
}
|
|
pszSource = StringTraits::CharNext( pszSource );
|
|
}
|
|
*pszDest = 0;
|
|
int nCount = int( pszSource-pszDest );
|
|
ReleaseBuffer( nLength-nCount );
|
|
|
|
return( nCount );
|
|
}
|
|
|
|
CStringT Tokenize( PCXSTR pszTokens, int& iStart ) const
|
|
{
|
|
ATLASSERT( iStart >= 0 );
|
|
|
|
if( pszTokens == NULL )
|
|
{
|
|
return( *this );
|
|
}
|
|
|
|
PCXSTR pszPlace = GetString()+iStart;
|
|
PCXSTR pszEnd = GetString()+GetLength();
|
|
if( pszPlace < pszEnd )
|
|
{
|
|
int nIncluding = StringTraits::StringSpanIncluding( pszPlace,
|
|
pszTokens );
|
|
|
|
if( (pszPlace+nIncluding) < pszEnd )
|
|
{
|
|
pszPlace += nIncluding;
|
|
int nExcluding = StringTraits::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( CStringT( GetManager() ) );
|
|
}
|
|
|
|
// find routines
|
|
|
|
// Find the first occurrence of character 'ch', starting at index 'iStart'
|
|
int Find( XCHAR ch, int iStart = 0 ) const throw()
|
|
{
|
|
// iStart is in XCHARs
|
|
ATLASSERT( iStart >= 0 );
|
|
|
|
// nLength is in XCHARs
|
|
int nLength = GetLength();
|
|
if( iStart >= nLength)
|
|
{
|
|
return( -1 );
|
|
}
|
|
|
|
// find first single character
|
|
PCXSTR psz = StringTraits::StringFindChar( GetString()+iStart, ch );
|
|
|
|
// return -1 if not found and index otherwise
|
|
return( (psz == NULL) ? -1 : int( psz-GetString() ) );
|
|
}
|
|
|
|
// look for a specific sub-string
|
|
|
|
// Find the first occurrence of string 'pszSub', starting at index 'iStart'
|
|
int Find( PCXSTR pszSub, int iStart = 0 ) const throw()
|
|
{
|
|
// iStart is in XCHARs
|
|
ATLASSERT( iStart >= 0 );
|
|
ATLASSERT( AtlIsValidString( pszSub ) );
|
|
|
|
// nLength is in XCHARs
|
|
int nLength = GetLength();
|
|
if( iStart > nLength )
|
|
{
|
|
return( -1 );
|
|
}
|
|
|
|
// find first matching substring
|
|
PCXSTR psz = StringTraits::StringFindString( GetString()+iStart, pszSub );
|
|
|
|
// return -1 for not found, distance from beginning otherwise
|
|
return( (psz == NULL) ? -1 : int( psz-GetString() ) );
|
|
}
|
|
|
|
// Find the first occurrence of any of the characters in string 'pszCharSet'
|
|
int FindOneOf( PCXSTR pszCharSet ) const throw()
|
|
{
|
|
ATLASSERT( AtlIsValidString( pszCharSet ) );
|
|
PCXSTR psz = StringTraits::StringScanSet( GetString(), pszCharSet );
|
|
return( (psz == NULL) ? -1 : int( psz-GetString() ) );
|
|
}
|
|
|
|
// Find the last occurrence of character 'ch'
|
|
int ReverseFind( XCHAR ch ) const throw()
|
|
{
|
|
// find last single character
|
|
PCXSTR psz = StringTraits::StringFindCharRev( GetString(), ch );
|
|
|
|
// return -1 if not found, distance from beginning otherwise
|
|
return( (psz == NULL) ? -1 : int( psz-GetString() ) );
|
|
}
|
|
|
|
// manipulation
|
|
|
|
// Convert the string to uppercase
|
|
CStringT& MakeUpper()
|
|
{
|
|
int nLength = GetLength();
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::StringUppercase( pszBuffer );
|
|
ReleaseBuffer( nLength );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Convert the string to lowercase
|
|
CStringT& MakeLower()
|
|
{
|
|
int nLength = GetLength();
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::StringLowercase( pszBuffer );
|
|
ReleaseBuffer( nLength );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Reverse the string
|
|
CStringT& MakeReverse()
|
|
{
|
|
int nLength = GetLength();
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::StringReverse( pszBuffer );
|
|
ReleaseBuffer( nLength );
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// trimming
|
|
|
|
// Remove all trailing whitespace
|
|
CStringT& TrimRight()
|
|
{
|
|
// find beginning of trailing spaces by starting
|
|
// at beginning (DBCS aware)
|
|
|
|
PCXSTR psz = GetString();
|
|
PCXSTR pszLast = NULL;
|
|
|
|
while( *psz != 0 )
|
|
{
|
|
if( StringTraits::IsSpace( *psz ) )
|
|
{
|
|
if( pszLast == NULL )
|
|
pszLast = psz;
|
|
}
|
|
else
|
|
{
|
|
pszLast = NULL;
|
|
}
|
|
psz = StringTraits::CharNext( psz );
|
|
}
|
|
|
|
if( pszLast != NULL )
|
|
{
|
|
// truncate at trailing space start
|
|
int iLast = int( pszLast-GetString() );
|
|
|
|
Truncate( iLast );
|
|
}
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Remove all leading whitespace
|
|
CStringT& TrimLeft()
|
|
{
|
|
// find first non-space character
|
|
|
|
PCXSTR psz = GetString();
|
|
|
|
while( StringTraits::IsSpace( *psz ) )
|
|
{
|
|
psz = StringTraits::CharNext( psz );
|
|
}
|
|
|
|
if( psz != GetString() )
|
|
{
|
|
// fix up data and length
|
|
int iFirst = int( psz-GetString() );
|
|
PXSTR pszBuffer = GetBuffer( GetLength() );
|
|
psz = pszBuffer+iFirst;
|
|
int nDataLength = GetLength()-iFirst;
|
|
memmove( pszBuffer, psz, (nDataLength+1)*sizeof( XCHAR ) );
|
|
ReleaseBuffer( nDataLength );
|
|
}
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Remove all leading and trailing whitespace
|
|
CStringT& Trim()
|
|
{
|
|
return( TrimRight().TrimLeft() );
|
|
}
|
|
|
|
// Remove all leading and trailing occurrences of character 'chTarget'
|
|
CStringT& Trim( XCHAR chTarget )
|
|
{
|
|
return( TrimRight( chTarget ).TrimLeft( chTarget ) );
|
|
}
|
|
|
|
// Remove all leading and trailing occurrences of any of the characters in the string 'pszTargets'
|
|
CStringT& Trim( PCXSTR pszTargets )
|
|
{
|
|
return( TrimRight( pszTargets ).TrimLeft( pszTargets ) );
|
|
}
|
|
|
|
// trimming anything (either side)
|
|
|
|
// Remove all trailing occurrences of character 'chTarget'
|
|
CStringT& TrimRight( XCHAR chTarget )
|
|
{
|
|
// find beginning of trailing matches
|
|
// by starting at beginning (DBCS aware)
|
|
|
|
PCXSTR psz = GetString();
|
|
PCXSTR pszLast = NULL;
|
|
|
|
while( *psz != 0 )
|
|
{
|
|
if( *psz == chTarget )
|
|
{
|
|
if( pszLast == NULL )
|
|
{
|
|
pszLast = psz;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszLast = NULL;
|
|
}
|
|
psz = StringTraits::CharNext( psz );
|
|
}
|
|
|
|
if( pszLast != NULL )
|
|
{
|
|
// truncate at left-most matching character
|
|
int iLast = int( pszLast-GetString() );
|
|
Truncate( iLast );
|
|
}
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Remove all trailing occurrences of any of the characters in string 'pszTargets'
|
|
CStringT& TrimRight( 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 = GetString();
|
|
PCXSTR pszLast = NULL;
|
|
|
|
while( *psz != 0 )
|
|
{
|
|
if( StringTraits::StringFindChar( pszTargets, *psz ) != NULL )
|
|
{
|
|
if( pszLast == NULL )
|
|
{
|
|
pszLast = psz;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszLast = NULL;
|
|
}
|
|
psz = StringTraits::CharNext( psz );
|
|
}
|
|
|
|
if( pszLast != NULL )
|
|
{
|
|
// truncate at left-most matching character
|
|
int iLast = int( pszLast-GetString() );
|
|
Truncate( iLast );
|
|
}
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Remove all leading occurrences of character 'chTarget'
|
|
CStringT& TrimLeft( XCHAR chTarget )
|
|
{
|
|
// find first non-matching character
|
|
PCXSTR psz = GetString();
|
|
|
|
while( chTarget == *psz )
|
|
{
|
|
psz = StringTraits::CharNext( psz );
|
|
}
|
|
|
|
if( psz != GetString() )
|
|
{
|
|
// fix up data and length
|
|
int iFirst = int( psz-GetString() );
|
|
PXSTR pszBuffer = GetBuffer( GetLength() );
|
|
psz = pszBuffer+iFirst;
|
|
int nDataLength = GetLength()-iFirst;
|
|
memmove( pszBuffer, psz, (nDataLength+1)*sizeof( XCHAR ) );
|
|
ReleaseBuffer( nDataLength );
|
|
}
|
|
|
|
return( *this );
|
|
}
|
|
|
|
// Remove all leading occurrences of any of the characters in string 'pszTargets'
|
|
CStringT& TrimLeft( PCXSTR pszTargets )
|
|
{
|
|
// if we're not trimming anything, we're not doing any work
|
|
if( (pszTargets == NULL) || (*pszTargets == 0) )
|
|
{
|
|
return( *this );
|
|
}
|
|
|
|
PCXSTR psz = GetString();
|
|
while( (*psz != 0) && (StringTraits::StringFindChar( pszTargets, *psz ) != NULL) )
|
|
{
|
|
psz = StringTraits::CharNext( psz );
|
|
}
|
|
|
|
if( psz != GetString() )
|
|
{
|
|
// fix up data and length
|
|
int iFirst = int( psz-GetString() );
|
|
PXSTR pszBuffer = GetBuffer( GetLength() );
|
|
psz = pszBuffer+iFirst;
|
|
int nDataLength = GetLength()-iFirst;
|
|
memmove( pszBuffer, psz, (nDataLength+1)*sizeof( XCHAR ) );
|
|
ReleaseBuffer( nDataLength );
|
|
}
|
|
|
|
return( *this );
|
|
}
|
|
|
|
__if_exists( StringTraits::ConvertToOem )
|
|
{
|
|
// Convert the string to the OEM character set
|
|
void AnsiToOem()
|
|
{
|
|
int nLength = GetLength();
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::ConvertToOem( pszBuffer );
|
|
ReleaseBuffer( nLength );
|
|
}
|
|
}
|
|
|
|
__if_exists( StringTraits::ConvertToAnsi )
|
|
{
|
|
// Convert the string to the ANSI character set
|
|
void OemToAnsi()
|
|
{
|
|
int nLength = GetLength();
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::ConvertToAnsi( pszBuffer );
|
|
ReleaseBuffer( nLength );
|
|
}
|
|
}
|
|
|
|
// Very simple sub-string extraction
|
|
|
|
// Return the substring starting at index 'iFirst'
|
|
CStringT Mid( int iFirst ) const
|
|
{
|
|
return( Mid( iFirst, GetLength()-iFirst ) );
|
|
}
|
|
|
|
// Return the substring starting at index 'iFirst', with length 'nCount'
|
|
CStringT Mid( int iFirst, int nCount ) const
|
|
{
|
|
// nCount is in XCHARs
|
|
|
|
// out-of-bounds requests return sensible things
|
|
ATLASSERT( iFirst >= 0 );
|
|
ATLASSERT( nCount >= 0 );
|
|
|
|
if( (iFirst+nCount) > GetLength() )
|
|
{
|
|
nCount = GetLength()-iFirst;
|
|
}
|
|
if( iFirst > GetLength() )
|
|
{
|
|
nCount = 0;
|
|
}
|
|
|
|
ATLASSERT( (nCount == 0) || ((iFirst+nCount) <= GetLength()) );
|
|
|
|
// optimize case of returning entire string
|
|
if( (iFirst == 0) && ((iFirst+nCount) == GetLength()) )
|
|
{
|
|
return( *this );
|
|
}
|
|
|
|
CStringT strDest( GetManager() ); //GetString()+nFirst, nCount );
|
|
|
|
PXSTR pszBuffer = strDest.GetBufferSetLength( nCount );
|
|
memcpy( pszBuffer, GetString()+iFirst, nCount*sizeof( XCHAR ) );
|
|
strDest.ReleaseBuffer( nCount );
|
|
|
|
return( strDest );
|
|
}
|
|
|
|
// Return the substring consisting of the rightmost 'nCount' characters
|
|
CStringT Right( int nCount ) const
|
|
{
|
|
// nCount is in XCHARs
|
|
ATLASSERT( nCount >= 0 );
|
|
if( nCount >= GetLength() )
|
|
{
|
|
return( *this );
|
|
}
|
|
|
|
CStringT strDest( GetManager() );
|
|
|
|
PXSTR pszBuffer = strDest.GetBufferSetLength( nCount );
|
|
memcpy( pszBuffer, GetString()+GetLength()-nCount, nCount*sizeof( XCHAR ) );
|
|
strDest.ReleaseBuffer( nCount );
|
|
|
|
return( strDest );
|
|
}
|
|
|
|
// Return the substring consisting of the leftmost 'nCount' characters
|
|
CStringT Left( int nCount ) const
|
|
{
|
|
// nCount is in XCHARs
|
|
ATLASSERT( nCount >= 0 );
|
|
if( nCount >= GetLength() )
|
|
{
|
|
return( *this );
|
|
}
|
|
|
|
CStringT strDest( GetManager() );
|
|
|
|
PXSTR pszBuffer = strDest.GetBufferSetLength( nCount );
|
|
memcpy( pszBuffer, GetString(), nCount*sizeof( XCHAR ) );
|
|
strDest.ReleaseBuffer( nCount );
|
|
|
|
return( strDest );
|
|
}
|
|
|
|
// Return the substring consisting of the leftmost characters in the set 'pszCharSet'
|
|
CStringT SpanIncluding( PCXSTR pszCharSet ) const
|
|
{
|
|
ATLASSERT( AtlIsValidString( pszCharSet ) );
|
|
|
|
return( Left( StringTraits::StringSpanIncluding( GetString(), pszCharSet ) ) );
|
|
}
|
|
|
|
// Return the substring consisting of the leftmost characters not in the set 'pszCharSet'
|
|
CStringT SpanExcluding( PCXSTR pszCharSet ) const
|
|
{
|
|
ATLASSERT( AtlIsValidString( pszCharSet ) );
|
|
return( Left( StringTraits::StringSpanExcluding( GetString(), pszCharSet ) ) );
|
|
}
|
|
|
|
// Format data using format string 'pszFormat'
|
|
void __cdecl Format( PCXSTR pszFormat, ... )
|
|
{
|
|
ATLASSERT( AtlIsValidString( pszFormat ) );
|
|
|
|
va_list argList;
|
|
va_start( argList, pszFormat );
|
|
FormatV( pszFormat, argList );
|
|
va_end( argList );
|
|
}
|
|
|
|
// Format data using format string loaded from resource 'nFormatID'
|
|
void __cdecl Format( UINT nFormatID, ... )
|
|
{
|
|
CStringT strFormat( GetManager() );
|
|
ATLVERIFY( 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'
|
|
void __cdecl AppendFormat( UINT nFormatID, ... )
|
|
{
|
|
CStringT strTemp( GetManager() );
|
|
va_list argList;
|
|
va_start( argList, nFormatID );
|
|
|
|
CStringT strFormat( GetManager() );
|
|
ATLVERIFY( strFormat.LoadString( nFormatID ) );
|
|
|
|
strTemp.FormatV( strFormat, argList );
|
|
operator+=( strTemp );
|
|
|
|
va_end( argList );
|
|
}
|
|
|
|
// Append formatted data using format string 'pszFormat'
|
|
void __cdecl AppendFormat( PCXSTR pszFormat, ... )
|
|
{
|
|
ATLASSERT( AtlIsValidString( pszFormat ) );
|
|
|
|
CStringT strTemp( GetManager() );
|
|
va_list argList;
|
|
va_start( argList, pszFormat );
|
|
|
|
strTemp.FormatV( pszFormat, argList );
|
|
operator+=( strTemp );
|
|
|
|
va_end( argList );
|
|
}
|
|
|
|
void FormatV( PCXSTR pszFormat, va_list args )
|
|
{
|
|
ATLASSERT( AtlIsValidString( pszFormat ) );
|
|
|
|
int nLength = StringTraits::GetFormattedLength( pszFormat, args );
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::Format( pszBuffer, pszFormat, args );
|
|
ReleaseBuffer( nLength );
|
|
}
|
|
|
|
__if_exists(StringTraits::FormatMessage)
|
|
{
|
|
// Format a message using format string 'pszFormat'
|
|
void __cdecl FormatMessage( PCXSTR pszFormat, ... )
|
|
{
|
|
va_list argList;
|
|
va_start( argList, pszFormat );
|
|
|
|
FormatMessageV( pszFormat, &argList );
|
|
|
|
va_end( argList );
|
|
}
|
|
|
|
// Format a message using format string loaded from resource 'nFormatID'
|
|
void __cdecl FormatMessage( UINT nFormatID, ... )
|
|
{
|
|
// get format string from string table
|
|
CStringT strFormat( GetManager() );
|
|
ATLVERIFY( strFormat.LoadString( nFormatID ) );
|
|
|
|
va_list argList;
|
|
va_start( argList, nFormatID );
|
|
|
|
FormatMessageV( strFormat, &argList );
|
|
|
|
va_end( argList );
|
|
}
|
|
}
|
|
|
|
// OLE BSTR support
|
|
|
|
// Allocate a BSTR containing a copy of the string
|
|
BSTR AllocSysString() const
|
|
{
|
|
BSTR bstrResult = StringTraits::AllocSysString( GetString(),
|
|
GetLength() );
|
|
if( bstrResult == NULL )
|
|
{
|
|
ThrowMemoryException();
|
|
}
|
|
|
|
return( bstrResult );
|
|
}
|
|
|
|
BSTR SetSysString( BSTR* pbstr ) const
|
|
{
|
|
ATLASSERT( AtlIsValidAddress( pbstr, sizeof( BSTR ) ) );
|
|
|
|
if( !StringTraits::ReAllocSysString( GetString(), pbstr,
|
|
GetLength() ) )
|
|
{
|
|
ThrowMemoryException();
|
|
}
|
|
|
|
ATLASSERT( *pbstr != NULL );
|
|
return( *pbstr );
|
|
}
|
|
|
|
// Set the string to the value of environment variable 'pszVar'
|
|
BOOL GetEnvironmentVariable( PCXSTR pszVar )
|
|
{
|
|
ULONG nLength = StringTraits::GetEnvironmentVariable( pszVar, NULL, 0 );
|
|
BOOL bRetVal = FALSE;
|
|
|
|
if( nLength == 0 )
|
|
{
|
|
Empty();
|
|
}
|
|
else
|
|
{
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::GetEnvironmentVariable( pszVar, pszBuffer, nLength );
|
|
ReleaseBuffer();
|
|
bRetVal = TRUE;
|
|
}
|
|
|
|
return( bRetVal );
|
|
}
|
|
|
|
// Load the string from resource 'nID'
|
|
BOOL LoadString( UINT nID )
|
|
{
|
|
HINSTANCE hInst = StringTraits::FindStringResourceInstance( nID );
|
|
if( hInst == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
return( LoadString( hInst, nID ) );
|
|
}
|
|
|
|
// Load the string from resource 'nID' in module 'hInstance'
|
|
BOOL LoadString( HINSTANCE hInstance, UINT nID )
|
|
{
|
|
const ATLSTRINGRESOURCEIMAGE* pImage = AtlGetStringResourceImage( hInstance, nID );
|
|
if( pImage == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
int nLength = StringTraits::GetBaseTypeLength( pImage->achString, pImage->nLength );
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::ConvertToBaseType( pszBuffer, nLength, pImage->achString, pImage->nLength );
|
|
ReleaseBuffer( nLength );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
// Load the string from resource 'nID' in module 'hInstance', using language 'wLanguageID'
|
|
BOOL LoadString( HINSTANCE hInstance, UINT nID, WORD wLanguageID )
|
|
{
|
|
const ATLSTRINGRESOURCEIMAGE* pImage = AtlGetStringResourceImage( hInstance, nID, wLanguageID );
|
|
if( pImage == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
int nLength = StringTraits::GetBaseTypeLength( pImage->achString, pImage->nLength );
|
|
PXSTR pszBuffer = GetBuffer( nLength );
|
|
StringTraits::ConvertToBaseType( pszBuffer, nLength, pImage->achString, pImage->nLength );
|
|
ReleaseBuffer( nLength );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
friend CStringT operator+( const CStringT& str1, const CStringT& str2 )
|
|
{
|
|
CStringT strResult( str1.GetManager() );
|
|
|
|
Concatenate( strResult, str1, str1.GetLength(), str2, str2.GetLength() );
|
|
|
|
return( strResult );
|
|
}
|
|
|
|
friend CStringT operator+( const CStringT& str1, PCXSTR psz2 )
|
|
{
|
|
CStringT strResult( str1.GetManager() );
|
|
|
|
Concatenate( strResult, str1, str1.GetLength(), psz2, StringLength( psz2 ) );
|
|
|
|
return( strResult );
|
|
}
|
|
|
|
friend CStringT operator+( PCXSTR psz1, const CStringT& str2 )
|
|
{
|
|
CStringT strResult( str2.GetManager() );
|
|
|
|
Concatenate( strResult, psz1, StringLength( psz1 ), str2, str2.GetLength() );
|
|
|
|
return( strResult );
|
|
}
|
|
|
|
friend CStringT operator+( const CStringT& str1, wchar_t ch2 )
|
|
{
|
|
CStringT strResult( str1.GetManager() );
|
|
XCHAR chTemp = XCHAR( ch2 );
|
|
|
|
Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );
|
|
|
|
return( strResult );
|
|
}
|
|
|
|
friend CStringT operator+( const CStringT& str1, char ch2 )
|
|
{
|
|
CStringT strResult( str1.GetManager() );
|
|
XCHAR chTemp = XCHAR( ch2 );
|
|
|
|
Concatenate( strResult, str1, str1.GetLength(), &chTemp, 1 );
|
|
|
|
return( strResult );
|
|
}
|
|
|
|
friend CStringT operator+( wchar_t ch1, const CStringT& str2 )
|
|
{
|
|
CStringT strResult( str2.GetManager() );
|
|
XCHAR chTemp = XCHAR( ch1 );
|
|
|
|
Concatenate( strResult, &chTemp, 1, str2, str2.GetLength() );
|
|
|
|
return( strResult );
|
|
}
|
|
|
|
friend CStringT operator+( char ch1, const CStringT& str2 )
|
|
{
|
|
CStringT strResult( str2.GetManager() );
|
|
XCHAR chTemp = XCHAR( ch1 );
|
|
|
|
Concatenate( strResult, &chTemp, 1, str2, str2.GetLength() );
|
|
|
|
return( strResult );
|
|
}
|
|
|
|
friend bool operator==( const CStringT& str1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str1.Compare( str2 ) == 0 );
|
|
}
|
|
|
|
friend bool operator==(
|
|
const CStringT& str1, PCXSTR psz2 ) throw()
|
|
{
|
|
return( str1.Compare( psz2 ) == 0 );
|
|
}
|
|
|
|
friend bool operator==(
|
|
PCXSTR psz1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str2.Compare( psz1 ) == 0 );
|
|
}
|
|
|
|
friend bool operator==(
|
|
const CStringT& str1, PCYSTR psz2 ) throw( ... )
|
|
{
|
|
CStringT str2( psz2, str1.GetManager() );
|
|
|
|
return( str1 == str2 );
|
|
}
|
|
|
|
friend bool operator==(
|
|
PCYSTR psz1, const CStringT& str2 ) throw( ... )
|
|
{
|
|
CStringT str1( psz1, str2.GetManager() );
|
|
|
|
return( str1 == str2 );
|
|
}
|
|
|
|
friend bool operator!=(
|
|
const CStringT& str1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str1.Compare( str2 ) != 0 );
|
|
}
|
|
|
|
friend bool operator!=(
|
|
const CStringT& str1, PCXSTR psz2 ) throw()
|
|
{
|
|
return( str1.Compare( psz2 ) != 0 );
|
|
}
|
|
|
|
friend bool operator!=(
|
|
PCXSTR psz1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str2.Compare( psz1 ) != 0 );
|
|
}
|
|
|
|
friend bool operator!=(
|
|
const CStringT& str1, PCYSTR psz2 ) throw( ... )
|
|
{
|
|
CStringT str2( psz2, str1.GetManager() );
|
|
|
|
return( str1 != str2 );
|
|
}
|
|
|
|
friend bool operator!=(
|
|
PCYSTR psz1, const CStringT& str2 ) throw( ... )
|
|
{
|
|
CStringT str1( psz1, str2.GetManager() );
|
|
|
|
return( str1 != str2 );
|
|
}
|
|
|
|
friend bool operator<( const CStringT& str1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str1.Compare( str2 ) < 0 );
|
|
}
|
|
|
|
friend bool operator<( const CStringT& str1, PCXSTR psz2 ) throw()
|
|
{
|
|
return( str1.Compare( psz2 ) < 0 );
|
|
}
|
|
|
|
friend bool operator<( PCXSTR psz1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str2.Compare( psz1 ) >= 0 );
|
|
}
|
|
|
|
friend bool operator>( const CStringT& str1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str1.Compare( str2 ) > 0 );
|
|
}
|
|
|
|
friend bool operator>( const CStringT& str1, PCXSTR psz2 ) throw()
|
|
{
|
|
return( str1.Compare( psz2 ) > 0 );
|
|
}
|
|
|
|
friend bool operator>( PCXSTR psz1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str2.Compare( psz1 ) <= 0 );
|
|
}
|
|
|
|
friend bool operator<=( const CStringT& str1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str1.Compare( str2 ) <= 0 );
|
|
}
|
|
|
|
friend bool operator<=( const CStringT& str1, PCXSTR psz2 ) throw()
|
|
{
|
|
return( str1.Compare( psz2 ) <= 0 );
|
|
}
|
|
|
|
friend bool operator<=( PCXSTR psz1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str2.Compare( psz1 ) > 0 );
|
|
}
|
|
|
|
friend bool operator>=( const CStringT& str1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str1.Compare( str2 ) >= 0 );
|
|
}
|
|
|
|
friend bool operator>=( const CStringT& str1, PCXSTR psz2 ) throw()
|
|
{
|
|
return( str1.Compare( psz2 ) >= 0 );
|
|
}
|
|
|
|
friend bool operator>=( PCXSTR psz1, const CStringT& str2 ) throw()
|
|
{
|
|
return( str2.Compare( psz1 ) < 0 );
|
|
}
|
|
|
|
friend bool operator==( XCHAR ch1, const CStringT& str2 ) throw()
|
|
{
|
|
return( (str2.GetLength() == 1) && (str2[0] == ch1) );
|
|
}
|
|
|
|
friend bool operator==( const CStringT& str1, XCHAR ch2 ) throw()
|
|
{
|
|
return( (str1.GetLength() == 1) && (str1[0] == ch2) );
|
|
}
|
|
|
|
friend bool operator!=( XCHAR ch1, const CStringT& str2 ) throw()
|
|
{
|
|
return( (str2.GetLength() != 1) || (str2[0] != ch1) );
|
|
}
|
|
|
|
friend bool operator!=( const CStringT& str1, XCHAR ch2 ) throw()
|
|
{
|
|
return( (str1.GetLength() != 1) || (str1[0] != ch2) );
|
|
}
|
|
|
|
private:
|
|
bool CheckImplicitLoad( const void* pv )
|
|
{
|
|
bool bRet = false;
|
|
|
|
if( (pv != NULL) && IS_INTRESOURCE( pv ) )
|
|
{
|
|
UINT nID = LOWORD( reinterpret_cast< DWORD_PTR >( pv ) );
|
|
if( !LoadString( nID ) )
|
|
{
|
|
ATLTRACE( atlTraceString, 2, _T( "Warning: implicit LoadString(%u) failed\n" ), nID );
|
|
}
|
|
bRet = true;
|
|
}
|
|
|
|
return( bRet );
|
|
}
|
|
|
|
__if_exists( StringTraits::FormatMessage )
|
|
{
|
|
void FormatMessageV( PCXSTR pszFormat, va_list* pArgList )
|
|
{
|
|
// format message into temporary buffer pszTemp
|
|
CHeapPtr< XCHAR, CLocalAllocator > pszTemp;
|
|
DWORD dwResult = StringTraits::FormatMessage( FORMAT_MESSAGE_FROM_STRING|
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER, pszFormat, 0, 0, reinterpret_cast< PXSTR >( &pszTemp ),
|
|
0, pArgList );
|
|
if( dwResult == 0 )
|
|
{
|
|
ThrowMemoryException();
|
|
}
|
|
|
|
*this = pszTemp;
|
|
}
|
|
}
|
|
};
|
|
|
|
class IFixedStringLog
|
|
{
|
|
public:
|
|
virtual void OnAllocateSpill( int nActualChars, int nFixedChars, const CStringData* pData ) throw() = 0;
|
|
virtual void OnReallocateSpill( int nActualChars, int nFixedChars, const CStringData* pData ) throw() = 0;
|
|
};
|
|
|
|
class CFixedStringMgr :
|
|
public IAtlStringMgr
|
|
{
|
|
public:
|
|
CFixedStringMgr( CStringData* pData, int nChars, IAtlStringMgr* pMgr = NULL ) throw() :
|
|
m_pData( pData ),
|
|
m_pMgr( pMgr )
|
|
{
|
|
m_pData->nRefs = -1;
|
|
m_pData->nDataLength = 0;
|
|
m_pData->nAllocLength = nChars;
|
|
m_pData->pStringMgr = this;
|
|
*static_cast< wchar_t* >( m_pData->data() ) = 0;
|
|
}
|
|
~CFixedStringMgr() throw()
|
|
{
|
|
}
|
|
|
|
// IAtlStringMgr
|
|
public:
|
|
virtual CStringData* Allocate( int nChars, int nCharSize ) throw()
|
|
{
|
|
ATLASSERT( m_pData->nRefs == -1 );
|
|
ATLASSERT( m_pData->nDataLength == 0 );
|
|
if( nChars > m_pData->nAllocLength )
|
|
{
|
|
if( s_pLog != NULL )
|
|
{
|
|
s_pLog->OnAllocateSpill( nChars, m_pData->nAllocLength, m_pData );
|
|
}
|
|
CStringData* pData = m_pMgr->Allocate( nChars, nCharSize );
|
|
if( pData != NULL )
|
|
{
|
|
pData->pStringMgr = this;
|
|
pData->nRefs = -1; // Locked
|
|
}
|
|
|
|
return pData;
|
|
}
|
|
|
|
m_pData->nRefs = -1; // Locked
|
|
m_pData->nDataLength = 0;
|
|
m_pData->pStringMgr = this;
|
|
|
|
return m_pData;
|
|
}
|
|
virtual void Free( CStringData* pData ) throw()
|
|
{
|
|
ATLASSERT( pData->nRefs <= 0 );
|
|
if( pData != m_pData )
|
|
{
|
|
// Must have been allocated from the backup manager
|
|
pData->pStringMgr = m_pMgr;
|
|
m_pMgr->Free( pData );
|
|
}
|
|
|
|
// Always make sure the fixed buffer is ready to be used as the nil string.
|
|
m_pData->nRefs = -1;
|
|
m_pData->nDataLength = 0;
|
|
*static_cast< wchar_t* >( m_pData->data() ) = 0;
|
|
}
|
|
virtual CStringData* Reallocate( CStringData* pData, int nChars, int nCharSize ) throw()
|
|
{
|
|
CStringData* pNewData;
|
|
|
|
ATLASSERT( pData->nRefs < 0 );
|
|
if( pData != m_pData )
|
|
{
|
|
pData->pStringMgr = m_pMgr;
|
|
pNewData = m_pMgr->Reallocate( pData, nChars, nCharSize );
|
|
if( pNewData == NULL )
|
|
{
|
|
pData->pStringMgr = this;
|
|
}
|
|
else
|
|
{
|
|
pNewData->pStringMgr = this;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( nChars > pData->nAllocLength )
|
|
{
|
|
if( s_pLog != NULL )
|
|
{
|
|
s_pLog->OnReallocateSpill( nChars, pData->nAllocLength, pData );
|
|
}
|
|
pNewData = m_pMgr->Allocate( nChars, nCharSize );
|
|
if( pNewData == NULL )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Copy the string data
|
|
memcpy( pNewData->data(), pData->data(), (pData->nAllocLength+1)*nCharSize );
|
|
pNewData->nRefs = pData->nRefs; // Locked
|
|
pNewData->pStringMgr = this;
|
|
pNewData->nDataLength = pData->nDataLength;
|
|
}
|
|
else
|
|
{
|
|
// Don't do anything if the buffer is already big enough.
|
|
pNewData = pData;
|
|
}
|
|
}
|
|
|
|
return pNewData;
|
|
}
|
|
virtual CStringData* GetNilString() throw()
|
|
{
|
|
ATLASSERT( m_pData->nRefs == -1 );
|
|
ATLASSERT( m_pData->nDataLength == 0 );
|
|
|
|
return m_pData;
|
|
}
|
|
virtual IAtlStringMgr* Clone() throw()
|
|
{
|
|
return m_pMgr;
|
|
}
|
|
|
|
public:
|
|
static IFixedStringLog* s_pLog;
|
|
|
|
IAtlStringMgr* GetBackupManager() const throw()
|
|
{
|
|
return m_pMgr;
|
|
}
|
|
|
|
protected:
|
|
IAtlStringMgr* m_pMgr;
|
|
CStringData* m_pData;
|
|
};
|
|
|
|
__declspec( selectany ) IFixedStringLog* CFixedStringMgr::s_pLog = NULL;
|
|
|
|
#pragma warning( push )
|
|
#pragma warning( disable: 4355 ) // 'this' used in base member initializer list
|
|
|
|
template< class StringType, int t_nChars >
|
|
class CFixedStringT :
|
|
private CFixedStringMgr, // This class must be first, since it needs to be initialized before StringType
|
|
public StringType
|
|
{
|
|
public:
|
|
CFixedStringT() throw() :
|
|
CFixedStringMgr( &m_data, t_nChars, StrTraits::GetDefaultManager() ),
|
|
StringType( static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
explicit CFixedStringT( IAtlStringMgr* pStringMgr ) throw() :
|
|
CFixedStringMgr( &m_data, t_nChars, pStringMgr ),
|
|
StringType( static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
CFixedStringT( const CFixedStringT< StringType, t_nChars >& str ) :
|
|
CFixedStringMgr( &m_data, t_nChars, StrTraits::GetDefaultManager() ),
|
|
StringType( str.GetString(), str.GetLength(), static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
CFixedStringT( const StringType& str ) :
|
|
CFixedStringMgr( &m_data, t_nChars, StrTraits::GetDefaultManager() ),
|
|
StringType( str.GetString(), str.GetLength(), static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
CFixedStringT( const StringType::XCHAR* psz ) :
|
|
CFixedStringMgr( &m_data, t_nChars, StrTraits::GetDefaultManager() ),
|
|
StringType( psz, static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
CFixedStringT( const StringType::XCHAR* psz, int nLength ) :
|
|
CFixedStringMgr( &m_data, t_nChars, StrTraits::GetDefaultManager() ),
|
|
StringType( psz, nLength, static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
explicit CFixedStringT( const StringType::YCHAR* psz ) :
|
|
CFixedStringMgr( &m_data, t_nChars, StrTraits::GetDefaultManager() ),
|
|
StringType( psz, static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
explicit CFixedStringT( const unsigned char* psz ) :
|
|
CFixedStringMgr( &m_data, t_nChars, StrTraits::GetDefaultManager() ),
|
|
StringType( psz, static_cast< CFixedStringMgr* >( this ) )
|
|
{
|
|
}
|
|
|
|
~CFixedStringT() throw()
|
|
{
|
|
Empty();
|
|
}
|
|
|
|
CFixedStringT< StringType, t_nChars >& operator=( const CFixedStringT< StringType, t_nChars >& str )
|
|
{
|
|
StringType::operator=( str );
|
|
return *this;
|
|
}
|
|
|
|
CFixedStringT< StringType, t_nChars >& operator=( const char* psz )
|
|
{
|
|
StringType::operator=( psz );
|
|
return *this;
|
|
}
|
|
|
|
CFixedStringT< StringType, t_nChars >& operator=( const wchar_t* psz )
|
|
{
|
|
StringType::operator=( psz );
|
|
return *this;
|
|
}
|
|
|
|
CFixedStringT< StringType, t_nChars >& operator=( const unsigned char* psz )
|
|
{
|
|
StringType::operator=( psz );
|
|
return *this;
|
|
}
|
|
|
|
CFixedStringT< StringType, t_nChars >& operator=( const StringType& str )
|
|
{
|
|
StringType::operator=( str );
|
|
return *this;
|
|
}
|
|
|
|
// Implementation
|
|
protected:
|
|
CStringData m_data;
|
|
StringType::XCHAR m_achData[t_nChars+1];
|
|
};
|
|
|
|
#pragma warning( pop )
|
|
class CFixedStringLog :
|
|
public IFixedStringLog
|
|
{
|
|
public:
|
|
CFixedStringLog() throw()
|
|
{
|
|
CFixedStringMgr::s_pLog = this;
|
|
}
|
|
~CFixedStringLog() throw()
|
|
{
|
|
CFixedStringMgr::s_pLog = NULL;
|
|
}
|
|
|
|
public:
|
|
void OnAllocateSpill( int nActualChars, int nFixedChars, const CStringData* pData ) throw()
|
|
{
|
|
(void)nActualChars;
|
|
(void)nFixedChars;
|
|
(void)pData;
|
|
ATLTRACE( atlTraceString, 0, _T( "CFixedStringMgr::Allocate() spilling to heap. %d chars (fixed size = %d chars)\n" ), nActualChars, nFixedChars );
|
|
}
|
|
void OnReallocateSpill( int nActualChars, int nFixedChars, const CStringData* pData ) throw()
|
|
{
|
|
(void)nActualChars;
|
|
(void)nFixedChars;
|
|
(void)pData;
|
|
ATLTRACE( atlTraceString, 0, _T( "CFixedStringMgr::Reallocate() spilling to heap. %d chars (fixed size = %d chars)\n" ), nActualChars, nFixedChars );
|
|
}
|
|
};
|
|
|
|
}; // namespace ATL
|
|
|
|
#pragma pop_macro("new")
|
|
|
|
#endif // __CSTRINGT_H__ (whole file)
|
|
|
|
|