// // TSTR - represents a writable position in a string. // // Has methods to safely append to the string. Will not overrun buffer, // truncates if reaches the end. // // // Sample usage: // // void SomeFunc( TSTR & str ) // { // int i = 42; // str << TEXT("Value is: ") << i; // } // // Can be used with TCHAR*-style APIs, by using the ptr(), left() and // advance() members. ptr() returns pointer to current write position, // left() returns number of chars left, and advance() updates the write // position. // // void MyGetWindowText( StrWrPos & str ) // { // int len = GetWindowText( hWnd, str.ptr(), str.left() ); // str.advance( len ); // } // // This makes sure that the text will not be truncated // void MyGetWindowText( StrWrPos & str ) // { // str.anticipate( GetWindowTextLength( hWnd ); // int len = GetWindowText( hWnd, str.ptr(), str.left() ); // str.advance( len ); // } // // Sample usage: // // void Func( TSTR & str ); // // TSTR s(128); // s << TEXT("Text added: ["); // Func( s ); // add more text to string // s << TEXT("]"); // // SetWindowText( hwnd, s ); // // // WriteHex - helper class to output hex values: // // Sample usage: // // str << TEXT("Value is:") << WriteHex( hwnd, 8 ); // // Can optionally specify number of digits to output. (result will be // 0-padded.) // // // WriteError - helper class to output COM error values: // // Sample usage: // // hr = ProcessData(); // if( hr != S_OK ) // { // str << WriteError( hr, TEXT("in ProcessData()"); // LogError( str.str() ); // } // #ifndef _TSTR_H_ #define _TSTR_H_ #if ! defined( _BASETSD_H_ ) || defined( NEED_BASETSD_DEFINES ) // These allow us to compile with the pre-Win64 SDK (eg. using visual studio) typedef unsigned long UINT_PTR; typedef DWORD DWORD_PTR; #define PtrToInt (int) #endif #define LONG_TEXT_LENGTH 40 #include #include #include typedef std::basic_string tstring; typedef std::string ASTR; // save these names for where we expand typedef std::wstring WSTR; // the usage of this stuff to include them class TSTR : public tstring { // this is only used for ptr, left and advance functions. ULONG m_lTheRealSize; public: TSTR() : m_lTheRealSize(-1) { } TSTR(const TCHAR *s) : tstring(s, static_cast(lstrlen(s))), m_lTheRealSize(-1) { } TSTR(const TCHAR *s, size_type n) : tstring(s, n), m_lTheRealSize(-1) { } TSTR(const tstring& rhs) : tstring(rhs), m_lTheRealSize(-1) { } TSTR(const tstring& rhs, size_type pos, size_type n) : tstring(rhs, pos, n), m_lTheRealSize(-1) { } TSTR(size_type n, TCHAR c) : tstring(n, c), m_lTheRealSize(-1) { } TSTR(size_type n) : tstring(), m_lTheRealSize(-1) { reserve( n + 1 ); } TSTR(const_iterator first, const_iterator last) : tstring(first, last), m_lTheRealSize(-1) { } operator const TCHAR * () { return c_str(); } TCHAR * ptr() { _ASSERT(m_lTheRealSize == -1); m_lTheRealSize = size(); TCHAR *pEnd = &(*end()); resize(capacity()); return pEnd; } unsigned int left() { unsigned int left; if (m_lTheRealSize == -1) left = ( capacity() - size() ) - 1; else left = ( capacity() - m_lTheRealSize ) - 1; return left; } void advance( unsigned int c ) { _ASSERT(m_lTheRealSize != -1); // ptr has not been called so we should not need to advance if (m_lTheRealSize != -1) { at( m_lTheRealSize + c ) = NULL; // make sure this stays null terminated resize(m_lTheRealSize + c); m_lTheRealSize = -1; } } void reset() { resize(0); m_lTheRealSize = -1; } void anticipate( unsigned int c ) { if ( c > 0 ) { unsigned int cSize; if ( m_lTheRealSize == -1 ) cSize = size(); else cSize = m_lTheRealSize; const unsigned int i = capacity() - cSize; if ( i < c ) reserve( cSize + c + 1 ); } } }; inline TSTR & operator << ( TSTR & str, const TCHAR * obj ) { if ( obj ) str.append( obj ); return str; } inline TSTR & operator << ( TSTR & str, TCHAR obj ) { str.append( &obj, 1 ); return str; } inline TSTR & operator << ( TSTR & str, long obj ) { TCHAR sz[LONG_TEXT_LENGTH]; #ifdef UNICODE str.append(_ltow( obj, sz, 10 )); return str; #else str.append(_ltoa( obj, sz, 10 )); return str; #endif } inline TSTR & operator << ( TSTR & str, unsigned long obj ) { TCHAR sz[LONG_TEXT_LENGTH]; #ifdef UNICODE str.append(_ultow( obj, sz, 10 )); return str; #else str.append(_ultoa( obj, sz, 10 )); return str; #endif } inline TSTR & operator << ( TSTR & str, int obj ) { TCHAR sz[LONG_TEXT_LENGTH]; #ifdef UNICODE str.append(_itow( obj, sz, 10 )); return str; #else str.append(_itoa( obj, sz, 10 )); return str; #endif } inline TSTR & operator << ( TSTR & str, unsigned int obj ) { TCHAR sz[LONG_TEXT_LENGTH]; #ifdef UNICODE str.append(_ultow( static_cast(obj), sz, 10 )); return str; #else str.append(_ultoa( static_cast(obj), sz, 10 )); return str; #endif } #ifndef UNICODE inline TSTR & operator << ( TSTR & str, const WCHAR * obj ) { if ( obj ) { str.anticipate( wcslen( obj ) + 1 ); int len = WideCharToMultiByte( CP_ACP, 0, obj, -1, str.ptr(), str.left(), NULL, NULL ); // Len, in this case, includes the terminating NUL - so subtract it, if // we got one... if( len > 0 ) len--; str.advance( len ); } return str; } #endif // // WriteHex - helper class to output hex values: // // See top of file for usage notes. // class WriteHex { DWORD_PTR m_dw; int m_Digits; public: // If Digits not specified, uses only as many as needed. WriteHex( DWORD dw, int Digits = -1 ) : m_dw( dw ), m_Digits( Digits ) { } // For pointer, pads if necessary to get std. ptr size. // (sizeof(ptr)*2, since 2 digits per byte in ptr). WriteHex( const void * pv, int Digits = sizeof(void*)*2 ) : m_dw( (DWORD_PTR)pv ), m_Digits( Digits ) { } void Write( TSTR & str ) const { static const TCHAR * HexChars = TEXT("0123456789ABCDEF"); //str << TEXT("0x"); int Digit; if( m_Digits == -1 ) { // Work out number of digits... Digit = 0; DWORD test = m_dw; while( test ) { Digit++; test >>= 4; } // Special case for 0 - still want one digit. if( Digit == 0 ) Digit = 1; } else Digit = m_Digits; while( Digit ) { Digit--; str << HexChars[ ( m_dw >> (Digit * 4) ) & 0x0F ]; } } }; inline TSTR & operator << ( TSTR & s, const WriteHex & obj ) { obj.Write( s ); return s; } // // WriteError - helper class to output COM error values: // // See top of file for usage notes. // class WriteError { HRESULT m_hr; LPCTSTR m_pWhere; public: WriteError( HRESULT hr, LPCTSTR pWhere = NULL ) : m_hr( hr ), m_pWhere( pWhere ) { } void Write( TSTR & str ) const { str << TEXT("[Error"); if( m_pWhere ) str << TEXT(" ") << m_pWhere; str << TEXT(": hr=0x") << WriteHex( m_hr ) << TEXT(" - "); if( m_hr == S_FALSE ) { str << TEXT("S_FALSE"); } else { int len = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, m_hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language str.ptr(), str.left(), NULL ); if( len > 2 ) len -= 2; // Ignore trailing /r/n that FmtMsg() adds... str.advance( len ); } str << TEXT("]"); } }; inline TSTR & operator << ( TSTR & s, const WriteError & obj ) { obj.Write( s ); return s; } inline TSTR & operator << ( TSTR & s, const GUID & guid ) { s << TEXT("{") << WriteHex( guid.Data1, 8 ) // DWORD << TEXT("-") << WriteHex( guid.Data2, 4 ) // WORD << TEXT("-") << WriteHex( guid.Data3, 4 ) // WORD << TEXT("-") << WriteHex( guid.Data4[ 0 ], 2 ) << WriteHex( guid.Data4[ 1 ], 2 ) << TEXT("-"); for( int i = 2 ; i < 8 ; i++ ) { s << WriteHex( guid.Data4[ i ], 2 ); // BYTE } s << TEXT("}"); return s; } inline TSTR & operator << ( TSTR & s, const VARIANT & var ) { s << TEXT("["); switch( var.vt ) { case VT_EMPTY: { s << TEXT("VT_EMPTY"); break; } case VT_I4: { s << TEXT("VT_I4=0x"); s << WriteHex( var.lVal ); break; } case VT_I2: { s << TEXT("VT_I2=0x"); s << WriteHex( var.iVal ); break; } case VT_BOOL: { s << TEXT("VT_BOOL="); if( var.boolVal == VARIANT_TRUE ) s << TEXT("TRUE"); else if( var.boolVal == VARIANT_FALSE ) s << TEXT("FALSE"); else s << TEXT("?") << var.boolVal; break; } case VT_R4: { float fltval = var.fltVal; int x = (int)(fltval * 100); s << TEXT("VT_R4=") << x/100 << TEXT(".") << x/10 % 10 << x % 10; break; } case VT_BSTR: { s << TEXT("VT_BSTR=\"") << var.bstrVal << TEXT("\""); break; } case VT_UNKNOWN: { s << TEXT("VT_UNKNOWN=0x") << WriteHex( var.punkVal, 8 ); break; } case VT_DISPATCH: { s << TEXT("VT_DISPATCH=0x") << WriteHex( var.pdispVal, 8 ); break; } default: { s << TEXT("VT_? ") << (long)var.vt; break; } } s << TEXT("]"); return s; } inline TSTR & operator << ( TSTR & s, const POINT & pt ) { s << TEXT("{x:") << pt.x << TEXT(" y:") << pt.y << TEXT("}"); return s; } inline TSTR & operator << ( TSTR & s, const RECT & rc ) { s << TEXT("{l:") << rc.left << TEXT(" t:") << rc.top << TEXT(" r:") << rc.right << TEXT(" b:") << rc.bottom << TEXT("}"); return s; } #endif // _TSTR_H_