mirror of https://github.com/UMSKT/xpmgr.git
9859 lines
312 KiB
C
9859 lines
312 KiB
C
/******************************************************************
|
|
* *
|
|
* strsafe.h -- This module defines safer C library string *
|
|
* routine replacements. These are meant to make C *
|
|
* a bit more safe in reference to security and *
|
|
* robustness *
|
|
* *
|
|
* Copyright (c) Microsoft Corp. All rights reserved. *
|
|
* *
|
|
******************************************************************/
|
|
#ifndef _STRSAFE_H_INCLUDED_
|
|
#define _STRSAFE_H_INCLUDED_
|
|
#if (_MSC_VER > 1000)
|
|
#pragma once
|
|
#endif
|
|
|
|
|
|
#include <stdio.h> // for _vsnprintf, _vsnwprintf, getc, getwc
|
|
#include <string.h> // for memset
|
|
#include <stdarg.h> // for va_start, etc.
|
|
#include <specstrings.h> // for __in, etc.
|
|
|
|
#if !defined(_W64)
|
|
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && (_MSC_VER >= 1300)
|
|
#define _W64 __w64
|
|
#else
|
|
#define _W64
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) || defined(_M_IA64) || defined(_M_AMD64)
|
|
#define ALIGNMENT_MACHINE
|
|
#define UNALIGNED __unaligned
|
|
#if defined(_WIN64)
|
|
#define UNALIGNED64 __unaligned
|
|
#else
|
|
#define UNALIGNED64
|
|
#endif
|
|
#else
|
|
#undef ALIGNMENT_MACHINE
|
|
#define UNALIGNED
|
|
#define UNALIGNED64
|
|
#endif
|
|
|
|
// typedefs
|
|
#ifdef _WIN64
|
|
typedef unsigned __int64 size_t;
|
|
#else
|
|
typedef _W64 unsigned int size_t;
|
|
#endif
|
|
|
|
#ifndef _HRESULT_DEFINED
|
|
#define _HRESULT_DEFINED
|
|
typedef __success(return >= 0) long HRESULT;
|
|
#endif
|
|
|
|
typedef unsigned long DWORD;
|
|
|
|
|
|
// macros
|
|
#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
|
|
#define FAILED(hr) (((HRESULT)(hr)) < 0)
|
|
|
|
#define S_OK ((HRESULT)0L)
|
|
|
|
#ifndef SORTPP_PASS
|
|
// compiletime asserts (failure results in error C2118: negative subscript)
|
|
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
|
|
#else
|
|
#define C_ASSERT(e)
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
#define EXTERN_C extern "C"
|
|
#else
|
|
#define EXTERN_C extern
|
|
#endif
|
|
|
|
// use the new secure crt functions if available
|
|
#ifndef STRSAFE_USE_SECURE_CRT
|
|
#if defined(__GOT_SECURE_LIB__) && (__GOT_SECURE_LIB__ >= 200402L)
|
|
#define STRSAFE_USE_SECURE_CRT 0
|
|
#else
|
|
#define STRSAFE_USE_SECURE_CRT 0
|
|
#endif
|
|
#endif // !STRSAFE_USE_SECURE_CRT
|
|
|
|
#ifdef _M_CEE_PURE
|
|
#define STRSAFEAPI __inline HRESULT __clrcall
|
|
#else
|
|
#define STRSAFEAPI __inline HRESULT __stdcall
|
|
#endif
|
|
|
|
#if defined(STRSAFE_LIB_IMPL) || defined(STRSAFE_LIB)
|
|
#define STRSAFEWORKERAPI EXTERN_C HRESULT __stdcall
|
|
#else
|
|
#define STRSAFEWORKERAPI static STRSAFEAPI
|
|
#endif
|
|
|
|
#ifdef STRSAFE_LOCALE_FUNCTIONS
|
|
#if defined(STRSAFE_LOCALE_LIB_IMPL) || defined(STRSAFE_LIB)
|
|
#define STRSAFELOCALEWORKERAPI EXTERN_C HRESULT __stdcall
|
|
#else
|
|
#define STRSAFELOCALEWORKERAPI static STRSAFEAPI
|
|
#endif
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS
|
|
|
|
#if defined(STRSAFE_LIB)
|
|
#pragma comment(lib, "strsafe.lib")
|
|
#endif
|
|
|
|
// The user can request no "Cb" or no "Cch" fuctions, but not both
|
|
#if defined(STRSAFE_NO_CB_FUNCTIONS) && defined(STRSAFE_NO_CCH_FUNCTIONS)
|
|
#error cannot specify both STRSAFE_NO_CB_FUNCTIONS and STRSAFE_NO_CCH_FUNCTIONS !!
|
|
#endif
|
|
|
|
// The user may override STRSAFE_MAX_CCH, but it must always be less than INT_MAX
|
|
#ifndef STRSAFE_MAX_CCH
|
|
#define STRSAFE_MAX_CCH 2147483647 // max buffer size, in characters, that we support (same as INT_MAX)
|
|
#endif
|
|
C_ASSERT(STRSAFE_MAX_CCH <= 2147483647);
|
|
C_ASSERT(STRSAFE_MAX_CCH > 1);
|
|
|
|
#define STRSAFE_MAX_LENGTH (STRSAFE_MAX_CCH - 1) // max buffer length, in characters, that we support
|
|
|
|
|
|
// Flags for controling the Ex functions
|
|
//
|
|
// STRSAFE_FILL_BYTE(0xFF) 0x000000FF // bottom byte specifies fill pattern
|
|
#define STRSAFE_IGNORE_NULLS 0x00000100 // treat null string pointers as TEXT("") -- don't fault on NULL buffers
|
|
#define STRSAFE_FILL_BEHIND_NULL 0x00000200 // on success, fill in extra space behind the null terminator with fill pattern
|
|
#define STRSAFE_FILL_ON_FAILURE 0x00000400 // on failure, overwrite pszDest with fill pattern and null terminate it
|
|
#define STRSAFE_NULL_ON_FAILURE 0x00000800 // on failure, set *pszDest = TEXT('\0')
|
|
#define STRSAFE_NO_TRUNCATION 0x00001000 // instead of returning a truncated result, copy/append nothing to pszDest and null terminate it
|
|
|
|
#define STRSAFE_VALID_FLAGS (0x000000FF | STRSAFE_IGNORE_NULLS | STRSAFE_FILL_BEHIND_NULL | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION)
|
|
|
|
// helper macro to set the fill character and specify buffer filling
|
|
#define STRSAFE_FILL_BYTE(x) ((DWORD)((x & 0x000000FF) | STRSAFE_FILL_BEHIND_NULL))
|
|
#define STRSAFE_FAILURE_BYTE(x) ((DWORD)((x & 0x000000FF) | STRSAFE_FILL_ON_FAILURE))
|
|
|
|
#define STRSAFE_GET_FILL_PATTERN(dwFlags) ((int)(dwFlags & 0x000000FF))
|
|
|
|
|
|
// error return codes
|
|
#define STRSAFE_E_INSUFFICIENT_BUFFER ((HRESULT)0x8007007AL) // 0x7A = 122L = ERROR_INSUFFICIENT_BUFFER
|
|
#define STRSAFE_E_INVALID_PARAMETER ((HRESULT)0x80070057L) // 0x57 = 87L = ERROR_INVALID_PARAMETER
|
|
#define STRSAFE_E_END_OF_FILE ((HRESULT)0x80070026L) // 0x26 = 38L = ERROR_HANDLE_EOF
|
|
|
|
//
|
|
// These typedefs are used in places where the string is guaranteed to
|
|
// be null terminated.
|
|
//
|
|
typedef __nullterminated char* STRSAFE_LPSTR;
|
|
typedef __nullterminated const char* STRSAFE_LPCSTR;
|
|
typedef __nullterminated wchar_t* STRSAFE_LPWSTR;
|
|
typedef __nullterminated const wchar_t* STRSAFE_LPCWSTR;
|
|
typedef __nullterminated const wchar_t UNALIGNED* STRSAFE_LPCUWSTR;
|
|
|
|
//
|
|
// These typedefs are used in places where the string is NOT guaranteed to
|
|
// be null terminated.
|
|
//
|
|
typedef __possibly_notnullterminated const char* STRSAFE_PCNZCH;
|
|
typedef __possibly_notnullterminated const wchar_t* STRSAFE_PCNZWCH;
|
|
typedef __possibly_notnullterminated const wchar_t UNALIGNED* STRSAFE_PCUNZWCH;
|
|
|
|
|
|
// prototypes for the worker functions
|
|
|
|
STRSAFEWORKERAPI
|
|
StringLengthWorkerA(
|
|
__in STRSAFE_PCNZCH psz,
|
|
__in __in_range(<=, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringLengthWorkerW(
|
|
__in STRSAFE_PCNZWCH psz,
|
|
__in __in_range(<=, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength);
|
|
|
|
#ifdef ALIGNMENT_MACHINE
|
|
STRSAFEWORKERAPI
|
|
UnalignedStringLengthWorkerW(
|
|
__in STRSAFE_PCUNZWCH psz,
|
|
__in __in_range(<=, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength);
|
|
#endif // ALIGNMENT_MACHINE
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateSrcA(
|
|
__deref_in_opt_out STRSAFE_LPCSTR* ppszSrc,
|
|
__inout_opt __deref_out_range(<, cchMax) size_t* pcchToRead,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateSrcW(
|
|
__deref_in_opt_out STRSAFE_LPCWSTR* ppszSrc,
|
|
__inout_opt __deref_out_range(<, cchMax) size_t* pcchToRead,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestA(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestAndLengthA(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestW(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZWCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestAndLengthW(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestA(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestAndLengthA(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestW(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZWCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestAndLengthW(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringCopyWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, (cchToCopy < cchDest) ? cchToCopy : cchDest - 1) size_t* pcchNewDestLength,
|
|
__in_xcount(cchToCopy) STRSAFE_PCNZCH pszSrc,
|
|
__in __in_range(<, STRSAFE_MAX_CCH) size_t cchToCopy);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringCopyWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, (cchToCopy < cchDest) ? cchToCopy : cchDest - 1) size_t* pcchNewDestLength,
|
|
__in_xcount(cchToCopy) STRSAFE_PCNZWCH pszSrc,
|
|
__in __in_range(<, STRSAFE_MAX_CCH) size_t cchToCopy);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringVPrintfWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in va_list argList);
|
|
|
|
#ifdef STRSAFE_LOCALE_FUNCTIONS
|
|
STRSAFELOCALEWORKERAPI
|
|
StringVPrintf_lWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList);
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS
|
|
|
|
STRSAFEWORKERAPI
|
|
StringVPrintfWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in va_list argList);
|
|
|
|
#ifdef STRSAFE_LOCALE_FUNCTIONS
|
|
STRSAFELOCALEWORKERAPI
|
|
StringVPrintf_lWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList);
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS
|
|
|
|
#ifndef STRSAFE_LIB_IMPL
|
|
// always run these functions inline always since they use stdin
|
|
|
|
STRSAFEAPI
|
|
StringGetsWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength);
|
|
|
|
STRSAFEAPI
|
|
StringGetsWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength);
|
|
|
|
#endif // !STRSAFE_LIB_IMPL
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleFillBehindNullA(
|
|
__inout_bcount(cbRemaining) STRSAFE_LPSTR pszDestEnd,
|
|
__in size_t cbRemaining,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleFillBehindNullW(
|
|
__inout_bcount(cbRemaining) STRSAFE_LPWSTR pszDestEnd,
|
|
__in size_t cbRemaining,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleOtherFlagsA(
|
|
__inout_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(sizeof(char), STRSAFE_MAX_CCH * sizeof(char)) size_t cbDest,
|
|
__in __in_range(<, cbDest / sizeof(char)) size_t cchOriginalDestLength,
|
|
__deref_inout_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out __deref_out_range(<=, cbDest / sizeof(char)) size_t* pcchRemaining,
|
|
__in DWORD dwFlags);
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleOtherFlagsW(
|
|
__inout_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(sizeof(wchar_t), STRSAFE_MAX_CCH * sizeof(wchar_t)) size_t cbDest,
|
|
__in __in_range(<, cbDest / sizeof(wchar_t)) size_t cchOriginalDestLength,
|
|
__deref_inout_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out __deref_out_range(<=, cbDest / sizeof(wchar_t)) size_t* pcchRemaining,
|
|
__in DWORD dwFlags);
|
|
|
|
|
|
// To allow this to stand alone.
|
|
#define __WARNING_CYCLOMATIC_COMPLEXITY 28734
|
|
#define __WARNING_DEREF_NULL_PTR 6011
|
|
#define __WARNING_INVALID_PARAM_VALUE_1 6387
|
|
#define __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY 26015
|
|
#define __WARNING_RETURNING_BAD_RESULT 28196
|
|
#define __WARNING_BANNED_API_USAGE 28719
|
|
|
|
#pragma warning(push)
|
|
#if _MSC_VER <= 1400
|
|
#pragma warning(disable: 4616) // turn off warning out of range so prefast pragmas won't show
|
|
// show up in build.wrn/build.err
|
|
#endif
|
|
#pragma warning(disable : 4996) // 'function': was declared deprecated
|
|
#pragma warning(disable : 4995) // name was marked as #pragma deprecated
|
|
#pragma warning(disable : 4793) // vararg causes native code generation
|
|
#pragma warning(disable : __WARNING_CYCLOMATIC_COMPLEXITY)
|
|
|
|
|
|
#ifndef STRSAFE_LIB_IMPL
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCopy(
|
|
__out_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcpy'.
|
|
The size of the destination buffer (in characters) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This routine is not a replacement for strncpy. That function will pad the
|
|
destination string with extra null termination characters if the count is
|
|
greater than the length of the source string, and it will fail to null
|
|
terminate the destination string if the source string length is greater
|
|
than or equal to the count. You can not blindly use this instead of strncpy:
|
|
it is common for code to use it to "patch" strings and you would introduce
|
|
errors if the code started null terminating in the middle of the string.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was copied without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases as much of
|
|
pszSrc will be copied to pszDest as possible, and pszDest will be null
|
|
terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be = (_tcslen(src) + 1) to hold all of the
|
|
source including the null terminator
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCchCopyEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCopy StringCchCopyW
|
|
#else
|
|
#define StringCchCopy StringCchCopyA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCWSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCopy(
|
|
__out_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcpy'.
|
|
The size of the destination buffer (in bytes) is a parameter and this
|
|
function will not write past the end of this buffer and it will ALWAYS
|
|
null terminate the destination buffer (unless it is zero length).
|
|
|
|
This routine is not a replacement for strncpy. That function will pad the
|
|
destination string with extra null termination characters if the count is
|
|
greater than the length of the source string, and it will fail to null
|
|
terminate the destination string if the source string length is greater
|
|
than or equal to the count. You can not blindly use this instead of strncpy:
|
|
it is common for code to use it to "patch" strings and you would introduce
|
|
errors if the code started null terminating in the middle of the string.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was copied without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases as much of pszSrc
|
|
will be copied to pszDest as possible, and pszDest will be null terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to
|
|
hold all of the source including the null terminator
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCopy StringCbCopyW
|
|
#else
|
|
#define StringCbCopy StringCbCopyA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCWSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCopyEx(
|
|
__out_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcpy' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCchCopy, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be = (_tcslen(pszSrc) + 1) to hold all of
|
|
the source including the null terminator
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function copied any data, the result will point to the
|
|
null termination character
|
|
|
|
pcchRemaining - if pcchRemaining is non-null, the function will return the
|
|
number of characters left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
this flag is useful for emulating functions like lstrcpy
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCopyEx StringCchCopyExW
|
|
#else
|
|
#define StringCchCopyEx StringCchCopyExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyExA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCSTR pszSrc,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if (*pszSrc != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyExW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCWSTR pszSrc,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if (*pszSrc != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCopyEx(
|
|
__out_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcpy' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCbCopy, this routine also returns a pointer to the end of the
|
|
destination string and the number of bytes left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to
|
|
hold all of the source including the null terminator
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function copied any data, the result will point to the
|
|
null termination character
|
|
|
|
pcbRemaining - pcbRemaining is non-null,the function will return the
|
|
number of bytes left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
this flag is useful for emulating functions like lstrcpy
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCopyEx StringCbCopyExW
|
|
#else
|
|
#define StringCbCopyEx StringCbCopyExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyExA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCSTR pszSrc,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if (*pszSrc != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyExW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCWSTR pszSrc,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if (*pszSrc != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCopyN(
|
|
__out_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc,
|
|
__in size_t cchToCopy
|
|
);
|
|
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncpy'.
|
|
The size of the destination buffer (in characters) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This routine is meant as a replacement for strncpy, but it does behave
|
|
differently. This function will not pad the destination buffer with extra
|
|
null termination characters if cchToCopy is greater than the length of pszSrc.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the entire string or the first cchToCopy characters were copied
|
|
without truncation and the resultant destination string was null terminated,
|
|
otherwise it will return a failure code. In failure cases as much of pszSrc
|
|
will be copied to pszDest as possible, and pszDest will be null terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be = (_tcslen(src) + 1) to hold all of the
|
|
source including the null terminator
|
|
|
|
pszSrc - source string
|
|
|
|
cchToCopy - maximum number of characters to copy from source string,
|
|
not including the null terminator.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCchCopyNEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCopyN StringCchCopyNW
|
|
#else
|
|
#define StringCchCopyN StringCchCopyNA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyNA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToCopy) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cchToCopy)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (cchToCopy > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
*pszDest = '\0';
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
cchToCopy);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyNW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToCopy) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cchToCopy)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (cchToCopy > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
*pszDest = L'\0';
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
cchToCopy);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCopyN(
|
|
__out_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc,
|
|
__in size_t cbToCopy
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncpy'.
|
|
The size of the destination buffer (in bytes) is a parameter and this
|
|
function will not write past the end of this buffer and it will ALWAYS
|
|
null terminate the destination buffer (unless it is zero length).
|
|
|
|
This routine is meant as a replacement for strncpy, but it does behave
|
|
differently. This function will not pad the destination buffer with extra
|
|
null termination characters if cbToCopy is greater than the size of pszSrc.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the entire string or the first cbToCopy characters were
|
|
copied without truncation and the resultant destination string was null
|
|
terminated, otherwise it will return a failure code. In failure cases as
|
|
much of pszSrc will be copied to pszDest as possible, and pszDest will be
|
|
null terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be = ((_tcslen(src) + 1) * sizeof(TCHAR)) to
|
|
hold all of the source including the null terminator
|
|
|
|
pszSrc - source string
|
|
|
|
cbToCopy - maximum number of bytes to copy from source string,
|
|
not including the null terminator.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCbCopyEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCopyN StringCbCopyNW
|
|
#else
|
|
#define StringCbCopyN StringCbCopyNA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyNA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToCopy) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cbToCopy)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
size_t cchToCopy = cbToCopy / sizeof(char);
|
|
|
|
if (cchToCopy > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
*pszDest = '\0';
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
cchToCopy);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyNW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToCopy) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cbToCopy)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
size_t cchToCopy = cbToCopy / sizeof(wchar_t);
|
|
|
|
if (cchToCopy > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
// Suppress espx false positive - cchDest cannot be 0 here
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY)
|
|
*pszDest = L'\0';
|
|
#pragma warning(pop)
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszSrc,
|
|
cchToCopy);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCopyNEx(
|
|
__out_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__in size_t cchToCopy,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncpy' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCchCopyN, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination
|
|
string including the null terminator. The flags parameter allows
|
|
additional controls.
|
|
|
|
This routine is meant as a replacement for strncpy, but it does behave
|
|
differently. This function will not pad the destination buffer with extra
|
|
null termination characters if cchToCopy is greater than the length of pszSrc.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be = (_tcslen(pszSrc) + 1) to hold all of
|
|
the source including the null terminator
|
|
|
|
pszSrc - source string
|
|
|
|
cchToCopy - maximum number of characters to copy from the source
|
|
string
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function copied any data, the result will point to the
|
|
null termination character
|
|
|
|
pcchRemaining - if pcchRemaining is non-null, the function will return the
|
|
number of characters left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
this flag is useful for emulating functions like lstrcpy
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCopyNEx StringCchCopyNExW
|
|
#else
|
|
#define StringCchCopyNEx StringCchCopyNExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyNExA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToCopy) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cchToCopy,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, &cchToCopy, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if ((cchToCopy != 0) && (*pszSrc != '\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToCopy);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCopyNExW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToCopy) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cchToCopy,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszSrc, &cchToCopy, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if ((cchToCopy != 0) && (*pszSrc != L'\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToCopy);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCopyNEx(
|
|
__out_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__in size_t cbToCopy,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncpy' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCbCopyN, this routine also returns a pointer to the end of the
|
|
destination string and the number of bytes left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
This routine is meant as a replacement for strncpy, but it does behave
|
|
differently. This function will not pad the destination buffer with extra
|
|
null termination characters if cbToCopy is greater than the size of pszSrc.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be ((_tcslen(pszSrc) + 1) * sizeof(TCHAR)) to
|
|
hold all of the source including the null terminator
|
|
|
|
pszSrc - source string
|
|
|
|
cbToCopy - maximum number of bytes to copy from source string
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function copied any data, the result will point to the
|
|
null termination character
|
|
|
|
pcbRemaining - pcbRemaining is non-null,the function will return the
|
|
number of bytes left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
this flag is useful for emulating functions like lstrcpy
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all copied and the
|
|
resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the copy
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCopyNEx StringCbCopyNExW
|
|
#else
|
|
#define StringCbCopyNEx StringCbCopyNExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyNExA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToCopy) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cbToCopy,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
size_t cchToCopy = cbToCopy / sizeof(char);
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, &cchToCopy, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if ((cchToCopy != 0) && (*pszSrc != '\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToCopy);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCopyNExW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToCopy) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cbToCopy,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
size_t cchToCopy = cbToCopy / sizeof(wchar_t);
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY)
|
|
hr = StringExValidateSrcW(&pszSrc, &cchToCopy, STRSAFE_MAX_CCH, dwFlags);
|
|
#pragma warning(pop)
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually src data to copy
|
|
if ((cchToCopy != 0) && (*pszSrc != L'\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDest,
|
|
cchDest,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToCopy);
|
|
|
|
pszDestEnd = pszDest + cchCopied;
|
|
cchRemaining = cchDest - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCat(
|
|
__inout_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcat'.
|
|
The size of the destination buffer (in characters) is a parameter and this
|
|
function will not write past the end of this buffer and it will ALWAYS
|
|
null terminate the destination buffer (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was concatenated without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases as much of pszSrc
|
|
will be appended to pszDest as possible, and pszDest will be null
|
|
terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be = (_tcslen(pszDest) + _tcslen(pszSrc) + 1)
|
|
to hold all of the combine string plus the null
|
|
terminator
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCchCatEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all concatenated and
|
|
the resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error occurs,
|
|
the destination buffer is modified to contain a truncated
|
|
version of the ideal result and is null terminated. This
|
|
is useful for situations where truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCat StringCchCatW
|
|
#else
|
|
#define StringCchCat StringCchCatA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCatA(
|
|
__inout_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerA(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_CCH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCatW(
|
|
__inout_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCWSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerW(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_CCH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCat(
|
|
__inout_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcat'.
|
|
The size of the destination buffer (in bytes) is a parameter and this
|
|
function will not write past the end of this buffer and it will ALWAYS
|
|
null terminate the destination buffer (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was concatenated without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases as much of pszSrc
|
|
will be appended to pszDest as possible, and pszDest will be null
|
|
terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be = ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR)
|
|
to hold all of the combine string plus the null
|
|
terminator
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCbCatEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all concatenated and
|
|
the resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error occurs,
|
|
the destination buffer is modified to contain a truncated
|
|
version of the ideal result and is null terminated. This
|
|
is useful for situations where truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCat StringCbCatW
|
|
#else
|
|
#define StringCbCat StringCbCatA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCatA(
|
|
__inout_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerA(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_CCH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCatW(
|
|
__inout_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCWSTR pszSrc)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringCopyWorkerW(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
STRSAFE_MAX_CCH);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCatEx(
|
|
__inout_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcat' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCchCat, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cchDest - size of destination buffer in characters
|
|
length must be (_tcslen(pszDest) + _tcslen(pszSrc) + 1)
|
|
to hold all of the combine string plus the null
|
|
terminator.
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function appended any data, the result will point to the
|
|
null termination character
|
|
|
|
pcchRemaining - if pcchRemaining is non-null, the function will return the
|
|
number of characters left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
this flag is useful for emulating functions like lstrcat
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any pre-existing
|
|
or truncated string
|
|
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any pre-existing or
|
|
truncated string
|
|
|
|
STRSAFE_NO_TRUNCATION
|
|
if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
|
|
will not contain a truncated string, it will remain unchanged.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all concatenated and
|
|
the resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error
|
|
occurs, the destination buffer is modified to contain
|
|
a truncated version of the ideal result and is null
|
|
terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCatEx StringCchCatExW
|
|
#else
|
|
#define StringCchCatEx StringCchCatExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCatExA(
|
|
__inout_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCSTR pszSrc,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if (*pszSrc != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCatExW(
|
|
__inout_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in STRSAFE_LPCWSTR pszSrc,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
|
|
hr = StringExValidateSrcW(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if (*pszSrc != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCatEx(
|
|
__inout_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strcat' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCbCat, this routine also returns a pointer to the end of the
|
|
destination string and the number of bytes left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be ((_tcslen(pszDest) + _tcslen(pszSrc) + 1) * sizeof(TCHAR)
|
|
to hold all of the combine string plus the null
|
|
terminator.
|
|
|
|
pszSrc - source string which must be null terminated
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function appended any data, the result will point to the
|
|
null termination character
|
|
|
|
pcbRemaining - if pcbRemaining is non-null, the function will return
|
|
the number of bytes left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
this flag is useful for emulating functions like lstrcat
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any pre-existing
|
|
or truncated string
|
|
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any pre-existing or
|
|
truncated string
|
|
|
|
STRSAFE_NO_TRUNCATION
|
|
if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
|
|
will not contain a truncated string, it will remain unchanged.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all concatenated
|
|
and the resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error
|
|
occurs, the destination buffer is modified to contain
|
|
a truncated version of the ideal result and is null
|
|
terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCatEx StringCbCatExW
|
|
#else
|
|
#define StringCbCatEx StringCbCatExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCatExA(
|
|
__inout_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCSTR pszSrc,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if (*pszSrc != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCatExW(
|
|
__inout_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in STRSAFE_LPCWSTR pszSrc,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
|
|
hr = StringExValidateSrcW(&pszSrc, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if (*pszSrc != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
STRSAFE_MAX_LENGTH);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCatN(
|
|
__inout_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc,
|
|
__in size_t cchToAppend
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncat'.
|
|
The size of the destination buffer (in characters) is a parameter as well as
|
|
the maximum number of characters to append, excluding the null terminator.
|
|
This function will not write past the end of the destination buffer and it will
|
|
ALWAYS null terminate pszDest (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if all of pszSrc or the first cchToAppend characters were appended
|
|
to the destination string and it was null terminated, otherwise it will
|
|
return a failure code. In failure cases as much of pszSrc will be appended
|
|
to pszDest as possible, and pszDest will be null terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be (_tcslen(pszDest) + min(cchToAppend, _tcslen(pszSrc)) + 1)
|
|
to hold all of the combine string plus the null
|
|
terminator.
|
|
|
|
pszSrc - source string
|
|
|
|
cchToAppend - maximum number of characters to append
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCchCatNEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if all of pszSrc or the first cchToAppend characters
|
|
were concatenated to pszDest and the resultant dest
|
|
string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error
|
|
occurs, the destination buffer is modified to contain
|
|
a truncated version of the ideal result and is null
|
|
terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCatN StringCchCatNW
|
|
#else
|
|
#define StringCchCatN StringCchCatNA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCatNA(
|
|
__inout_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToAppend) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cchToAppend)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (cchToAppend > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerA(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
cchToAppend);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCatNW(
|
|
__inout_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToAppend) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cchToAppend)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (cchToAppend > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerW(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
cchToAppend);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCatN(
|
|
__inout_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc,
|
|
__in size_t cbToAppend
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncat'.
|
|
The size of the destination buffer (in bytes) is a parameter as well as
|
|
the maximum number of bytes to append, excluding the null terminator.
|
|
This function will not write past the end of the destination buffer and it will
|
|
ALWAYS null terminate pszDest (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if all of pszSrc or the first cbToAppend bytes were appended
|
|
to the destination string and it was null terminated, otherwise it will
|
|
return a failure code. In failure cases as much of pszSrc will be appended
|
|
to pszDest as possible, and pszDest will be null terminated.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be ((_tcslen(pszDest) + min(cbToAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
|
|
to hold all of the combine string plus the null
|
|
terminator.
|
|
|
|
pszSrc - source string
|
|
|
|
cbToAppend - maximum number of bytes to append
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL. See StringCbCatNEx if you require
|
|
the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if all of pszSrc or the first cbToAppend bytes were
|
|
concatenated to pszDest and the resultant dest string
|
|
was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error
|
|
occurs, the destination buffer is modified to contain
|
|
a truncated version of the ideal result and is null
|
|
terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCatN StringCbCatNW
|
|
#else
|
|
#define StringCbCatN StringCbCatNA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCatNA(
|
|
__inout_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToAppend) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cbToAppend)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
size_t cchDestLength;
|
|
|
|
hr = StringValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
size_t cchToAppend = cbToAppend / sizeof(char);
|
|
|
|
if (cchToAppend > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerA(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
cchToAppend);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCatNW(
|
|
__inout_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToAppend) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cbToAppend)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
size_t cchDestLength;
|
|
|
|
hr = StringValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
size_t cchToAppend = cbToAppend / sizeof(wchar_t);
|
|
|
|
if (cchToAppend > STRSAFE_MAX_LENGTH)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringCopyWorkerW(pszDest + cchDestLength,
|
|
cchDest - cchDestLength,
|
|
NULL,
|
|
pszSrc,
|
|
cchToAppend);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchCatNEx(
|
|
__inout_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__in size_t cchToAppend,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncat', with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCchCatN, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be (_tcslen(pszDest) + min(cchToAppend, _tcslen(pszSrc)) + 1)
|
|
to hold all of the combine string plus the null
|
|
terminator.
|
|
|
|
pszSrc - source string
|
|
|
|
cchToAppend - maximum number of characters to append
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function appended any data, the result will point to the
|
|
null termination character
|
|
|
|
pcchRemaining - if pcchRemaining is non-null, the function will return the
|
|
number of characters left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT(""))
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any pre-existing
|
|
or truncated string
|
|
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any pre-existing or
|
|
truncated string
|
|
|
|
STRSAFE_NO_TRUNCATION
|
|
if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
|
|
will not contain a truncated string, it will remain unchanged.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if all of pszSrc or the first cchToAppend characters
|
|
were concatenated to pszDest and the resultant dest
|
|
string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error
|
|
occurs, the destination buffer is modified to contain
|
|
a truncated version of the ideal result and is null
|
|
terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchCatNEx StringCchCatNExW
|
|
#else
|
|
#define StringCchCatNEx StringCchCatNExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchCatNExA(
|
|
__inout_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToAppend) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cchToAppend,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, &cchToAppend, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if ((cchToAppend != 0) && (*pszSrc != '\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToAppend);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchCatNExW(
|
|
__inout_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in_ecount(cchToAppend) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cchToAppend,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
|
|
hr = StringExValidateSrcW(&pszSrc, &cchToAppend, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if ((cchToAppend != 0) && (*pszSrc != L'\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToAppend);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbCatNEx(
|
|
__inout_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__in LPCTSTR pszSrc OPTIONAL,
|
|
__in size_t cbToAppend,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strncat', with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCbCatN, this routine also returns a pointer to the end of the
|
|
destination string and the number of bytes left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string which must be null terminated
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be ((_tcslen(pszDest) + min(cbToAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
|
|
to hold all of the combine string plus the null
|
|
terminator.
|
|
|
|
pszSrc - source string
|
|
|
|
cbToAppend - maximum number of bytes to append
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function appended any data, the result will point to the
|
|
null termination character
|
|
|
|
pcbRemaining - if pcbRemaining is non-null, the function will return the
|
|
number of bytes left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT(""))
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any pre-existing
|
|
or truncated string
|
|
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any pre-existing or
|
|
truncated string
|
|
|
|
STRSAFE_NO_TRUNCATION
|
|
if the function returns STRSAFE_E_INSUFFICIENT_BUFFER, pszDest
|
|
will not contain a truncated string, it will remain unchanged.
|
|
|
|
Notes:
|
|
Behavior is undefined if source and destination strings overlap.
|
|
|
|
pszDest and pszSrc should not be NULL unless the STRSAFE_IGNORE_NULLS flag
|
|
is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and pszSrc
|
|
may be NULL. An error may still be returned even though NULLS are ignored
|
|
due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if all of pszSrc or the first cbToAppend bytes were
|
|
concatenated to pszDest and the resultant dest string
|
|
was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the operation
|
|
failed due to insufficient space. When this error
|
|
occurs, the destination buffer is modified to contain
|
|
a truncated version of the ideal result and is null
|
|
terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbCatNEx StringCbCatNExW
|
|
#else
|
|
#define StringCbCatNEx StringCbCatNExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbCatNExA(
|
|
__inout_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToAppend) STRSAFE_PCNZCH pszSrc,
|
|
__in size_t cbToAppend,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
size_t cchToAppend = cbToAppend / sizeof(char);
|
|
|
|
hr = StringExValidateSrcA(&pszSrc, &cchToAppend, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if ((cchToAppend != 0) && (*pszSrc != '\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerA(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToAppend);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbCatNExW(
|
|
__inout_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in_bcount(cbToAppend) STRSAFE_PCNZWCH pszSrc,
|
|
__in size_t cbToAppend,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
size_t cchDestLength;
|
|
|
|
hr = StringExValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
&cchDestLength,
|
|
STRSAFE_MAX_CCH,
|
|
dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest + cchDestLength;
|
|
size_t cchRemaining = cchDest - cchDestLength;
|
|
size_t cchToAppend = cbToAppend / sizeof(wchar_t);
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY)
|
|
hr = StringExValidateSrcW(&pszSrc, &cchToAppend, STRSAFE_MAX_CCH, dwFlags);
|
|
#pragma warning(pop)
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if (cchRemaining <= 1)
|
|
{
|
|
// only fail if there was actually src data to append
|
|
if ((cchToAppend != 0) && (*pszSrc != L'\0'))
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchCopied = 0;
|
|
|
|
hr = StringCopyWorkerW(pszDestEnd,
|
|
cchRemaining,
|
|
&cchCopied,
|
|
pszSrc,
|
|
cchToAppend);
|
|
|
|
pszDestEnd = pszDestEnd + cchCopied;
|
|
cchRemaining = cchRemaining - cchCopied;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
cchDestLength,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchVPrintf(
|
|
__out_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
__in va_list argList
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'vsprintf'.
|
|
The size of the destination buffer (in characters) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was printed without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases it will return
|
|
a truncated version of the ideal result.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters
|
|
length must be sufficient to hold the resulting formatted
|
|
string, including the null terminator.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
argList - va_list from the variable arguments according to the
|
|
stdarg.h convention
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL. See StringCchVPrintfEx if you
|
|
require the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was sufficient space in the dest buffer for
|
|
the resultant string and it was null terminated.
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchVPrintf StringCchVPrintfW
|
|
#else
|
|
#define StringCchVPrintf StringCchVPrintfA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintfA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintfW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CCH_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchVPrintf_l(
|
|
__out_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
__in locale_t locale,
|
|
__in va_list argList
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCchVPrintf that also takes a locale.
|
|
Please see notes for StringCchVPrintf above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchVPrintf_l StringCchVPrintf_lW
|
|
#else
|
|
#define StringCchVPrintf_l StringCchVPrintf_lA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintf_lA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintf_lW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbVPrintf(
|
|
__out_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
__in va_list argList
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'vsprintf'.
|
|
The size of the destination buffer (in bytes) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was printed without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases it will return
|
|
a truncated version of the ideal result.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes
|
|
length must be sufficient to hold the resulting formatted
|
|
string, including the null terminator.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
argList - va_list from the variable arguments according to the
|
|
stdarg.h convention
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL. See StringCbVPrintfEx if you
|
|
require the handling of NULL values.
|
|
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was sufficient space in the dest buffer for
|
|
the resultant string and it was null terminated.
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbVPrintf StringCbVPrintfW
|
|
#else
|
|
#define StringCbVPrintf StringCbVPrintfA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintfA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintfW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CB_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbVPrintf_l(
|
|
__out_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
__in local_t locale,
|
|
__in va_list argList
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCbVPrintf that also takes a locale.
|
|
Please see notes for StringCbVPrintf above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbVPrintf_l StringCbVPrintf_lW
|
|
#else
|
|
#define StringCbVPrintf_l StringCbVPrintf_lA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintf_lA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintf_lW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef _M_CEE_PURE
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchPrintf(
|
|
__out_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'sprintf'.
|
|
The size of the destination buffer (in characters) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was printed without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases it will return
|
|
a truncated version of the ideal result.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters
|
|
length must be sufficient to hold the resulting formatted
|
|
string, including the null terminator.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
... - additional parameters to be formatted according to
|
|
the format string
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL. See StringCchPrintfEx if you
|
|
require the handling of NULL values.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was sufficient space in the dest buffer for
|
|
the resultant string and it was null terminated.
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchPrintf StringCchPrintfW
|
|
#else
|
|
#define StringCchPrintf StringCchPrintfA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintfA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintfW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CCH_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchPrintf_l(
|
|
__out_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
__in locale_t locale,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of a StringCchPrintf_l that also takes a locale.
|
|
Please see notes for StringCchPrintf above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchPrintf_l StringCchPrintf_lW
|
|
#else
|
|
#define StringCchPrintf_l StringCchPrintf_lA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintf_lA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintf_lW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbPrintf(
|
|
__out_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'sprintf'.
|
|
The size of the destination buffer (in bytes) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string was printed without truncation and null terminated,
|
|
otherwise it will return a failure code. In failure cases it will return
|
|
a truncated version of the ideal result.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes
|
|
length must be sufficient to hold the resulting formatted
|
|
string, including the null terminator.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
... - additional parameters to be formatted according to
|
|
the format string
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL. See StringCbPrintfEx if you
|
|
require the handling of NULL values.
|
|
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was sufficient space in the dest buffer for
|
|
the resultant string and it was null terminated.
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbPrintf StringCbPrintfW
|
|
#else
|
|
#define StringCbPrintf StringCbPrintfA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintfA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintfW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CB_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbPrintf_l(
|
|
__out_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string LPCTSTR pszFormat,
|
|
__in locale_t locale,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCbPrintf that also takes a locale.
|
|
Please see notes for StringCbPrintf above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbPrintf_l StringCbPrintf_lW
|
|
#else
|
|
#define StringCbPrintf_l StringCbPrintf_lA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintf_lA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintf_lW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
NULL,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchPrintfEx(
|
|
__out_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'sprintf' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCchPrintf, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be sufficient to contain the resulting
|
|
formatted string plus the null terminator.
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function printed any data, the result will point to the
|
|
null termination character
|
|
|
|
pcchRemaining - if pcchRemaining is non-null, the function will return
|
|
the number of characters left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT(""))
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
... - additional parameters to be formatted according to
|
|
the format string
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
|
|
flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
|
|
pszFormat may be NULL. An error may still be returned even though NULLS
|
|
are ignored due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was sufficient space in the dest buffer for
|
|
the resultant string and it was null terminated.
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchPrintfEx StringCchPrintfExW
|
|
#else
|
|
#define StringCchPrintfEx StringCchPrintfExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintfExA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintfExW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CCH_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchPrintf_lEx(
|
|
__out_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
__in locale_t locale,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCchPrintfEx that also takes a locale.
|
|
Please see notes for StringCchPrintfEx above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchPrintf_lEx StringCchPrintf_lExW
|
|
#else
|
|
#define StringCchPrintf_lEx StringCchPrintf_lExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintf_lExA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchPrintf_lExW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbPrintfEx(
|
|
__out_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'sprintf' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCbPrintf, this routine also returns a pointer to the end of the
|
|
destination string and the number of bytes left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be sufficient to contain the resulting
|
|
formatted string plus the null terminator.
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function printed any data, the result will point to the
|
|
null termination character
|
|
|
|
pcbRemaining - if pcbRemaining is non-null, the function will return
|
|
the number of bytes left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT(""))
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
... - additional parameters to be formatted according to
|
|
the format string
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
|
|
flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
|
|
pszFormat may be NULL. An error may still be returned even though NULLS
|
|
are ignored due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all concatenated and
|
|
the resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbPrintfEx StringCbPrintfExW
|
|
#else
|
|
#define StringCbPrintfEx StringCbPrintfExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintfExA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintfExW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, pszFormat);
|
|
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CB_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbPrintf_lEx(
|
|
__out_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
__in locale_t locale,
|
|
...
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCbPrintfEx that also takes a locale.
|
|
Please seee notes for StringCbPrintfEx above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbPrintf_lEx StringCbPrintf_lExW
|
|
#else
|
|
#define StringCbPrintf_lEx StringCbPrintf_lExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintf_lExA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbPrintf_lExW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
...)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
va_list argList;
|
|
|
|
va_start(argList, locale);
|
|
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
va_end(argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
#endif // !_M_CEE_PURE
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchVPrintfEx(
|
|
__out_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
__in va_list argList
|
|
);
|
|
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'vsprintf' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCchVPrintf, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
length must be sufficient to contain the resulting
|
|
formatted string plus the null terminator.
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function printed any data, the result will point to the
|
|
null termination character
|
|
|
|
pcchRemaining - if pcchRemaining is non-null, the function will return
|
|
the number of characters left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT(""))
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
argList - va_list from the variable arguments according to the
|
|
stdarg.h convention
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
|
|
flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
|
|
pszFormat may be NULL. An error may still be returned even though NULLS
|
|
are ignored due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all concatenated and
|
|
the resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchVPrintfEx StringCchVPrintfExW
|
|
#else
|
|
#define StringCchVPrintfEx StringCchVPrintfExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintfExA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintfExW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CCH_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchVPrintf_lEx(
|
|
__out_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
__in locale_t locale,
|
|
__in va_list argList
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCchVPrintfEx that also takes a locale.
|
|
Please see notes for StringCchVPrintfEx above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchVPrintf_lEx StringCchVPrintf_lExW
|
|
#else
|
|
#define StringCchVPrintf_lEx StringCchVPrintf_lExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintf_lExA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchVPrintf_lExW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbVPrintfEx(
|
|
__out_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
__in va_list argList
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'vsprintf' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCbVPrintf, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
length must be sufficient to contain the resulting
|
|
formatted string plus the null terminator.
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return
|
|
a pointer to the end of the destination string. If the
|
|
function printed any data, the result will point to the
|
|
null termination character
|
|
|
|
pcbRemaining - if pcbRemaining is non-null, the function will return
|
|
the number of bytes left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT(""))
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated. This will overwrite any truncated
|
|
string returned when the failure is
|
|
STRSAFE_E_INSUFFICIENT_BUFFER
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string. This will overwrite any truncated string
|
|
returned when the failure is STRSAFE_E_INSUFFICIENT_BUFFER.
|
|
|
|
pszFormat - format string which must be null terminated
|
|
|
|
argList - va_list from the variable arguments according to the
|
|
stdarg.h convention
|
|
|
|
Notes:
|
|
Behavior is undefined if destination, format strings or any arguments
|
|
strings overlap.
|
|
|
|
pszDest and pszFormat should not be NULL unless the STRSAFE_IGNORE_NULLS
|
|
flag is specified. If STRSAFE_IGNORE_NULLS is passed, both pszDest and
|
|
pszFormat may be NULL. An error may still be returned even though NULLS
|
|
are ignored due to insufficient space.
|
|
|
|
Return Value:
|
|
|
|
S_OK - if there was source data and it was all concatenated and
|
|
the resultant dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that the print
|
|
operation failed due to insufficient space. When this
|
|
error occurs, the destination buffer is modified to
|
|
contain a truncated version of the ideal result and is
|
|
null terminated. This is useful for situations where
|
|
truncation is ok.
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbVPrintfEx StringCbVPrintfExW
|
|
#else
|
|
#define StringCbVPrintfEx StringCbVPrintfExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintfExA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintfWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintfExW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintfWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#if defined(STRSAFE_LOCALE_FUNCTIONS) && !defined(STRSAFE_NO_CB_FUNCTIONS)
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbVPrintf_lEx(
|
|
__out_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags,
|
|
__in __format_string LPCTSTR pszFormat OPTIONAL,
|
|
__in locale_t locale,
|
|
__in va_list argList
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCbVPrintfEx that also takes a locale.
|
|
Please see notes for StringCbVPrintfEx above.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbVPrintf_lEx StringCbVPrintf_lExW
|
|
#else
|
|
#define StringCbVPrintf_lEx StringCbVPrintf_lExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintf_lExA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcA(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != '\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintf_lWorkerA(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbVPrintf_lExW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
hr = StringExValidateSrcW(&pszFormat, NULL, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
// only fail if there was actually a non-empty format string
|
|
if (*pszFormat != L'\0')
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringVPrintf_lWorkerW(pszDest,
|
|
cchDest,
|
|
&cchNewDestLength,
|
|
pszFormat,
|
|
locale,
|
|
argList);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS && !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_LIB_IMPL
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchGets(
|
|
__out_ecount(cchDest) LPTSTR pszDest,
|
|
__in size_t cchDest
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'gets'.
|
|
The size of the destination buffer (in characters) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This routine is not a replacement for fgets. That function does not replace
|
|
newline characters with a null terminator.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if any characters were read from stdin and copied to pszDest and
|
|
pszDest was null terminated, otherwise it will return a failure code.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
|
|
Notes:
|
|
pszDest should not be NULL. See StringCchGetsEx if you require the handling
|
|
of NULL values.
|
|
|
|
cchDest must be > 1 for this function to succeed.
|
|
|
|
Return Value:
|
|
|
|
S_OK - data was read from stdin and copied, and the resultant
|
|
dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_END_OF_FILE /
|
|
HRESULT_CODE(hr) == ERROR_HANDLE_EOF
|
|
- this return value indicates an error or end-of-file
|
|
condition, use feof or ferror to determine which one has
|
|
occured.
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that there was
|
|
insufficient space in the destination buffer to copy any
|
|
data
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchGets StringCchGetsW
|
|
#else
|
|
#define StringCchGets StringCchGetsA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchGetsA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringGetsWorkerA(pszDest, cchDest, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchGetsW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringGetsWorkerW(pszDest, cchDest, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbGets(
|
|
__out_bcount(cbDest) LPTSTR pszDest,
|
|
__in size_t cbDest
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'gets'.
|
|
The size of the destination buffer (in bytes) is a parameter and
|
|
this function will not write past the end of this buffer and it will
|
|
ALWAYS null terminate the destination buffer (unless it is zero length).
|
|
|
|
This routine is not a replacement for fgets. That function does not replace
|
|
newline characters with a null terminator.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if any characters were read from stdin and copied to pszDest
|
|
and pszDest was null terminated, otherwise it will return a failure code.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
|
|
Notes:
|
|
pszDest should not be NULL. See StringCbGetsEx if you require the handling
|
|
of NULL values.
|
|
|
|
cbDest must be > sizeof(TCHAR) for this function to succeed.
|
|
|
|
Return Value:
|
|
|
|
S_OK - data was read from stdin and copied, and the resultant
|
|
dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_END_OF_FILE /
|
|
HRESULT_CODE(hr) == ERROR_HANDLE_EOF
|
|
- this return value indicates an error or end-of-file
|
|
condition, use feof or ferror to determine which one has
|
|
occured.
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that there was
|
|
insufficient space in the destination buffer to copy any
|
|
data
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbGets StringCbGetsW
|
|
#else
|
|
#define StringCbGets StringCbGetsA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbGetsA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringGetsWorkerA(pszDest, cchDest, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbGetsW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringGetsWorkerW(pszDest, cchDest, NULL);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchGetsEx(
|
|
__out_ecount(cchDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcchRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'gets' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCchGets, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cchDest - size of destination buffer in characters.
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function copied any data, the result will point to the
|
|
null termination character
|
|
|
|
pcchRemaining - if pcchRemaining is non-null, the function will return the
|
|
number of characters left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated.
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string.
|
|
|
|
Notes:
|
|
pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified.
|
|
If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be
|
|
returned even though NULLS are ignored
|
|
|
|
cchDest must be > 1 for this function to succeed.
|
|
|
|
Return Value:
|
|
|
|
S_OK - data was read from stdin and copied, and the resultant
|
|
dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_END_OF_FILE /
|
|
HRESULT_CODE(hr) == ERROR_HANDLE_EOF
|
|
- this return value indicates an error or end-of-file
|
|
condition, use feof or ferror to determine which one has
|
|
occured.
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that there was
|
|
insufficient space in the destination buffer to copy any
|
|
data
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchGetsEx StringCchGetsExW
|
|
#else
|
|
#define StringCchGetsEx StringCchGetsExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCchGetsExA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringGetsWorkerA(pszDest, cchDest, &cchNewDestLength);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = cchRemaining * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbDest = cchDest * sizeof(char);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) ||
|
|
(hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
|
|
(hr == STRSAFE_E_END_OF_FILE))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCchGetsExW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__deref_opt_out_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringGetsWorkerW(pszDest, cchDest, &cchNewDestLength);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = cchRemaining * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cchDest != 0))
|
|
{
|
|
size_t cbDest;
|
|
|
|
// safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbDest = cchDest * sizeof(wchar_t);
|
|
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) ||
|
|
(hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
|
|
(hr == STRSAFE_E_END_OF_FILE))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcchRemaining)
|
|
{
|
|
*pcchRemaining = cchRemaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbGetsEx(
|
|
__out_bcount(cbDest) LPTSTR pszDest OPTIONAL,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) LPTSTR* ppszDestEnd OPTIONAL,
|
|
__out_opt size_t* pcbRemaining OPTIONAL,
|
|
__in DWORD dwFlags
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'gets' with
|
|
some additional parameters. In addition to functionality provided by
|
|
StringCbGets, this routine also returns a pointer to the end of the
|
|
destination string and the number of characters left in the destination string
|
|
including the null terminator. The flags parameter allows additional controls.
|
|
|
|
Arguments:
|
|
|
|
pszDest - destination string
|
|
|
|
cbDest - size of destination buffer in bytes.
|
|
|
|
ppszDestEnd - if ppszDestEnd is non-null, the function will return a
|
|
pointer to the end of the destination string. If the
|
|
function copied any data, the result will point to the
|
|
null termination character
|
|
|
|
pcbRemaining - if pcbRemaining is non-null, the function will return the
|
|
number of bytes left in the destination string,
|
|
including the null terminator
|
|
|
|
dwFlags - controls some details of the string copy:
|
|
|
|
STRSAFE_FILL_BEHIND_NULL
|
|
if the function succeeds, the low byte of dwFlags will be
|
|
used to fill the uninitialize part of destination buffer
|
|
behind the null terminator
|
|
|
|
STRSAFE_IGNORE_NULLS
|
|
treat NULL string pointers like empty strings (TEXT("")).
|
|
|
|
STRSAFE_FILL_ON_FAILURE
|
|
if the function fails, the low byte of dwFlags will be
|
|
used to fill all of the destination buffer, and it will
|
|
be null terminated.
|
|
|
|
STRSAFE_NO_TRUNCATION /
|
|
STRSAFE_NULL_ON_FAILURE
|
|
if the function fails, the destination buffer will be set
|
|
to the empty string.
|
|
|
|
Notes:
|
|
pszDest should not be NULL unless the STRSAFE_IGNORE_NULLS flag is specified.
|
|
If STRSAFE_IGNORE_NULLS is passed and pszDest is NULL, an error may still be
|
|
returned even though NULLS are ignored
|
|
|
|
cbDest must be > sizeof(TCHAR) for this function to succeed
|
|
|
|
Return Value:
|
|
|
|
S_OK - data was read from stdin and copied, and the resultant
|
|
dest string was null terminated
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
STRSAFE_E_END_OF_FILE /
|
|
HRESULT_CODE(hr) == ERROR_HANDLE_EOF
|
|
- this return value indicates an error or end-of-file
|
|
condition, use feof or ferror to determine which one has
|
|
occured.
|
|
|
|
STRSAFE_E_INSUFFICIENT_BUFFER /
|
|
HRESULT_CODE(hr) == ERROR_INSUFFICIENT_BUFFER
|
|
- this return value is an indication that there was
|
|
insufficient space in the destination buffer to copy any
|
|
data
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbGetsEx StringCbGetsExW
|
|
#else
|
|
#define StringCbGetsEx StringCbGetsExA
|
|
#endif // !UNICODE
|
|
|
|
STRSAFEAPI
|
|
StringCbGetsExA(
|
|
__out_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
hr = StringExValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = '\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringGetsWorkerA(pszDest, cchDest, &cchNewDestLength);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
(dwFlags & STRSAFE_FILL_BEHIND_NULL) &&
|
|
(cchRemaining > 1))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
cbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullA(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsA(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) ||
|
|
(hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
|
|
(hr == STRSAFE_E_END_OF_FILE))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(char) since cchRemaining < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbRemaining = (cchRemaining * sizeof(char)) + (cbDest % sizeof(char));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringCbGetsExW(
|
|
__out_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in size_t cbDest,
|
|
__deref_opt_out_bcount(*pcbRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out_opt size_t* pcbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
hr = StringExValidateDestW(pszDest, cchDest, STRSAFE_MAX_CCH, dwFlags);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
STRSAFE_LPWSTR pszDestEnd = pszDest;
|
|
size_t cchRemaining = cchDest;
|
|
|
|
if (dwFlags & (~STRSAFE_VALID_FLAGS))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
|
|
if (cchDest != 0)
|
|
{
|
|
*pszDest = L'\0';
|
|
}
|
|
}
|
|
else if (cchDest == 0)
|
|
{
|
|
if (pszDest == NULL)
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cchNewDestLength = 0;
|
|
|
|
hr = StringGetsWorkerW(pszDest, cchDest, &cchNewDestLength);
|
|
|
|
pszDestEnd = pszDest + cchNewDestLength;
|
|
cchRemaining = cchDest - cchNewDestLength;
|
|
|
|
if (SUCCEEDED(hr) && (dwFlags & STRSAFE_FILL_BEHIND_NULL))
|
|
{
|
|
size_t cbRemaining;
|
|
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
cbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
|
|
// handle the STRSAFE_FILL_BEHIND_NULL flag
|
|
StringExHandleFillBehindNullW(pszDestEnd, cbRemaining, dwFlags);
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) &&
|
|
(dwFlags & (STRSAFE_NO_TRUNCATION | STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)) &&
|
|
(cbDest != 0))
|
|
{
|
|
// handle the STRSAFE_FILL_ON_FAILURE, STRSAFE_NULL_ON_FAILURE, and STRSAFE_NO_TRUNCATION flags
|
|
StringExHandleOtherFlagsW(pszDest,
|
|
cbDest,
|
|
0,
|
|
&pszDestEnd,
|
|
&cchRemaining,
|
|
dwFlags);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) ||
|
|
(hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
|
|
(hr == STRSAFE_E_END_OF_FILE))
|
|
{
|
|
if (ppszDestEnd)
|
|
{
|
|
*ppszDestEnd = pszDestEnd;
|
|
}
|
|
|
|
if (pcbRemaining)
|
|
{
|
|
// safe to multiply cchRemaining * sizeof(wchar_t) since cchRemaining < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbRemaining = (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t));
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
#endif // !STRSAFE_LIB_IMPL
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCchLength(
|
|
__in LPCTSTR psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength OPTIONAL
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strlen'.
|
|
It is used to make sure a string is not larger than a given length, and
|
|
it optionally returns the current length in characters not including
|
|
the null terminator.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string is non-null and the length including the null
|
|
terminator is less than or equal to cchMax characters.
|
|
|
|
Arguments:
|
|
|
|
psz - string to check the length of
|
|
|
|
cchMax - maximum number of characters including the null terminator
|
|
that psz is allowed to contain
|
|
|
|
pcch - if the function succeeds and pcch is non-null, the current length
|
|
in characters of psz excluding the null terminator will be returned.
|
|
This out parameter is equivalent to the return value of strlen(psz)
|
|
|
|
Notes:
|
|
psz can be null but the function will fail
|
|
|
|
cchMax should be greater than zero or the function will fail
|
|
|
|
Return Value:
|
|
|
|
S_OK - psz is non-null and the length including the null
|
|
terminator is less than or equal to cchMax characters
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCchLength StringCchLengthW
|
|
#else
|
|
#define StringCchLength StringCchLengthA
|
|
#endif // !UNICODE
|
|
|
|
__checkReturn
|
|
STRSAFEAPI
|
|
StringCchLengthA(
|
|
__in STRSAFE_PCNZCH psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringLengthWorkerA(psz, cchMax, pcchLength);
|
|
}
|
|
|
|
if (FAILED(hr) && pcchLength)
|
|
{
|
|
*pcchLength = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
__checkReturn
|
|
STRSAFEAPI
|
|
StringCchLengthW(
|
|
__in STRSAFE_PCNZWCH psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringLengthWorkerW(psz, cchMax, pcchLength);
|
|
}
|
|
|
|
if (FAILED(hr) && pcchLength)
|
|
{
|
|
*pcchLength = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
StringCbLength(
|
|
__in LPCTSTR psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH * sizeof(TCHAR)) size_t cbMax,
|
|
__out_opt __deref_out_range(<, cbMax) size_t* pcbLength OPTIONAL
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a safer version of the C built-in function 'strlen'.
|
|
It is used to make sure a string is not larger than a given length, and
|
|
it optionally returns the current length in bytes not including
|
|
the null terminator.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string is non-null and the length including the null
|
|
terminator is less than or equal to cbMax bytes.
|
|
|
|
Arguments:
|
|
|
|
psz - string to check the length of
|
|
|
|
cbMax - maximum number of bytes including the null terminator
|
|
that psz is allowed to contain
|
|
|
|
pcb - if the function succeeds and pcb is non-null, the current length
|
|
in bytes of psz excluding the null terminator will be returned.
|
|
This out parameter is equivalent to the return value of strlen(psz) * sizeof(TCHAR)
|
|
|
|
Notes:
|
|
psz can be null but the function will fail
|
|
|
|
cbMax should be greater than or equal to sizeof(TCHAR) or the function will fail
|
|
|
|
Return Value:
|
|
|
|
S_OK - psz is non-null and the length including the null
|
|
terminator is less than or equal to cbMax bytes
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define StringCbLength StringCbLengthW
|
|
#else
|
|
#define StringCbLength StringCbLengthA
|
|
#endif // !UNICODE
|
|
|
|
__checkReturn
|
|
STRSAFEAPI
|
|
StringCbLengthA(
|
|
__in STRSAFE_PCNZCH psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH * sizeof(char)) size_t cbMax,
|
|
__out_opt __deref_out_range(<, cbMax) size_t* pcbLength)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchMax = cbMax / sizeof(char);
|
|
size_t cchLength = 0;
|
|
|
|
if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringLengthWorkerA(psz, cchMax, &cchLength);
|
|
}
|
|
|
|
if (pcbLength)
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// safe to multiply cchLength * sizeof(char) since cchLength < STRSAFE_MAX_CCH and sizeof(char) is 1
|
|
*pcbLength = cchLength * sizeof(char);
|
|
}
|
|
else
|
|
{
|
|
*pcbLength = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
__checkReturn
|
|
STRSAFEAPI
|
|
StringCbLengthW(
|
|
__in STRSAFE_PCNZWCH psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH * sizeof(wchar_t)) size_t cbMax,
|
|
__out_opt __deref_out_range(<, cbMax - 1) size_t* pcbLength)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchMax = cbMax / sizeof(wchar_t);
|
|
size_t cchLength = 0;
|
|
|
|
if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = StringLengthWorkerW(psz, cchMax, &cchLength);
|
|
}
|
|
|
|
if (pcbLength)
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// safe to multiply cchLength * sizeof(wchar_t) since cchLength < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbLength = cchLength * sizeof(wchar_t);
|
|
}
|
|
else
|
|
{
|
|
*pcbLength = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
#ifndef STRSAFE_NO_CCH_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
UnalignedStringCchLength(
|
|
__in LPCUTSTR psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength OPTIONAL
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCchLength that accepts an unaligned string pointer.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string is non-null and the length including the null
|
|
terminator is less than or equal to cchMax characters.
|
|
|
|
Arguments:
|
|
|
|
psz - string to check the length of
|
|
|
|
cchMax - maximum number of characters including the null terminator
|
|
that psz is allowed to contain
|
|
|
|
pcch - if the function succeeds and pcch is non-null, the current length
|
|
in characters of psz excluding the null terminator will be returned.
|
|
This out parameter is equivalent to the return value of strlen(psz)
|
|
|
|
Notes:
|
|
psz can be null but the function will fail
|
|
|
|
cchMax should be greater than zero or the function will fail
|
|
|
|
Return Value:
|
|
|
|
S_OK - psz is non-null and the length including the null
|
|
terminator is less than or equal to cchMax characters
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define UnalignedStringCchLength UnalignedStringCchLengthW
|
|
#else
|
|
#define UnalignedStringCchLength StringCchLengthA
|
|
#endif // !UNICODE
|
|
|
|
#ifdef ALIGNMENT_MACHINE
|
|
__checkReturn
|
|
STRSAFEAPI
|
|
UnalignedStringCchLengthW(
|
|
__in STRSAFE_PCUNZWCH psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = UnalignedStringLengthWorkerW(psz, cchMax, pcchLength);
|
|
}
|
|
|
|
if (FAILED(hr) && pcchLength)
|
|
{
|
|
*pcchLength = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#else
|
|
#define UnalignedStringCchLengthW StringCchLengthW
|
|
#endif // !ALIGNMENT_MACHINE
|
|
#endif // !STRSAFE_NO_CCH_FUNCTIONS
|
|
|
|
#ifndef STRSAFE_NO_CB_FUNCTIONS
|
|
/*++
|
|
|
|
STDAPI
|
|
UnalignedStringCbLength(
|
|
__in LPCUTSTR psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH * sizeof(TCHAR)) size_t cbMax,
|
|
__out_opt __deref_out_range(<, cbMax) size_t* pcbLength OPTIONAL
|
|
);
|
|
|
|
Routine Description:
|
|
|
|
This routine is a version of StringCbLength that accepts an unaligned string pointer.
|
|
|
|
This function returns a hresult, and not a pointer. It returns
|
|
S_OK if the string is non-null and the length including the null
|
|
terminator is less than or equal to cbMax bytes.
|
|
|
|
Arguments:
|
|
|
|
psz - string to check the length of
|
|
|
|
cbMax - maximum number of bytes including the null terminator
|
|
that psz is allowed to contain
|
|
|
|
pcb - if the function succeeds and pcb is non-null, the current length
|
|
in bytes of psz excluding the null terminator will be returned.
|
|
This out parameter is equivalent to the return value of strlen(psz) * sizeof(TCHAR)
|
|
|
|
Notes:
|
|
psz can be null but the function will fail
|
|
|
|
cbMax should be greater than or equal to sizeof(TCHAR) or the function will fail
|
|
|
|
Return Value:
|
|
|
|
S_OK - psz is non-null and the length including the null
|
|
terminator is less than or equal to cbMax bytes
|
|
|
|
failure - you can use the macro HRESULT_CODE() to get a win32
|
|
error code for all hresult failure cases
|
|
|
|
It is strongly recommended to use the SUCCEEDED() / FAILED() macros to test the
|
|
return value of this function.
|
|
|
|
--*/
|
|
#ifdef UNICODE
|
|
#define UnalignedStringCbLength UnalignedStringCbLengthW
|
|
#else
|
|
#define UnalignedStringCbLength StringCbLengthA
|
|
#endif // !UNICODE
|
|
|
|
#ifdef ALIGNMENT_MACHINE
|
|
__checkReturn
|
|
STRSAFEAPI
|
|
UnalignedStringCbLengthW(
|
|
__in STRSAFE_PCUNZWCH psz,
|
|
__in __in_range(1, STRSAFE_MAX_CCH * sizeof(wchar_t)) size_t cbMax,
|
|
__out_opt __deref_out_range(<, cbMax - 1) size_t* pcbLength)
|
|
{
|
|
HRESULT hr;
|
|
size_t cchMax = cbMax / sizeof(wchar_t);
|
|
size_t cchLength = 0;
|
|
|
|
if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
hr = UnalignedStringLengthWorkerW(psz, cchMax, &cchLength);
|
|
}
|
|
|
|
if (pcbLength)
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// safe to multiply cchLength * sizeof(wchar_t) since cchLength < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
|
|
*pcbLength = cchLength * sizeof(wchar_t);
|
|
}
|
|
else
|
|
{
|
|
*pcbLength = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#else
|
|
#define UnalignedStringCbLengthW StringCbLengthW
|
|
#endif // !ALIGNMENT_MACHINE
|
|
#endif // !STRSAFE_NO_CB_FUNCTIONS
|
|
|
|
|
|
#endif // !STRSAFE_LIB_IMPL
|
|
|
|
|
|
// Below here are the worker functions that actually do the work
|
|
|
|
#if defined(STRSAFE_LIB_IMPL) || !defined(STRSAFE_LIB)
|
|
|
|
STRSAFEWORKERAPI
|
|
StringLengthWorkerA(
|
|
__in STRSAFE_PCNZCH psz,
|
|
__in __in_range(<=, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cchOriginalMax = cchMax;
|
|
|
|
while (cchMax && (*psz != '\0'))
|
|
{
|
|
psz++;
|
|
cchMax--;
|
|
}
|
|
|
|
if (cchMax == 0)
|
|
{
|
|
// the string is longer than cchMax
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (pcchLength)
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pcchLength = cchOriginalMax - cchMax;
|
|
}
|
|
else
|
|
{
|
|
*pcchLength = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEWORKERAPI
|
|
StringLengthWorkerW(
|
|
__in STRSAFE_PCNZWCH psz,
|
|
__in __in_range(<=, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cchOriginalMax = cchMax;
|
|
|
|
while (cchMax && (*psz != L'\0'))
|
|
{
|
|
psz++;
|
|
cchMax--;
|
|
}
|
|
|
|
if (cchMax == 0)
|
|
{
|
|
// the string is longer than cchMax
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (pcchLength)
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pcchLength = cchOriginalMax - cchMax;
|
|
}
|
|
else
|
|
{
|
|
*pcchLength = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#ifdef ALIGNMENT_MACHINE
|
|
STRSAFEWORKERAPI
|
|
UnalignedStringLengthWorkerW(
|
|
__in STRSAFE_PCUNZWCH psz,
|
|
__in __in_range(<=, STRSAFE_MAX_CCH) size_t cchMax,
|
|
__out_opt __deref_out_range(<, cchMax) size_t* pcchLength)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cchOriginalMax = cchMax;
|
|
|
|
while (cchMax && (*psz != L'\0'))
|
|
{
|
|
psz++;
|
|
cchMax--;
|
|
}
|
|
|
|
if (cchMax == 0)
|
|
{
|
|
// the string is longer than cchMax
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (pcchLength)
|
|
{
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pcchLength = cchOriginalMax - cchMax;
|
|
}
|
|
else
|
|
{
|
|
*pcchLength = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // ALIGNMENT_MACHINE
|
|
|
|
// Intentionally allow null deref when STRSAFE_IGNORE_NULLS is not present.
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_DEREF_NULL_PTR)
|
|
#pragma warning(disable : __WARNING_INVALID_PARAM_VALUE_1)
|
|
#pragma warning(disable : __WARNING_RETURNING_BAD_RESULT)
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateSrcA(
|
|
__deref_in_opt_out STRSAFE_LPCSTR* ppszSrc,
|
|
__inout_opt __deref_out_range(<, cchMax) size_t* pcchToRead,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pcchToRead && (*pcchToRead >= cchMax))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if ((dwFlags & STRSAFE_IGNORE_NULLS) && (*ppszSrc == NULL))
|
|
{
|
|
*ppszSrc = "";
|
|
|
|
if (pcchToRead)
|
|
{
|
|
*pcchToRead = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateSrcW(
|
|
__deref_in_opt_out STRSAFE_LPCWSTR* ppszSrc,
|
|
__inout_opt __deref_out_range(<, cchMax) size_t* pcchToRead,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pcchToRead && (*pcchToRead >= cchMax))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
else if ((dwFlags & STRSAFE_IGNORE_NULLS) && (*ppszSrc == NULL))
|
|
{
|
|
*ppszSrc = L"";
|
|
|
|
if (pcchToRead)
|
|
{
|
|
*pcchToRead = 0;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#pragma warning(pop) // allow null deref
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4100) // Unused parameter (pszDest)
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestA(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if ((cchDest == 0) || (cchDest > cchMax))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#pragma warning(pop)
|
|
|
|
// Intentionally allow null deref when STRSAFE_IGNORE_NULLS is not present.
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_DEREF_NULL_PTR)
|
|
#pragma warning(disable : __WARNING_INVALID_PARAM_VALUE_1)
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestAndLengthA(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestA(pszDest, cchDest, cchMax);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringLengthWorkerA(pszDest, cchDest, pcchDestLength);
|
|
}
|
|
else
|
|
{
|
|
*pcchDestLength = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
// End intentionally allow null deref.
|
|
#pragma warning(pop)
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4100) // Unused parameter (pszDest)
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestW(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZWCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if ((cchDest == 0) || (cchDest > cchMax))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#pragma warning(pop)
|
|
|
|
// Intentionally allow null deref when STRSAFE_IGNORE_NULLS is not present.
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_DEREF_NULL_PTR)
|
|
#pragma warning(disable : __WARNING_INVALID_PARAM_VALUE_1)
|
|
STRSAFEWORKERAPI
|
|
StringValidateDestAndLengthW(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = StringValidateDestW(pszDest, cchDest, cchMax);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = StringLengthWorkerW(pszDest, cchDest, pcchDestLength);
|
|
}
|
|
else
|
|
{
|
|
*pcchDestLength = 0;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
// End intentionally allow null deref.
|
|
#pragma warning(pop)
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestA(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (dwFlags & STRSAFE_IGNORE_NULLS)
|
|
{
|
|
if (((pszDest == NULL) && (cchDest != 0)) ||
|
|
(cchDest > cchMax))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringValidateDestA(pszDest, cchDest, cchMax);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Intentionally allow null deref when STRSAFE_IGNORE_NULLS is not present.
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_DEREF_NULL_PTR)
|
|
#pragma warning(disable : __WARNING_INVALID_PARAM_VALUE_1)
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestAndLengthA(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (dwFlags & STRSAFE_IGNORE_NULLS)
|
|
{
|
|
hr = StringExValidateDestA(pszDest, cchDest, cchMax, dwFlags);
|
|
|
|
if (FAILED(hr) || (cchDest == 0))
|
|
{
|
|
*pcchDestLength = 0;
|
|
}
|
|
else
|
|
{
|
|
hr = StringLengthWorkerA(pszDest, cchDest, pcchDestLength);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringValidateDestAndLengthA(pszDest,
|
|
cchDest,
|
|
pcchDestLength,
|
|
cchMax);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
// End intentionally allow null deref.
|
|
#pragma warning(pop)
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestW(
|
|
__in_ecount_opt(cchDest) STRSAFE_PCNZWCH pszDest,
|
|
__in size_t cchDest,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (dwFlags & STRSAFE_IGNORE_NULLS)
|
|
{
|
|
if (((pszDest == NULL) && (cchDest != 0)) ||
|
|
(cchDest > cchMax))
|
|
{
|
|
hr = STRSAFE_E_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringValidateDestW(pszDest, cchDest, cchMax);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Intentionally allow null deref when STRSAFE_IGNORE_NULLS is not present.
|
|
#pragma warning(push)
|
|
#pragma warning(disable : __WARNING_DEREF_NULL_PTR)
|
|
#pragma warning(disable : __WARNING_INVALID_PARAM_VALUE_1)
|
|
STRSAFEWORKERAPI
|
|
StringExValidateDestAndLengthW(
|
|
__in_ecount_opt(cchDest) STRSAFE_LPCWSTR pszDest,
|
|
__in size_t cchDest,
|
|
__out __deref_out_range(<, cchDest) size_t* pcchDestLength,
|
|
__in const size_t cchMax,
|
|
__in DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (dwFlags & STRSAFE_IGNORE_NULLS)
|
|
{
|
|
hr = StringExValidateDestW(pszDest, cchDest, cchMax, dwFlags);
|
|
|
|
if (FAILED(hr) || (cchDest == 0))
|
|
{
|
|
*pcchDestLength = 0;
|
|
}
|
|
else
|
|
{
|
|
hr = StringLengthWorkerW(pszDest, cchDest, pcchDestLength);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = StringValidateDestAndLengthW(pszDest,
|
|
cchDest,
|
|
pcchDestLength,
|
|
cchMax);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
// End intentionally allow null deref.
|
|
#pragma warning(pop)
|
|
|
|
STRSAFEWORKERAPI
|
|
StringCopyWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, (cchToCopy < cchDest) ? cchToCopy : cchDest - 1) size_t* pcchNewDestLength,
|
|
__in_xcount(cchToCopy) STRSAFE_PCNZCH pszSrc,
|
|
__in __in_range(<, STRSAFE_MAX_CCH) size_t cchToCopy)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
// ASSERT(cchDest != 0);
|
|
|
|
while (cchDest && cchToCopy && (*pszSrc != '\0'))
|
|
{
|
|
*pszDest++ = *pszSrc++;
|
|
cchDest--;
|
|
cchToCopy--;
|
|
|
|
cchNewDestLength++;
|
|
}
|
|
|
|
if (cchDest == 0)
|
|
{
|
|
// we are going to truncate pszDest
|
|
pszDest--;
|
|
cchNewDestLength--;
|
|
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
*pszDest = '\0';
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEWORKERAPI
|
|
StringCopyWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, (cchToCopy < cchDest) ? cchToCopy : cchDest - 1) size_t* pcchNewDestLength,
|
|
__in_xcount(cchToCopy) STRSAFE_PCNZWCH pszSrc,
|
|
__in __in_range(<, STRSAFE_MAX_CCH) size_t cchToCopy)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
// ASSERT(cchDest != 0);
|
|
|
|
while (cchDest && cchToCopy && (*pszSrc != L'\0'))
|
|
{
|
|
*pszDest++ = *pszSrc++;
|
|
cchDest--;
|
|
cchToCopy--;
|
|
|
|
cchNewDestLength++;
|
|
}
|
|
|
|
if (cchDest == 0)
|
|
{
|
|
// we are going to truncate pszDest
|
|
pszDest--;
|
|
cchNewDestLength--;
|
|
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
*pszDest = L'\0';
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEWORKERAPI
|
|
StringVPrintfWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iRet;
|
|
size_t cchMax;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
// leave the last space for the null terminator
|
|
cchMax = cchDest - 1;
|
|
|
|
#if (STRSAFE_USE_SECURE_CRT == 1) && !defined(STRSAFE_LIB_IMPL)
|
|
iRet = _vsnprintf_s(pszDest, cchDest, cchMax, pszFormat, argList);
|
|
#else
|
|
#pragma warning(push)
|
|
#pragma warning(disable: __WARNING_BANNED_API_USAGE)// "STRSAFE not included"
|
|
iRet = _vsnprintf(pszDest, cchMax, pszFormat, argList);
|
|
#pragma warning(pop)
|
|
#endif
|
|
// ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
|
|
|
|
if ((iRet < 0) || (((size_t)iRet) > cchMax))
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = '\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
|
|
// we have truncated pszDest
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
else if (((size_t)iRet) == cchMax)
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = '\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
}
|
|
else
|
|
{
|
|
cchNewDestLength = (size_t)iRet;
|
|
}
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#ifdef STRSAFE_LOCALE_FUNCTIONS
|
|
STRSAFELOCALEWORKERAPI
|
|
StringVPrintf_lWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iRet;
|
|
size_t cchMax;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
// leave the last space for the null terminator
|
|
cchMax = cchDest - 1;
|
|
|
|
#if (STRSAFE_USE_SECURE_CRT == 1) && !defined(STRSAFE_LIB_IMPL)
|
|
iRet = _vsnprintf_s_l(pszDest, cchDest, cchMax, pszFormat, locale, argList);
|
|
#else
|
|
iRet = _vsnprintf_l(pszDest, cchMax, pszFormat, locale, argList);
|
|
#endif
|
|
// ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
|
|
|
|
if ((iRet < 0) || (((size_t)iRet) > cchMax))
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = '\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
|
|
// we have truncated pszDest
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
else if (((size_t)iRet) == cchMax)
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = '\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
}
|
|
else
|
|
{
|
|
cchNewDestLength = (size_t)iRet;
|
|
}
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS
|
|
|
|
STRSAFEWORKERAPI
|
|
StringVPrintfWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iRet;
|
|
size_t cchMax;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
// leave the last space for the null terminator
|
|
cchMax = cchDest - 1;
|
|
|
|
#if (STRSAFE_USE_SECURE_CRT == 1) && !defined(STRSAFE_LIB_IMPL)
|
|
iRet = _vsnwprintf_s(pszDest, cchDest, cchMax, pszFormat, argList);
|
|
#else
|
|
#pragma warning(push)
|
|
#pragma warning(disable: __WARNING_BANNED_API_USAGE)// "STRSAFE not included"
|
|
iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
|
|
#pragma warning(pop)
|
|
#endif
|
|
// ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
|
|
|
|
if ((iRet < 0) || (((size_t)iRet) > cchMax))
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = L'\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
|
|
// we have truncated pszDest
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
else if (((size_t)iRet) == cchMax)
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = L'\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
}
|
|
else
|
|
{
|
|
cchNewDestLength = (size_t)iRet;
|
|
}
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#ifdef STRSAFE_LOCALE_FUNCTIONS
|
|
STRSAFELOCALEWORKERAPI
|
|
StringVPrintf_lWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength,
|
|
__in __format_string STRSAFE_LPCWSTR pszFormat,
|
|
__in _locale_t locale,
|
|
__in va_list argList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iRet;
|
|
size_t cchMax;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
// leave the last space for the null terminator
|
|
cchMax = cchDest - 1;
|
|
|
|
#if (STRSAFE_USE_SECURE_CRT == 1) && !defined(STRSAFE_LIB_IMPL)
|
|
iRet = _vsnwprintf_s_l(pszDest, cchDest, cchMax, pszFormat, locale, argList);
|
|
#else
|
|
iRet = _vsnwprintf_l(pszDest, cchMax, pszFormat, locale, argList);
|
|
#endif
|
|
// ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
|
|
|
|
if ((iRet < 0) || (((size_t)iRet) > cchMax))
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = L'\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
|
|
// we have truncated pszDest
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
else if (((size_t)iRet) == cchMax)
|
|
{
|
|
// need to null terminate the string
|
|
pszDest += cchMax;
|
|
*pszDest = L'\0';
|
|
|
|
cchNewDestLength = cchMax;
|
|
}
|
|
else
|
|
{
|
|
cchNewDestLength = (size_t)iRet;
|
|
}
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
#endif // STRSAFE_LOCALE_FUNCTIONS
|
|
|
|
#endif // defined(STRSAFE_LIB_IMPL) || !defined(STRSAFE_LIB)
|
|
|
|
#ifndef STRSAFE_LIB_IMPL
|
|
// the StringGetsWorkerA/W functions always run inline since we do not want to
|
|
// have a different strsafe lib versions each type of c runtime (eg msvcrt,
|
|
// libcmt, etc..)
|
|
|
|
STRSAFEAPI
|
|
StringGetsWorkerA(
|
|
__out_ecount(cchDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
if (cchDest == 1)
|
|
{
|
|
*pszDest = '\0';
|
|
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
else
|
|
{
|
|
while (cchDest > 1)
|
|
{
|
|
char ch;
|
|
int i = getc(stdin);
|
|
|
|
if (i == EOF)
|
|
{
|
|
if (cchNewDestLength == 0)
|
|
{
|
|
// we failed to read anything from stdin
|
|
hr = STRSAFE_E_END_OF_FILE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
ch = (char)i;
|
|
|
|
if (ch == '\n')
|
|
{
|
|
break;
|
|
}
|
|
|
|
*pszDest++ = ch;
|
|
cchDest--;
|
|
|
|
cchNewDestLength++;
|
|
}
|
|
|
|
*pszDest = '\0';
|
|
}
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STRSAFEAPI
|
|
StringGetsWorkerW(
|
|
__out_ecount(cchDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(1, STRSAFE_MAX_CCH) size_t cchDest,
|
|
__out_opt __deref_out_range(<=, cchDest - 1) size_t* pcchNewDestLength)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
size_t cchNewDestLength = 0;
|
|
|
|
if (cchDest == 1)
|
|
{
|
|
*pszDest = '\0';
|
|
|
|
hr = STRSAFE_E_INSUFFICIENT_BUFFER;
|
|
}
|
|
else
|
|
{
|
|
while (cchDest > 1)
|
|
{
|
|
wchar_t ch = getwc(stdin);
|
|
// ASSERT(sizeof(wchar_t) == sizeof(wint_t));
|
|
|
|
if (ch == WEOF)
|
|
{
|
|
if (cchNewDestLength == 0)
|
|
{
|
|
// we failed to read anything from stdin
|
|
hr = STRSAFE_E_END_OF_FILE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (ch == L'\n')
|
|
{
|
|
break;
|
|
}
|
|
|
|
*pszDest++ = ch;
|
|
cchDest--;
|
|
|
|
cchNewDestLength++;
|
|
}
|
|
|
|
*pszDest = L'\0';
|
|
}
|
|
|
|
if (pcchNewDestLength)
|
|
{
|
|
*pcchNewDestLength = cchNewDestLength;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#endif // !STRSAFE_LIB_IMPL
|
|
|
|
#if defined(STRSAFE_LIB_IMPL) || !defined(STRSAFE_LIB)
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleFillBehindNullA(
|
|
__inout_bcount(cbRemaining) STRSAFE_LPSTR pszDestEnd,
|
|
__in size_t cbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
if (cbRemaining > sizeof(char))
|
|
{
|
|
memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), cbRemaining - sizeof(char));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleFillBehindNullW(
|
|
__inout_bcount(cbRemaining) STRSAFE_LPWSTR pszDestEnd,
|
|
__in size_t cbRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
if (cbRemaining > sizeof(wchar_t))
|
|
{
|
|
memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), cbRemaining - sizeof(wchar_t));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleOtherFlagsA(
|
|
__inout_bcount(cbDest) STRSAFE_LPSTR pszDest,
|
|
__in __in_range(sizeof(char), STRSAFE_MAX_CCH * sizeof(char)) size_t cbDest,
|
|
__in __in_range(<, cbDest / sizeof(char)) size_t cchOriginalDestLength,
|
|
__deref_inout_ecount(*pcchRemaining) STRSAFE_LPSTR* ppszDestEnd,
|
|
__out __deref_out_range(<=, cbDest / sizeof(char)) size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
size_t cchDest = cbDest / sizeof(char);
|
|
|
|
if ((cchDest > 0) && (dwFlags & STRSAFE_NO_TRUNCATION))
|
|
{
|
|
char* pszOriginalDestEnd;
|
|
|
|
pszOriginalDestEnd = pszDest + cchOriginalDestLength;
|
|
|
|
*ppszDestEnd = pszOriginalDestEnd;
|
|
*pcchRemaining = cchDest - cchOriginalDestLength;
|
|
|
|
// null terminate the end of the original string
|
|
*pszOriginalDestEnd = '\0';
|
|
}
|
|
|
|
if (dwFlags & STRSAFE_FILL_ON_FAILURE)
|
|
{
|
|
memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
|
|
|
|
if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
|
|
{
|
|
*ppszDestEnd = pszDest;
|
|
*pcchRemaining = cchDest;
|
|
}
|
|
else if (cchDest > 0)
|
|
{
|
|
char* pszDestEnd;
|
|
|
|
pszDestEnd = pszDest + cchDest - 1;
|
|
|
|
*ppszDestEnd = pszDestEnd;
|
|
*pcchRemaining = 1;
|
|
|
|
// null terminate the end of the string
|
|
*pszDestEnd = L'\0';
|
|
}
|
|
}
|
|
|
|
if ((cchDest > 0) && (dwFlags & STRSAFE_NULL_ON_FAILURE))
|
|
{
|
|
*ppszDestEnd = pszDest;
|
|
*pcchRemaining = cchDest;
|
|
|
|
// null terminate the beginning of the string
|
|
*pszDest = '\0';
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STRSAFEWORKERAPI
|
|
StringExHandleOtherFlagsW(
|
|
__inout_bcount(cbDest) STRSAFE_LPWSTR pszDest,
|
|
__in __in_range(sizeof(wchar_t), STRSAFE_MAX_CCH * sizeof(wchar_t)) size_t cbDest,
|
|
__in __in_range(<, cbDest / sizeof(wchar_t)) size_t cchOriginalDestLength,
|
|
__deref_inout_ecount(*pcchRemaining) STRSAFE_LPWSTR* ppszDestEnd,
|
|
__out __deref_out_range(<=, cbDest / sizeof(wchar_t)) size_t* pcchRemaining,
|
|
__in DWORD dwFlags)
|
|
{
|
|
size_t cchDest = cbDest / sizeof(wchar_t);
|
|
|
|
if ((cchDest > 0) && (dwFlags & STRSAFE_NO_TRUNCATION))
|
|
{
|
|
wchar_t* pszOriginalDestEnd;
|
|
|
|
pszOriginalDestEnd = pszDest + cchOriginalDestLength;
|
|
|
|
*ppszDestEnd = pszOriginalDestEnd;
|
|
*pcchRemaining = cchDest - cchOriginalDestLength;
|
|
|
|
// null terminate the end of the original string
|
|
*pszOriginalDestEnd = L'\0';
|
|
}
|
|
|
|
if (dwFlags & STRSAFE_FILL_ON_FAILURE)
|
|
{
|
|
memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
|
|
|
|
if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
|
|
{
|
|
*ppszDestEnd = pszDest;
|
|
*pcchRemaining = cchDest;
|
|
}
|
|
else if (cchDest > 0)
|
|
{
|
|
wchar_t* pszDestEnd;
|
|
|
|
pszDestEnd = pszDest + cchDest - 1;
|
|
|
|
*ppszDestEnd = pszDestEnd;
|
|
*pcchRemaining = 1;
|
|
|
|
// null terminate the end of the string
|
|
*pszDestEnd = L'\0';
|
|
}
|
|
}
|
|
|
|
if ((cchDest > 0) && (dwFlags & STRSAFE_NULL_ON_FAILURE))
|
|
{
|
|
*ppszDestEnd = pszDest;
|
|
*pcchRemaining = cchDest;
|
|
|
|
// null terminate the beginning of the string
|
|
*pszDest = L'\0';
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#endif // defined(STRSAFE_LIB_IMPL) || !defined(STRSAFE_LIB)
|
|
|
|
|
|
// Do not call these functions, they are worker functions for internal use within this file
|
|
#ifdef DEPRECATE_SUPPORTED
|
|
#pragma deprecated(StringLengthWorkerA)
|
|
#pragma deprecated(StringLengthWorkerW)
|
|
#pragma deprecated(UnalignedStringLengthWorkerW)
|
|
#pragma deprecated(StringExValidateSrcA)
|
|
#pragma deprecated(StringExValidateSrcW)
|
|
#pragma deprecated(StringValidateDestA)
|
|
#pragma deprecated(StringValidateDestAndLengthA)
|
|
#pragma deprecated(StringValidateDestW)
|
|
#pragma deprecated(StringValidateDestAndLengthW)
|
|
#pragma deprecated(StringExValidateDestA)
|
|
#pragma deprecated(StringExValidateDestAndLengthA)
|
|
#pragma deprecated(StringExValidateDestW)
|
|
#pragma deprecated(StringExValidateDestAndLengthW)
|
|
#pragma deprecated(StringCopyWorkerA)
|
|
#pragma deprecated(StringCopyWorkerW)
|
|
#pragma deprecated(StringVPrintfWorkerA)
|
|
#pragma deprecated(StringVPrintfWorkerW)
|
|
#pragma deprecated(StringGetsWorkerA)
|
|
#pragma deprecated(StringGetsWorkerW)
|
|
#pragma deprecated(StringExHandleFillBehindNullA)
|
|
#pragma deprecated(StringExHandleFillBehindNullW)
|
|
#pragma deprecated(StringExHandleOtherFlagsA)
|
|
#pragma deprecated(StringExHandleOtherFlagsW)
|
|
#else
|
|
#define StringLengthWorkerA StringLengthWorkerA_instead_use_StringCchLengthA_or_StringCbLengthA
|
|
#define StringLengthWorkerW StringLengthWorkerW_instead_use_StringCchLengthW_or_StringCbLengthW
|
|
#define UnalignedStringLengthWorkerW UnalignedStringLengthWorkerW_instead_use_UnalignedStringCchLengthW
|
|
#define StringExValidateSrcA StringExValidateSrcA_do_not_call_this_function
|
|
#define StringExValidateSrcW StringExValidateSrcW_do_not_call_this_function
|
|
#define StringValidateDestA StringValidateDestA_do_not_call_this_function
|
|
#define StringValidateDestAndLengthA StringValidateDestAndLengthA_do_not_call_this_function
|
|
#define StringValidateDestW StringValidateDestW_do_not_call_this_function
|
|
#define StringValidateDestAndLengthW StringValidateDestAndLengthW_do_not_call_this_function
|
|
#define StringExValidateDestA StringExValidateDestA_do_not_call_this_function
|
|
#define StringExValidateDestAndLengthA StringExValidateDestAndLengthA_do_not_call_this_function
|
|
#define StringExValidateDestW StringExValidateDestW_do_not_call_this_function
|
|
#define StringExValidateDestAndLengthW StringExValidateDestAndLengthW_do_not_call_this_function
|
|
#define StringCopyWorkerA StringCopyWorkerA_instead_use_StringCchCopyA_or_StringCbCopyA
|
|
#define StringCopyWorkerW StringCopyWorkerW_instead_use_StringCchCopyW_or_StringCbCopyW
|
|
#define StringVPrintfWorkerA StringVPrintfWorkerA_instead_use_StringCchVPrintfA_or_StringCbVPrintfA
|
|
#define StringVPrintfWorkerW StringVPrintfWorkerW_instead_use_StringCchVPrintfW_or_StringCbVPrintfW
|
|
#define StringGetsWorkerA StringGetsWorkerA_instead_use_StringCchGetsA_or_StringCbGetsA
|
|
#define StringGetsWorkerW StringGetsWorkerW_instead_use_StringCchGetsW_or_StringCbGetsW
|
|
#define StringExHandleFillBehindNullA StringExHandleFillBehindNullA_do_not_call_this_function
|
|
#define StringExHandleFillBehindNullW StringExHandleFillBehindNullW_do_not_call_this_function
|
|
#define StringExHandleOtherFlagsA StringExHandleOtherFlagsA_do_not_call_this_function
|
|
#define StringExHandleOtherFlagsW StringExHandleOtherFlagsW_do_not_call_this_function
|
|
#endif // !DEPRECATE_SUPPORTED
|
|
|
|
|
|
#ifndef STRSAFE_NO_DEPRECATE
|
|
// Deprecate all of the unsafe functions to generate compiletime errors. If you do not want
|
|
// this then you can #define STRSAFE_NO_DEPRECATE before including this file
|
|
#ifdef DEPRECATE_SUPPORTED
|
|
|
|
// First all the names that are a/w variants (or shouldn't be #defined by now anyway)
|
|
#pragma deprecated(lstrcpyA)
|
|
#pragma deprecated(lstrcpyW)
|
|
#pragma deprecated(lstrcatA)
|
|
#pragma deprecated(lstrcatW)
|
|
#pragma deprecated(wsprintfA)
|
|
#pragma deprecated(wsprintfW)
|
|
|
|
#pragma deprecated(StrCpyW)
|
|
#pragma deprecated(StrCatW)
|
|
#pragma deprecated(StrNCatA)
|
|
#pragma deprecated(StrNCatW)
|
|
#pragma deprecated(StrCatNA)
|
|
#pragma deprecated(StrCatNW)
|
|
#pragma deprecated(wvsprintfA)
|
|
#pragma deprecated(wvsprintfW)
|
|
|
|
#pragma deprecated(strcpy)
|
|
#pragma deprecated(wcscpy)
|
|
#pragma deprecated(strcat)
|
|
#pragma deprecated(wcscat)
|
|
#pragma deprecated(sprintf)
|
|
#pragma deprecated(swprintf)
|
|
#pragma deprecated(vsprintf)
|
|
#pragma deprecated(vswprintf)
|
|
#pragma deprecated(_snprintf)
|
|
#pragma deprecated(_snwprintf)
|
|
#pragma deprecated(_vsnprintf)
|
|
#pragma deprecated(_vsnwprintf)
|
|
#pragma deprecated(gets)
|
|
#pragma deprecated(_getws)
|
|
|
|
// Then all the windows.h names
|
|
#undef lstrcpy
|
|
#undef lstrcat
|
|
#undef wsprintf
|
|
#undef wvsprintf
|
|
#pragma deprecated(lstrcpy)
|
|
#pragma deprecated(lstrcat)
|
|
#pragma deprecated(wsprintf)
|
|
#pragma deprecated(wvsprintf)
|
|
#ifdef UNICODE
|
|
#define lstrcpy lstrcpyW
|
|
#define lstrcat lstrcatW
|
|
#define wsprintf wsprintfW
|
|
#define wvsprintf wvsprintfW
|
|
#else
|
|
#define lstrcpy lstrcpyA
|
|
#define lstrcat lstrcatA
|
|
#define wsprintf wsprintfA
|
|
#define wvsprintf wvsprintfA
|
|
#endif
|
|
|
|
// Then the shlwapi names
|
|
#undef StrCpyA
|
|
#undef StrCpy
|
|
#undef StrCatA
|
|
#undef StrCat
|
|
#undef StrNCat
|
|
#undef StrCatN
|
|
#pragma deprecated(StrCpyA)
|
|
#pragma deprecated(StrCatA)
|
|
#pragma deprecated(StrCatN)
|
|
#pragma deprecated(StrCpy)
|
|
#pragma deprecated(StrCat)
|
|
#pragma deprecated(StrNCat)
|
|
#define StrCpyA lstrcpyA
|
|
#define StrCatA lstrcatA
|
|
#define StrCatN StrNCat
|
|
#ifdef UNICODE
|
|
#define StrCpy StrCpyW
|
|
#define StrCat StrCatW
|
|
#define StrNCat StrNCatW
|
|
#else
|
|
#define StrCpy lstrcpyA
|
|
#define StrCat lstrcatA
|
|
#define StrNCat StrNCatA
|
|
#endif
|
|
|
|
#undef _tcscpy
|
|
#undef _ftcscpy
|
|
#undef _tcscat
|
|
#undef _ftcscat
|
|
#undef _stprintf
|
|
#undef _sntprintf
|
|
#undef _vstprintf
|
|
#undef _vsntprintf
|
|
#undef _getts
|
|
#pragma deprecated(_tcscpy)
|
|
#pragma deprecated(_ftcscpy)
|
|
#pragma deprecated(_tcscat)
|
|
#pragma deprecated(_ftcscat)
|
|
#pragma deprecated(_stprintf)
|
|
#pragma deprecated(_sntprintf)
|
|
#pragma deprecated(_vstprintf)
|
|
#pragma deprecated(_vsntprintf)
|
|
#pragma deprecated(_getts)
|
|
#ifdef _UNICODE
|
|
#define _tcscpy wcscpy
|
|
#define _ftcscpy wcscpy
|
|
#define _tcscat wcscat
|
|
#define _ftcscat wcscat
|
|
#define _stprintf swprintf
|
|
#define _sntprintf _snwprintf
|
|
#define _vstprintf vswprintf
|
|
#define _vsntprintf _vsnwprintf
|
|
#define _getts _getws
|
|
#else
|
|
#define _tcscpy strcpy
|
|
#define _ftcscpy strcpy
|
|
#define _tcscat strcat
|
|
#define _ftcscat strcat
|
|
#define _stprintf sprintf
|
|
#define _sntprintf _snprintf
|
|
#define _vstprintf vsprintf
|
|
#define _vsntprintf _vsnprintf
|
|
#define _getts gets
|
|
#endif
|
|
|
|
#else // DEPRECATE_SUPPORTED
|
|
|
|
#undef strcpy
|
|
#define strcpy strcpy_instead_use_StringCchCopyA_or_StringCbCopyA;
|
|
|
|
#undef wcscpy
|
|
#define wcscpy wcscpy_instead_use_StringCchCopyW_or_StringCbCopyW;
|
|
|
|
#undef strcat
|
|
#define strcat strcat_instead_use_StringCchCatA_or_StringCbCatA;
|
|
|
|
#undef wcscat
|
|
#define wcscat wcscat_instead_use_StringCchCatW_or_StringCbCatW;
|
|
|
|
#undef sprintf
|
|
#define sprintf sprintf_instead_use_StringCchPrintfA_or_StringCbPrintfA;
|
|
|
|
#undef swprintf
|
|
#define swprintf swprintf_instead_use_StringCchPrintfW_or_StringCbPrintfW;
|
|
|
|
#undef vsprintf
|
|
#define vsprintf vsprintf_instead_use_StringCchVPrintfA_or_StringCbVPrintfA;
|
|
|
|
#undef vswprintf
|
|
#define vswprintf vswprintf_instead_use_StringCchVPrintfW_or_StringCbVPrintfW;
|
|
|
|
#undef _snprintf
|
|
#define _snprintf _snprintf_instead_use_StringCchPrintfA_or_StringCbPrintfA;
|
|
|
|
#undef _snwprintf
|
|
#define _snwprintf _snwprintf_instead_use_StringCchPrintfW_or_StringCbPrintfW;
|
|
|
|
#undef _vsnprintf
|
|
#define _vsnprintf _vsnprintf_instead_use_StringCchVPrintfA_or_StringCbVPrintfA;
|
|
|
|
#undef _vsnwprintf
|
|
#define _vsnwprintf _vsnwprintf_instead_use_StringCchVPrintfW_or_StringCbVPrintfW;
|
|
|
|
#undef strcpyA
|
|
#define strcpyA strcpyA_instead_use_StringCchCopyA_or_StringCbCopyA;
|
|
|
|
#undef strcpyW
|
|
#define strcpyW strcpyW_instead_use_StringCchCopyW_or_StringCbCopyW;
|
|
|
|
#undef lstrcpy
|
|
#define lstrcpy lstrcpy_instead_use_StringCchCopy_or_StringCbCopy;
|
|
|
|
#undef lstrcpyA
|
|
#define lstrcpyA lstrcpyA_instead_use_StringCchCopyA_or_StringCbCopyA;
|
|
|
|
#undef lstrcpyW
|
|
#define lstrcpyW lstrcpyW_instead_use_StringCchCopyW_or_StringCbCopyW;
|
|
|
|
#undef StrCpy
|
|
#define StrCpy StrCpy_instead_use_StringCchCopy_or_StringCbCopy;
|
|
|
|
#undef StrCpyA
|
|
#define StrCpyA StrCpyA_instead_use_StringCchCopyA_or_StringCbCopyA;
|
|
|
|
#undef StrCpyW
|
|
#define StrCpyW StrCpyW_instead_use_StringCchCopyW_or_StringCbCopyW;
|
|
|
|
#undef _tcscpy
|
|
#define _tcscpy _tcscpy_instead_use_StringCchCopy_or_StringCbCopy;
|
|
|
|
#undef _ftcscpy
|
|
#define _ftcscpy _ftcscpy_instead_use_StringCchCopy_or_StringCbCopy;
|
|
|
|
#undef lstrcat
|
|
#define lstrcat lstrcat_instead_use_StringCchCat_or_StringCbCat;
|
|
|
|
#undef lstrcatA
|
|
#define lstrcatA lstrcatA_instead_use_StringCchCatA_or_StringCbCatA;
|
|
|
|
#undef lstrcatW
|
|
#define lstrcatW lstrcatW_instead_use_StringCchCatW_or_StringCbCatW;
|
|
|
|
#undef StrCat
|
|
#define StrCat StrCat_instead_use_StringCchCat_or_StringCbCat;
|
|
|
|
#undef StrCatA
|
|
#define StrCatA StrCatA_instead_use_StringCchCatA_or_StringCbCatA;
|
|
|
|
#undef StrCatW
|
|
#define StrCatW StrCatW_instead_use_StringCchCatW_or_StringCbCatW;
|
|
|
|
#undef StrNCat
|
|
#define StrNCat StrNCat_instead_use_StringCchCatN_or_StringCbCatN;
|
|
|
|
#undef StrNCatA
|
|
#define StrNCatA StrNCatA_instead_use_StringCchCatNA_or_StringCbCatNA;
|
|
|
|
#undef StrNCatW
|
|
#define StrNCatW StrNCatW_instead_use_StringCchCatNW_or_StringCbCatNW;
|
|
|
|
#undef StrCatN
|
|
#define StrCatN StrCatN_instead_use_StringCchCatN_or_StringCbCatN;
|
|
|
|
#undef StrCatNA
|
|
#define StrCatNA StrCatNA_instead_use_StringCchCatNA_or_StringCbCatNA;
|
|
|
|
#undef StrCatNW
|
|
#define StrCatNW StrCatNW_instead_use_StringCchCatNW_or_StringCbCatNW;
|
|
|
|
#undef _tcscat
|
|
#define _tcscat _tcscat_instead_use_StringCchCat_or_StringCbCat;
|
|
|
|
#undef _ftcscat
|
|
#define _ftcscat _ftcscat_instead_use_StringCchCat_or_StringCbCat;
|
|
|
|
#undef wsprintf
|
|
#define wsprintf wsprintf_instead_use_StringCchPrintf_or_StringCbPrintf;
|
|
|
|
#undef wsprintfA
|
|
#define wsprintfA wsprintfA_instead_use_StringCchPrintfA_or_StringCbPrintfA;
|
|
|
|
#undef wsprintfW
|
|
#define wsprintfW wsprintfW_instead_use_StringCchPrintfW_or_StringCbPrintfW;
|
|
|
|
#undef wvsprintf
|
|
#define wvsprintf wvsprintf_instead_use_StringCchVPrintf_or_StringCbVPrintf;
|
|
|
|
#undef wvsprintfA
|
|
#define wvsprintfA wvsprintfA_instead_use_StringCchVPrintfA_or_StringCbVPrintfA;
|
|
|
|
#undef wvsprintfW
|
|
#define wvsprintfW wvsprintfW_instead_use_StringCchVPrintfW_or_StringCbVPrintfW;
|
|
|
|
#undef _vstprintf
|
|
#define _vstprintf _vstprintf_instead_use_StringCchVPrintf_or_StringCbVPrintf;
|
|
|
|
#undef _vsntprintf
|
|
#define _vsntprintf _vsntprintf_instead_use_StringCchVPrintf_or_StringCbVPrintf;
|
|
|
|
#undef _stprintf
|
|
#define _stprintf _stprintf_instead_use_StringCchPrintf_or_StringCbPrintf;
|
|
|
|
#undef _sntprintf
|
|
#define _sntprintf _sntprintf_instead_use_StringCchPrintf_or_StringCbPrintf;
|
|
|
|
#undef _getts
|
|
#define _getts _getts_instead_use_StringCchGets_or_StringCbGets;
|
|
|
|
#undef gets
|
|
#define gets _gets_instead_use_StringCchGetsA_or_StringCbGetsA;
|
|
|
|
#undef _getws
|
|
#define _getws _getws_instead_use_StringCchGetsW_or_StringCbGetsW;
|
|
|
|
#endif // DEPRECATE_SUPPORTED
|
|
#endif // !STRSAFE_NO_DEPRECATE
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif // _STRSAFE_H_INCLUDED_
|
|
|