1272 lines
25 KiB
C++
1272 lines
25 KiB
C++
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
|
#pragma warning (disable : 4267)
|
|
|
|
#include "precomp.h"
|
|
|
|
STRU::STRU(
|
|
VOID
|
|
) : m_cchLen( 0 )
|
|
{
|
|
*(QueryStr()) = L'\0';
|
|
}
|
|
|
|
STRU::STRU(
|
|
__inout_ecount(cchInit) WCHAR* pbInit,
|
|
__in DWORD cchInit
|
|
) : m_Buff( pbInit, cchInit * sizeof( WCHAR ) ),
|
|
m_cchLen( 0 )
|
|
/*++
|
|
Description:
|
|
|
|
Used by STACK_STRU. Initially populates underlying buffer with pbInit.
|
|
|
|
pbInit is not freed.
|
|
|
|
Arguments:
|
|
|
|
pbInit - initial memory to use
|
|
cchInit - count, in characters, of pbInit
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
_ASSERTE( cchInit <= (MAXDWORD / sizeof( WCHAR )) );
|
|
_ASSERTE( NULL != pbInit );
|
|
_ASSERTE(cchInit > 0 );
|
|
_ASSERTE(pbInit[0] == L'\0');
|
|
}
|
|
|
|
BOOL
|
|
STRU::IsEmpty(
|
|
VOID
|
|
) const
|
|
{
|
|
return ( m_cchLen == 0 );
|
|
}
|
|
|
|
DWORD
|
|
STRU::QueryCB(
|
|
VOID
|
|
) const
|
|
//
|
|
// Returns the number of bytes in the string excluding the terminating NULL
|
|
//
|
|
{
|
|
return m_cchLen * sizeof( WCHAR );
|
|
}
|
|
|
|
DWORD
|
|
STRU::QueryCCH(
|
|
VOID
|
|
) const
|
|
//
|
|
// Returns the number of characters in the string excluding the terminating NULL
|
|
//
|
|
{
|
|
return m_cchLen;
|
|
}
|
|
|
|
DWORD
|
|
STRU::QuerySizeCCH(
|
|
VOID
|
|
) const
|
|
//
|
|
// Returns size of the underlying storage buffer, in characters
|
|
//
|
|
{
|
|
return m_Buff.QuerySize() / sizeof( WCHAR );
|
|
}
|
|
|
|
__nullterminated
|
|
__ecount(this->m_cchLen)
|
|
WCHAR*
|
|
STRU::QueryStr(
|
|
VOID
|
|
) const
|
|
//
|
|
// Return the string buffer
|
|
//
|
|
{
|
|
return m_Buff.QueryPtr();
|
|
}
|
|
|
|
VOID
|
|
STRU::Reset(
|
|
VOID
|
|
)
|
|
//
|
|
// Resets the internal string to be NULL string. Buffer remains cached.
|
|
//
|
|
{
|
|
_ASSERTE( QueryStr() != NULL );
|
|
*(QueryStr()) = L'\0';
|
|
m_cchLen = 0;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Resize(
|
|
DWORD cchSize
|
|
)
|
|
{
|
|
SIZE_T cbSize = cchSize * sizeof( WCHAR );
|
|
if ( cbSize > MAXDWORD )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
}
|
|
if( !m_Buff.Resize( cbSize ) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::SyncWithBuffer(
|
|
VOID
|
|
)
|
|
//
|
|
// Recalculate the length of the string, etc. because we've modified
|
|
// the buffer directly.
|
|
//
|
|
{
|
|
HRESULT hr;
|
|
size_t size;
|
|
hr = StringCchLengthW( QueryStr(),
|
|
QuerySizeCCH(),
|
|
&size );
|
|
if ( SUCCEEDED( hr ) )
|
|
{
|
|
m_cchLen = static_cast<DWORD>(size);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Copy(
|
|
__in PCWSTR pszCopy
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
size_t cbStr;
|
|
|
|
hr = StringCchLengthW( pszCopy,
|
|
STRSAFE_MAX_CCH,
|
|
&cbStr );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
_ASSERTE( cbStr <= MAXDWORD );
|
|
return Copy( pszCopy,
|
|
cbStr );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Copy(
|
|
__in_ecount(cchLen)
|
|
PCWSTR pszCopy,
|
|
SIZE_T cchLen
|
|
)
|
|
//
|
|
// Copy the contents of another string to this one
|
|
//
|
|
{
|
|
return AuxAppend( pszCopy,
|
|
cchLen * sizeof(WCHAR),
|
|
0);
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Copy(
|
|
__in const STRU * pstrRhs
|
|
)
|
|
{
|
|
_ASSERTE( NULL != pstrRhs );
|
|
return Copy( pstrRhs->QueryStr(), pstrRhs->QueryCCH() );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Copy(
|
|
__in const STRU & str
|
|
)
|
|
{
|
|
return Copy( str.QueryStr(), str.QueryCCH() );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::CopyAndExpandEnvironmentStrings(
|
|
__in PCWSTR pszSource
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD cchDestReqBuff = 0;
|
|
|
|
Reset();
|
|
|
|
cchDestReqBuff = ExpandEnvironmentStringsW( pszSource,
|
|
QueryStr(),
|
|
QuerySizeCCH() );
|
|
if ( cchDestReqBuff == 0 )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
else if ( cchDestReqBuff > QuerySizeCCH() )
|
|
{
|
|
hr = Resize( cchDestReqBuff );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
cchDestReqBuff = ExpandEnvironmentStringsW( pszSource,
|
|
QueryStr(),
|
|
QuerySizeCCH() );
|
|
|
|
if ( cchDestReqBuff == 0 || cchDestReqBuff > QuerySizeCCH() )
|
|
{
|
|
_ASSERTE( FALSE );
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
hr = SyncWithBuffer();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
Finished:
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT
|
|
STRU::CopyA(
|
|
__in PCSTR pszCopyA
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
size_t cbStr;
|
|
|
|
hr = StringCbLengthA( pszCopyA,
|
|
STRSAFE_MAX_CCH,
|
|
&cbStr );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
_ASSERTE( cbStr <= MAXDWORD );
|
|
return CopyA( pszCopyA,
|
|
cbStr );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::CopyA(
|
|
__in_bcount(cchLen)
|
|
PCSTR pszCopyA,
|
|
SIZE_T cchLen,
|
|
UINT CodePage /*= CP_UTF8*/
|
|
)
|
|
{
|
|
return AuxAppendA(
|
|
pszCopyA,
|
|
cchLen,
|
|
0,
|
|
CodePage
|
|
);
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Append(
|
|
__in PCWSTR pszAppend
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
size_t cbStr;
|
|
|
|
hr = StringCchLengthW( pszAppend,
|
|
STRSAFE_MAX_CCH,
|
|
&cbStr );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
_ASSERTE( cbStr <= MAXDWORD );
|
|
return Append( pszAppend,
|
|
cbStr );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Append(
|
|
__in_ecount(cchLen)
|
|
PCWSTR pszAppend,
|
|
SIZE_T cchLen
|
|
)
|
|
//
|
|
// Append something to the end of the string
|
|
//
|
|
{
|
|
if ( cchLen == 0 )
|
|
{
|
|
return S_OK;
|
|
}
|
|
return AuxAppend( pszAppend,
|
|
cchLen * sizeof(WCHAR),
|
|
QueryCB() );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Append(
|
|
__in const STRU * pstrRhs
|
|
)
|
|
{
|
|
_ASSERTE( NULL != pstrRhs );
|
|
return Append( pstrRhs->QueryStr(), pstrRhs->QueryCCH() );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::Append(
|
|
__in const STRU & strRhs
|
|
)
|
|
{
|
|
return Append( strRhs.QueryStr(), strRhs.QueryCCH() );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::AppendA(
|
|
__in PCSTR pszAppendA
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
size_t cbStr;
|
|
|
|
hr = StringCbLengthA( pszAppendA,
|
|
STRSAFE_MAX_CCH,
|
|
&cbStr );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
_ASSERTE( cbStr <= MAXDWORD );
|
|
return AppendA( pszAppendA,
|
|
cbStr );
|
|
}
|
|
|
|
HRESULT
|
|
STRU::AppendA(
|
|
__in_bcount(cchLen)
|
|
PCSTR pszAppendA,
|
|
SIZE_T cchLen,
|
|
UINT CodePage /*= CP_UTF8*/
|
|
)
|
|
{
|
|
if ( cchLen == 0 )
|
|
{
|
|
return S_OK;
|
|
}
|
|
return AuxAppendA(
|
|
pszAppendA,
|
|
cchLen,
|
|
QueryCB(),
|
|
CodePage
|
|
);
|
|
}
|
|
|
|
HRESULT
|
|
STRU::CopyToBuffer(
|
|
__out_bcount(*pcb) WCHAR* pszBuffer,
|
|
PDWORD pcb
|
|
) const
|
|
//
|
|
// Makes a copy of the stored string into the given buffer
|
|
//
|
|
{
|
|
_ASSERTE( NULL != pszBuffer );
|
|
_ASSERTE( NULL != pcb );
|
|
|
|
HRESULT hr = S_OK;
|
|
DWORD cbNeeded = QueryCB() + sizeof( WCHAR );
|
|
|
|
if( *pcb < cbNeeded )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER );
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// BUGBUG: StringCchCopy?
|
|
//
|
|
memcpy( pszBuffer, QueryStr(), cbNeeded );
|
|
|
|
Finished:
|
|
|
|
*pcb = cbNeeded;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::SetLen(
|
|
__in DWORD cchLen
|
|
)
|
|
/*++
|
|
*
|
|
Routine Description:
|
|
|
|
Set the length of the string and null terminate, if there
|
|
is sufficient buffer already allocated. Will not reallocate.
|
|
|
|
Arguments:
|
|
|
|
cchLen - The number of characters in the new string.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
if( cchLen >= QuerySizeCCH() )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
*( QueryStr() + cchLen ) = L'\0';
|
|
m_cchLen = cchLen;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::SafeSnwprintf(
|
|
__in PCWSTR pwszFormatString,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes to a STRU, growing it as needed. It arbitrarily caps growth at 64k chars.
|
|
|
|
Arguments:
|
|
|
|
pwszFormatString - printf format
|
|
... - printf args
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
va_list argsList;
|
|
va_start( argsList, pwszFormatString );
|
|
|
|
hr = SafeVsnwprintf(pwszFormatString, argsList);
|
|
|
|
va_end( argsList );
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::SafeVsnwprintf(
|
|
__in PCWSTR pwszFormatString,
|
|
va_list argsList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes to a STRU, growing it as needed. It arbitrarily caps growth at 64k chars.
|
|
|
|
Arguments:
|
|
|
|
pwszFormatString - printf format
|
|
argsList - printf va_list
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int cchOutput;
|
|
int cchNeeded;
|
|
|
|
//
|
|
// Format the incoming message using vsnprintf()
|
|
// so that the overflows are captured
|
|
//
|
|
cchOutput = _vsnwprintf_s(
|
|
QueryStr(),
|
|
QuerySizeCCH(),
|
|
QuerySizeCCH() - 1,
|
|
pwszFormatString,
|
|
argsList
|
|
);
|
|
|
|
if( cchOutput == -1 )
|
|
{
|
|
//
|
|
// Couldn't fit this in the original STRU size.
|
|
//
|
|
cchNeeded = _vscwprintf( pwszFormatString, argsList );
|
|
if( cchNeeded > 64 * 1024 )
|
|
{
|
|
//
|
|
// If we're trying to produce a string > 64k chars, then
|
|
// there is probably a problem
|
|
//
|
|
hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// _vscprintf doesn't include terminating null character
|
|
//
|
|
cchNeeded++;
|
|
|
|
hr = Resize( cchNeeded );
|
|
if( FAILED( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
cchOutput = _vsnwprintf_s(
|
|
QueryStr(),
|
|
QuerySizeCCH(),
|
|
QuerySizeCCH() - 1,
|
|
pwszFormatString,
|
|
argsList
|
|
);
|
|
if( -1 == cchOutput )
|
|
{
|
|
//
|
|
// This should never happen, cause we should already have correctly sized memory
|
|
//
|
|
_ASSERTE( FALSE );
|
|
|
|
hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
//
|
|
// always null terminate at the last WCHAR
|
|
//
|
|
QueryStr()[ QuerySizeCCH() - 1 ] = L'\0';
|
|
|
|
//
|
|
// we directly touched the buffer - therefore:
|
|
//
|
|
hr = SyncWithBuffer();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
Finished:
|
|
|
|
if( FAILED( hr ) )
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::AuxAppend(
|
|
__in_ecount(cNumStrings)
|
|
PCWSTR const rgpszStrings[],
|
|
SIZE_T cNumStrings
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Appends an array of strings of length cNumStrings
|
|
|
|
Arguments:
|
|
|
|
rgStrings - The array of strings to be appened
|
|
cNumStrings - The count of String
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cbStringsTotal = sizeof( WCHAR ); // Account for null-terminator
|
|
|
|
//
|
|
// Compute total size of the string.
|
|
// Resize internal buffer
|
|
// Copy each array element one by one to backing buffer
|
|
// Update backing buffer string length
|
|
//
|
|
for ( SIZE_T i = 0; i < cNumStrings; i++ )
|
|
{
|
|
_ASSERTE( rgpszStrings[ i ] != NULL );
|
|
if ( NULL == rgpszStrings[ i ] )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
size_t cbString = 0;
|
|
|
|
hr = StringCbLengthW( rgpszStrings[ i ],
|
|
STRSAFE_MAX_CCH * sizeof( WCHAR ),
|
|
&cbString );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
cbStringsTotal += cbString;
|
|
|
|
if ( cbStringsTotal > MAXDWORD )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
}
|
|
}
|
|
|
|
size_t cbBufSizeRequired = QueryCB() + cbStringsTotal;
|
|
if ( cbBufSizeRequired > MAXDWORD )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
}
|
|
|
|
if( m_Buff.QuerySize() < cbBufSizeRequired )
|
|
{
|
|
if( !m_Buff.Resize( cbBufSizeRequired ) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
STRSAFE_LPWSTR pszStringEnd = QueryStr() + QueryCCH();
|
|
size_t cchRemaining = QuerySizeCCH() - QueryCCH();
|
|
for ( SIZE_T i = 0; i < cNumStrings; i++ )
|
|
{
|
|
hr = StringCchCopyExW( pszStringEnd, // pszDest
|
|
cchRemaining, // cchDest
|
|
rgpszStrings[ i ], // pszSrc
|
|
&pszStringEnd, // ppszDestEnd
|
|
&cchRemaining, // pcchRemaining
|
|
0 ); // dwFlags
|
|
if ( FAILED( hr ) )
|
|
{
|
|
_ASSERTE( FALSE );
|
|
HRESULT hr2 = SyncWithBuffer();
|
|
if ( FAILED( hr2 ) )
|
|
{
|
|
return hr2;
|
|
}
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
m_cchLen = static_cast< DWORD >( cbBufSizeRequired ) / sizeof( WCHAR ) - 1;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::AuxAppend(
|
|
__in_bcount(cbStr)
|
|
const WCHAR* pStr,
|
|
SIZE_T cbStr,
|
|
DWORD cbOffset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Appends to the string starting at the (byte) offset cbOffset.
|
|
|
|
Arguments:
|
|
|
|
pStr - A unicode string to be appended
|
|
cbStr - Length, in bytes, of pStr
|
|
cbOffset - Offset, in bytes, at which to begin the append
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
_ASSERTE( NULL != pStr );
|
|
_ASSERTE( 0 == cbStr % sizeof( WCHAR ) );
|
|
_ASSERTE( cbOffset <= QueryCB() );
|
|
_ASSERTE( 0 == cbOffset % sizeof( WCHAR ) );
|
|
|
|
ULONGLONG cb64NewSize = (ULONGLONG)cbOffset + cbStr + sizeof( WCHAR );
|
|
if( cb64NewSize > MAXDWORD )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
}
|
|
|
|
if( m_Buff.QuerySize() < cb64NewSize )
|
|
{
|
|
if( !m_Buff.Resize( static_cast<SIZE_T>(cb64NewSize) ) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
memcpy( reinterpret_cast<BYTE*>(m_Buff.QueryPtr()) + cbOffset, pStr, cbStr );
|
|
|
|
m_cchLen = (static_cast<DWORD>(cbStr) + cbOffset) / sizeof(WCHAR);
|
|
|
|
*( QueryStr() + m_cchLen ) = L'\0';
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
STRU::AuxAppendA(
|
|
__in_bcount(cbStr)
|
|
const CHAR* pStr,
|
|
SIZE_T cbStr,
|
|
DWORD cbOffset,
|
|
UINT CodePage
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert and append an ANSI string to the string starting at
|
|
the (byte) offset cbOffset
|
|
|
|
Arguments:
|
|
|
|
pStr - An ANSI string to be appended
|
|
cbStr - Length, in bytes, of pStr
|
|
cbOffset - Offset, in bytes, at which to begin the append
|
|
CodePage - code page to use for conversion
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
WCHAR* pszBuffer;
|
|
DWORD cchBuffer;
|
|
DWORD cchCharsCopied = 0;
|
|
|
|
_ASSERTE( NULL != pStr );
|
|
_ASSERTE( cbOffset <= QueryCB() );
|
|
_ASSERTE( 0 == cbOffset % sizeof( WCHAR ) );
|
|
|
|
if ( NULL == pStr )
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
if( 0 == cbStr )
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Only resize when we have to. When we do resize, we tack on
|
|
// some extra space to avoid extra reallocations.
|
|
//
|
|
if( m_Buff.QuerySize() < (ULONGLONG)cbOffset + (cbStr * sizeof( WCHAR )) + sizeof(WCHAR) )
|
|
{
|
|
ULONGLONG cb64NewSize = (ULONGLONG)( cbOffset + cbStr * sizeof(WCHAR) + sizeof( WCHAR ) );
|
|
|
|
//
|
|
// Check for the arithmetic overflow
|
|
//
|
|
if( cb64NewSize > MAXDWORD )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_ARITHMETIC_OVERFLOW );
|
|
}
|
|
|
|
if( !m_Buff.Resize( static_cast<SIZE_T>(cb64NewSize) ) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
pszBuffer = reinterpret_cast<WCHAR*>(reinterpret_cast<BYTE*>(m_Buff.QueryPtr()) + cbOffset);
|
|
cchBuffer = ( m_Buff.QuerySize() - cbOffset - sizeof( WCHAR ) ) / sizeof( WCHAR );
|
|
|
|
cchCharsCopied = MultiByteToWideChar(
|
|
CodePage,
|
|
MB_ERR_INVALID_CHARS,
|
|
pStr,
|
|
static_cast<int>(cbStr),
|
|
pszBuffer,
|
|
cchBuffer
|
|
);
|
|
if( 0 == cchCharsCopied )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
//
|
|
// set the new length
|
|
//
|
|
m_cchLen = cchCharsCopied + cbOffset/sizeof(WCHAR);
|
|
|
|
//
|
|
// Must be less than, cause still need to add NULL
|
|
//
|
|
_ASSERTE( m_cchLen < QuerySizeCCH() );
|
|
|
|
//
|
|
// append NULL character
|
|
//
|
|
*(QueryStr() + m_cchLen) = L'\0';
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes leading and trailing whitespace
|
|
|
|
--*/
|
|
|
|
VOID
|
|
STRU::Trim()
|
|
{
|
|
PWSTR pwszString = QueryStr();
|
|
DWORD cchNewLength = m_cchLen;
|
|
DWORD cchLeadingWhitespace = 0;
|
|
DWORD cchTempLength = 0;
|
|
|
|
for (LONG ixString = m_cchLen - 1; ixString >= 0; ixString--)
|
|
{
|
|
if (iswspace(pwszString[ixString]) != 0)
|
|
{
|
|
pwszString[ixString] = L'\0';
|
|
cchNewLength--;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
cchTempLength = cchNewLength;
|
|
for (DWORD ixString = 0; ixString < cchTempLength; ixString++)
|
|
{
|
|
if (iswspace(pwszString[ixString]) != 0)
|
|
{
|
|
cchLeadingWhitespace++;
|
|
cchNewLength--;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cchNewLength == 0)
|
|
{
|
|
|
|
Reset();
|
|
}
|
|
else if (cchLeadingWhitespace > 0)
|
|
{
|
|
memmove(pwszString, pwszString + cchLeadingWhitespace, cchNewLength * sizeof(WCHAR));
|
|
pwszString[cchNewLength] = L'\0';
|
|
}
|
|
|
|
SyncWithBuffer();
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compares the string to the provided prefix to check for equality
|
|
|
|
Arguments:
|
|
|
|
pwszPrefix - wide char string to compare with
|
|
fIgnoreCase - indicates whether the string comparison should be case-sensitive
|
|
|
|
Return Value:
|
|
|
|
TRUE if prefix string matches with internal string, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
STRU::StartsWith(
|
|
__in PCWSTR pwszPrefix,
|
|
__in bool fIgnoreCase) const
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fMatch = FALSE;
|
|
size_t cchPrefix = 0;
|
|
|
|
if (pwszPrefix == NULL)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
hr = StringCchLengthW( pwszPrefix,
|
|
STRSAFE_MAX_CCH,
|
|
&cchPrefix );
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
_ASSERTE( cchPrefix <= MAXDWORD );
|
|
|
|
if (cchPrefix > m_cchLen)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
#if defined( NTDDI_VERSION ) && NTDDI_VERSION >= NTDDI_LONGHORN
|
|
|
|
fMatch = ( CSTR_EQUAL == CompareStringOrdinal( QueryStr(),
|
|
cchPrefix,
|
|
pwszPrefix,
|
|
cchPrefix,
|
|
fIgnoreCase ) );
|
|
#else
|
|
|
|
if( fIgnoreCase )
|
|
{
|
|
fMatch = ( 0 == _wcsnicmp( QueryStr(), pwszPrefix, cchPrefix ) );
|
|
}
|
|
else
|
|
{
|
|
fMatch = ( 0 == wcsncmp( QueryStr(), pwszPrefix, cchPrefix ) );
|
|
}
|
|
|
|
#endif
|
|
|
|
Finished:
|
|
|
|
return fMatch;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compares the string to the provided suffix to check for equality
|
|
|
|
Arguments:
|
|
|
|
pwszSuffix - wide char string to compare with
|
|
fIgnoreCase - indicates whether the string comparison should be case-sensitive
|
|
|
|
Return Value:
|
|
|
|
TRUE if suffix string matches with internal string, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
|
|
BOOL
|
|
STRU::EndsWith(
|
|
__in PCWSTR pwszSuffix,
|
|
__in bool fIgnoreCase) const
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PWSTR pwszString = QueryStr();
|
|
BOOL fMatch = FALSE;
|
|
size_t cchSuffix = 0;
|
|
ptrdiff_t ixOffset = 0;
|
|
|
|
if (pwszSuffix == NULL)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
hr = StringCchLengthW( pwszSuffix,
|
|
STRSAFE_MAX_CCH,
|
|
&cchSuffix );
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
_ASSERTE( cchSuffix <= MAXDWORD );
|
|
|
|
if (cchSuffix > m_cchLen)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
ixOffset = m_cchLen - cchSuffix;
|
|
_ASSERTE(ixOffset >= 0 && ixOffset <= MAXDWORD);
|
|
|
|
#if defined( NTDDI_VERSION ) && NTDDI_VERSION >= NTDDI_LONGHORN
|
|
|
|
fMatch = ( CSTR_EQUAL == CompareStringOrdinal( pwszString + ixOffset,
|
|
cchSuffix,
|
|
pwszSuffix,
|
|
cchSuffix,
|
|
fIgnoreCase ) );
|
|
#else
|
|
|
|
if( fIgnoreCase )
|
|
{
|
|
fMatch = ( 0 == _wcsnicmp( pwszString + ixOffset, pwszSuffix, cchSuffix ) );
|
|
}
|
|
else
|
|
{
|
|
fMatch = ( 0 == wcsncmp( pwszString + ixOffset, pwszSuffix, cchSuffix ) );
|
|
}
|
|
|
|
#endif
|
|
|
|
Finished:
|
|
|
|
return fMatch;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches the string for the first occurrence of the specified character.
|
|
|
|
Arguments:
|
|
|
|
charValue - character to find
|
|
dwStartIndex - the initial index.
|
|
|
|
Return Value:
|
|
|
|
The index for the first character occurence in the string.
|
|
|
|
-1 if not found.
|
|
|
|
--*/
|
|
INT
|
|
STRU::IndexOf(
|
|
__in WCHAR charValue,
|
|
__in DWORD dwStartIndex
|
|
) const
|
|
{
|
|
INT nIndex = -1;
|
|
|
|
// Make sure that there are no buffer overruns.
|
|
if( dwStartIndex >= QueryCCH() )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
const WCHAR* pwChar = wcschr( QueryStr() + dwStartIndex, charValue );
|
|
|
|
// Determine the index if found
|
|
if( pwChar )
|
|
{
|
|
// nIndex will be set to -1 on failure.
|
|
(VOID)SizeTToInt( pwChar - QueryStr(), &nIndex );
|
|
}
|
|
|
|
Finished:
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches the string for the first occurrence of the specified substring.
|
|
|
|
Arguments:
|
|
|
|
pwszValue - substring to find
|
|
dwStartIndex - initial index.
|
|
|
|
Return Value:
|
|
|
|
The index for the first character occurence in the string.
|
|
|
|
-1 if not found.
|
|
|
|
--*/
|
|
INT
|
|
STRU::IndexOf(
|
|
__in PCWSTR pwszValue,
|
|
__in DWORD dwStartIndex
|
|
) const
|
|
{
|
|
HRESULT hr = S_OK;
|
|
INT nIndex = -1;
|
|
SIZE_T cchValue = 0;
|
|
|
|
// Validate input parameters
|
|
if( dwStartIndex >= QueryCCH() || !pwszValue )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
const WCHAR* pwChar = wcsstr( QueryStr() + dwStartIndex, pwszValue );
|
|
|
|
// Determine the index if found
|
|
if( pwChar )
|
|
{
|
|
// nIndex will be set to -1 on failure.
|
|
(VOID)SizeTToInt( pwChar - QueryStr(), &nIndex );
|
|
}
|
|
|
|
Finished:
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches the string for the last occurrence of the specified character.
|
|
|
|
Arguments:
|
|
|
|
charValue - character to find
|
|
dwStartIndex - initial index.
|
|
|
|
Return Value:
|
|
|
|
The index for the last character occurence in the string.
|
|
|
|
-1 if not found.
|
|
|
|
--*/
|
|
INT
|
|
STRU::LastIndexOf(
|
|
__in WCHAR charValue,
|
|
__in DWORD dwStartIndex
|
|
) const
|
|
{
|
|
INT nIndex = -1;
|
|
|
|
// Make sure that there are no buffer overruns.
|
|
if( dwStartIndex >= QueryCCH() )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
const WCHAR* pwChar = wcsrchr( QueryStr() + dwStartIndex, charValue );
|
|
|
|
// Determine the index if found
|
|
if( pwChar )
|
|
{
|
|
// nIndex will be set to -1 on failure.
|
|
(VOID)SizeTToInt( pwChar - QueryStr(), &nIndex );
|
|
}
|
|
|
|
Finished:
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
STRU::ExpandEnvironmentVariables(
|
|
__in PCWSTR pszString,
|
|
__out STRU * pstrExpandedString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Expand the environment variables in a string
|
|
|
|
Arguments:
|
|
|
|
pszString - String with environment variables to expand
|
|
pstrExpandedString - Receives expanded string on success
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD cchNewSize = 0;
|
|
|
|
if ( pszString == NULL ||
|
|
pstrExpandedString == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
goto Exit;
|
|
}
|
|
|
|
cchNewSize = ExpandEnvironmentStrings( pszString,
|
|
pstrExpandedString->QueryStr(),
|
|
pstrExpandedString->QuerySizeCCH() );
|
|
if ( cchNewSize == 0 )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Exit;
|
|
}
|
|
|
|
if ( cchNewSize > pstrExpandedString->QuerySizeCCH() )
|
|
{
|
|
hr = pstrExpandedString->Resize(
|
|
( cchNewSize + 1 ) * sizeof( WCHAR )
|
|
);
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
cchNewSize = ExpandEnvironmentStrings(
|
|
pszString,
|
|
pstrExpandedString->QueryStr(),
|
|
pstrExpandedString->QuerySizeCCH()
|
|
);
|
|
|
|
if ( cchNewSize == 0 ||
|
|
cchNewSize > pstrExpandedString->QuerySizeCCH() )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pstrExpandedString->SyncWithBuffer();
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
|
|
return hr;
|
|
}
|
|
|
|
#pragma warning(default:4267)
|