text: /******************************************************************
  *                                                                 *
  *  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_
  #pragma once
  
  #include <stdio.h>      // for _vsnprintf, _vsnwprintf, getc, getwc
  #include <string.h>     // for memset
  #include <stdarg.h>     // for va_start, etc.
  
  
  #ifndef _SIZE_T_DEFINED
  #ifdef  _WIN64
  typedef unsigned __int64    size_t;
  #else
  typedef __w64 unsigned int  size_t;
  #endif  // !_WIN64
  #define _SIZE_T_DEFINED
  #endif  // !_SIZE_T_DEFINED
  
  #if !defined(_WCHAR_T_DEFINED) && !defined(_NATIVE_WCHAR_T_DEFINED)
  typedef unsigned short wchar_t;
  #define _WCHAR_T_DEFINED
  #endif
  
  #ifndef _HRESULT_DEFINED
  #define _HRESULT_DEFINED
  typedef long HRESULT;
  #endif // !_HRESULT_DEFINED
  
  #ifndef SUCCEEDED
  #define SUCCEEDED(hr)  ((HRESULT)(hr) >= 0)
  #endif
  
  #ifndef FAILED
  #define FAILED(hr)  ((HRESULT)(hr) < 0)
  #endif
  
  #ifndef S_OK
  #define S_OK  ((HRESULT)0x00000000L)
  #endif
  
  #ifdef __cplusplus
  #define _STRSAFE_EXTERN_C    extern "C"
  #else
  #define _STRSAFE_EXTERN_C    extern
  #endif
  
  // If you do not want to use these functions inline (and instead want to link w/ strsafe.lib), then
  // #define STRSAFE_LIB before including this header file.
  #if defined(STRSAFE_LIB)
  #define STRSAFEAPI  _STRSAFE_EXTERN_C HRESULT __stdcall
  #pragma comment(lib, "strsafe.lib")
  #elif defined(STRSAFE_LIB_IMPL)
  #define STRSAFEAPI  _STRSAFE_EXTERN_C HRESULT __stdcall
  #else
  #define STRSAFEAPI  __inline HRESULT __stdcall
  #define STRSAFE_INLINE
  #endif
  
  // Some functions always run inline because they use stdin and we want to avoid building multiple
  // versions of strsafe lib depending on if you use msvcrt, libcmt, etc.
  #define STRSAFE_INLINE_API  __inline HRESULT __stdcall
  
  // 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
  
  // This should only be defined when we are building strsafe.lib
  #ifdef STRSAFE_LIB_IMPL
  #define STRSAFE_INLINE
  #endif
  
  
  // If both strsafe.h and ntstrsafe.h are included, only use definitions from one.
  #ifndef _NTSTRSAFE_H_INCLUDED_
  
  #define STRSAFE_MAX_CCH  2147483647 // max # of characters we support (same as INT_MAX)
  
  // Flags for controling the Ex functions
  //
  //      STRSAFE_FILL_BYTE(0xFF)     0x000000FF  // bottom byte specifies fill pattern
  #define STRSAFE_IGNORE_NULLS        0x00000100  // treat null as TEXT("") -- don't fault on NULL buffers
  #define STRSAFE_FILL_BEHIND_NULL    0x00000200  // fill in extra space behind the null terminator
  #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)        ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_BEHIND_NULL))
  #define STRSAFE_FAILURE_BYTE(x)     ((unsigned long)((x & 0x000000FF) | STRSAFE_FILL_ON_FAILURE))
  
  #define STRSAFE_GET_FILL_PATTERN(dwFlags)  ((int)(dwFlags & 0x000000FF))
  
  #endif // _NTSTRSAFE_H_INCLUDED_
  
  // STRSAFE 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
  
  // prototypes for the worker functions
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc);
  STRSAFEAPI StringCopyWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
  STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCopyExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc);
  STRSAFEAPI StringCopyNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc);
  STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCopyNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc);
  STRSAFEAPI StringCatWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
  STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCatExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend);
  STRSAFEAPI StringCatNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend);
  STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCatNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList);
  STRSAFEAPI StringVPrintfWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList);
  STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
  STRSAFEAPI StringVPrintfExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList);
  STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch);
  STRSAFEAPI StringLengthWorkerW(const wchar_t* psz, size_t cchMax, size_t* pcch);
  #endif  // STRSAFE_INLINE
  
  #ifndef STRSAFE_LIB_IMPL
  // these functions are always inline
  STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFE_INLINE_API StringGetsExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  #endif
  
  #ifdef _NTSTRSAFE_H_INCLUDED_
  #pragma warning(push)
  #pragma warning(disable : 4995)
  #endif // _NTSTRSAFE_H_INCLUDED_
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCopy(
      OUT 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.
  
  --*/
  
  STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc);
  STRSAFEAPI StringCchCopyW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
  #ifdef UNICODE
  #define StringCchCopy  StringCchCopyW
  #else
  #define StringCchCopy  StringCchCopyA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyWorkerA(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCopyW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyWorkerW(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCopy(
      OUT 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.
  
  --*/
  
  STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc);
  STRSAFEAPI StringCbCopyW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc);
  #ifdef UNICODE
  #define StringCbCopy  StringCbCopyW
  #else
  #define StringCbCopy  StringCbCopyA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCopyA(char* pszDest, size_t cbDest, const char* pszSrc)
  {
      HRESULT hr;
      size_t cchDest;
  
      // convert to count of characters
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyWorkerA(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCbCopyW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc)
  {
      HRESULT hr;
      size_t cchDest;
  
      // convert to count of characters
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyWorkerW(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCopyEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cchDest,
      IN  LPCTSTR pszSrc          OPTIONAL,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT 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
  
  --*/
  
  STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCchCopyExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCchCopyEx  StringCchCopyExW
  #else
  #define StringCchCopyEx  StringCchCopyExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCopyExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
  
          hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCopyExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          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);
  
          hr = StringCopyExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCopyEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cbDest,
      IN  LPCTSTR pszSrc          OPTIONAL,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT 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
  
  --*/
  
  STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCbCopyExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCbCopyEx  StringCbCopyExW
  #else
  #define StringCbCopyEx  StringCbCopyExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCopyExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCopyN(
      OUT LPTSTR  pszDest,
      IN  size_t  cchDest,
      IN  LPCTSTR pszSrc,
      IN  size_t  cchSrc
      );
  
  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 cchSrc 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 cchSrc 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
  
      cchSrc         -   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.
  
  --*/
  
  STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc);
  STRSAFEAPI StringCchCopyNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc);
  #ifdef UNICODE
  #define StringCchCopyN  StringCchCopyNW
  #else
  #define StringCchCopyN  StringCchCopyNA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCopyNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc)
  {
      HRESULT hr;
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCopyNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc)
  {
      HRESULT hr;
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyNWorkerW(pszDest, cchDest, pszSrc, cchSrc);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCopyN(
      OUT LPTSTR  pszDest,
      IN  size_t  cbDest,
      IN  LPCTSTR pszSrc,
      IN  size_t  cbSrc
      );
  
  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 cbSrc 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 cbSrc 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
  
      cbSrc          -   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.
  
  --*/
  
  STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc);
  STRSAFEAPI StringCbCopyNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc);
  #ifdef UNICODE
  #define StringCbCopyN  StringCbCopyNW
  #else
  #define StringCbCopyN  StringCbCopyNA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCopyNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchSrc;
  
      // convert to count of characters
      cchDest = cbDest / sizeof(char);
      cchSrc = cbSrc / sizeof(char);
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyNWorkerA(pszDest, cchDest, pszSrc, cchSrc);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCbCopyNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchSrc;
  
      // convert to count of characters
      cchDest = cbDest / sizeof(wchar_t);
      cchSrc = cbSrc / sizeof(wchar_t);
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyNWorkerW(pszDest, cchDest, pszSrc, cchSrc);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCopyNEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cchDest,
      IN  LPCTSTR pszSrc          OPTIONAL,
      IN  size_t  cchSrc,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT 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 cchSrc 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
  
      cchSrc          -   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
  
  --*/
  
  STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCchCopyNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCchCopyNEx  StringCchCopyNExW
  #else
  #define StringCchCopyNEx  StringCchCopyNExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCopyNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
  
          hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCopyNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          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);
  
          hr = StringCopyNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCopyNEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cbDest,
      IN  LPCTSTR pszSrc          OPTIONAL,
      IN  size_t  cbSrc,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT 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 cbSrc 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
  
      cbSrc           -   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
  
  --*/
  
  STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCbCopyNExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCbCopyNEx  StringCbCopyNExW
  #else
  #define StringCbCopyNEx  StringCbCopyNExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCopyNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchSrc;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(char);
      cchSrc = cbSrc / sizeof(char);
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchSrc;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(wchar_t);
      cchSrc = cbSrc / sizeof(wchar_t);
  
      if ((cchDest > STRSAFE_MAX_CCH) ||
          (cchSrc > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCopyNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchSrc, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCat(
      IN OUT 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
  
  --*/
  
  STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc);
  STRSAFEAPI StringCchCatW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc);
  #ifdef UNICODE
  #define StringCchCat  StringCchCatW
  #else
  #define StringCchCat  StringCchCatA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatWorkerA(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCatW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatWorkerW(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCat(
      IN OUT 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
  
  --*/
  
  STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc);
  STRSAFEAPI StringCbCatW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc);
  #ifdef UNICODE
  #define StringCbCat  StringCbCatW
  #else
  #define StringCbCat  StringCbCatA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCatA(char* pszDest, size_t cbDest, const char* pszSrc)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatWorkerA(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCbCatW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatWorkerW(pszDest, cchDest, pszSrc);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCatEx(
      IN OUT LPTSTR  pszDest         OPTIONAL,
      IN     size_t  cchDest,
      IN     LPCTSTR pszSrc          OPTIONAL,
      OUT    LPTSTR* ppszDestEnd     OPTIONAL,
      OUT    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
  
  --*/
  
  STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCchCatExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCchCatEx  StringCchCatExW
  #else
  #define StringCchCatEx  StringCchCatExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCatExA(char* pszDest, size_t cchDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
  
          hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCatExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          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);
  
          hr = StringCatExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCatEx(
      IN OUT LPTSTR  pszDest         OPTIONAL,
      IN     size_t  cbDest,
      IN     LPCTSTR pszSrc          OPTIONAL,
      OUT    LPTSTR* ppszDestEnd     OPTIONAL,
      OUT    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
  
  --*/
  
  STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCbCatExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCbCatEx  StringCbCatExW
  #else
  #define StringCbCatEx  StringCbCatExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCatExA(char* pszDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatExWorkerA(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatExWorkerW(pszDest, cchDest, cbDest, pszSrc, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCatN(
      IN OUT LPTSTR  pszDest,
      IN     size_t  cchDest,
      IN     LPCTSTR pszSrc,
      IN     size_t  cchMaxAppend
      );
  
  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 cchMaxAppend 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(cchMaxAppend, _tcslen(pszSrc)) + 1)
                          to hold all of the combine string plus the null
                          terminator.
  
      pszSrc          -   source string
  
      cchMaxAppend    -   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 cchMaxAppend 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
  
  --*/
  
  STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend);
  STRSAFEAPI StringCchCatNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend);
  #ifdef UNICODE
  #define StringCchCatN  StringCchCatNW
  #else
  #define StringCchCatN  StringCchCatNA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCatNA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCatNW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringCatNWorkerW(pszDest, cchDest, pszSrc, cchMaxAppend);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCatN(
      IN OUT LPTSTR  pszDest,
      IN     size_t  cbDest,
      IN     LPCTSTR pszSrc,
      IN     size_t  cbMaxAppend
      );
  
  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 cbMaxAppend 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(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
                          to hold all of the combine string plus the null
                          terminator.
  
      pszSrc          -   source string
  
      cbMaxAppend     -   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 cbMaxAppend 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
  
  --*/
  
  STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend);
  STRSAFEAPI StringCbCatNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend);
  #ifdef UNICODE
  #define StringCbCatN  StringCbCatNW
  #else
  #define StringCbCatN  StringCbCatNA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCatNA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cchMaxAppend;
  
          cchMaxAppend = cbMaxAppend / sizeof(char);
  
          hr = StringCatNWorkerA(pszDest, cchDest, pszSrc, cchMaxAppend);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCbCatNW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cchMaxAppend;
  
          cchMaxAppend = cbMaxAppend / sizeof(wchar_t);
  
          hr = StringCatNWorkerW(pszDest, cchDest, pszSrc, cchMaxAppend);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchCatNEx(
      IN OUT LPTSTR  pszDest         OPTIONAL,
      IN     size_t  cchDest,
      IN     LPCTSTR pszSrc          OPTIONAL,
      IN     size_t  cchMaxAppend,
      OUT    LPTSTR* ppszDestEnd     OPTIONAL,
      OUT    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(cchMaxAppend, _tcslen(pszSrc)) + 1)
                          to hold all of the combine string plus the null
                          terminator.
  
      pszSrc          -   source string
  
      cchMaxAppend    -   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 cchMaxAppend 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
  
  --*/
  
  STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCchCatNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCchCatNEx  StringCchCatNExW
  #else
  #define StringCchCatNEx  StringCchCatNExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchCatNExA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
  
          hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchCatNExW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          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);
  
          hr = StringCatNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbCatNEx(
      IN OUT LPTSTR  pszDest         OPTIONAL,
      IN     size_t  cbDest,
      IN     LPCTSTR pszSrc          OPTIONAL,
      IN     size_t  cbMaxAppend,
      OUT    LPTSTR* ppszDestEnd     OPTIONAL,
      OUT    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(cbMaxAppend / sizeof(TCHAR), _tcslen(pszSrc)) + 1) * sizeof(TCHAR)
                          to hold all of the combine string plus the null
                          terminator.
  
      pszSrc          -   source string
  
      cbMaxAppend     -   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 cbMaxAppend 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
  
  --*/
  
  STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  STRSAFEAPI StringCbCatNExW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCbCatNEx  StringCbCatNExW
  #else
  #define StringCbCatNEx  StringCbCatNExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbCatNExA(char* pszDest, size_t cbDest, const char* pszSrc, size_t cbMaxAppend, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cchMaxAppend;
  
          cchMaxAppend = cbMaxAppend / sizeof(char);
  
          hr = StringCatNExWorkerA(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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(wchar_t* pszDest, size_t cbDest, const wchar_t* pszSrc, size_t cbMaxAppend, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cchMaxAppend;
  
          cchMaxAppend = cbMaxAppend / sizeof(wchar_t);
  
          hr = StringCatNExWorkerW(pszDest, cchDest, cbDest, pszSrc, cchMaxAppend, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchVPrintf(
      OUT LPTSTR  pszDest,
      IN  size_t  cchDest,
      IN  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
  
  --*/
  
  STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList);
  STRSAFEAPI StringCchVPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList);
  #ifdef UNICODE
  #define StringCchVPrintf  StringCchVPrintfW
  #else
  #define StringCchVPrintf  StringCchVPrintfA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchVPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchVPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbVPrintf(
      OUT LPTSTR  pszDest,
      IN  size_t  cbDest,
      IN  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
  
  --*/
  
  STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList);
  STRSAFEAPI StringCbVPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, va_list argList);
  #ifdef UNICODE
  #define StringCbVPrintf  StringCbVPrintfW
  #else
  #define StringCbVPrintf  StringCbVPrintfA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbVPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, va_list argList)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCbVPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, va_list argList)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchPrintf(
      OUT LPTSTR  pszDest,
      IN  size_t  cchDest,
      IN  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
  
  --*/
  
  STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...);
  STRSAFEAPI StringCchPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, ...);
  #ifdef UNICODE
  #define StringCchPrintf  StringCchPrintfW
  #else
  #define StringCchPrintf  StringCchPrintfA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchPrintfA(char* pszDest, size_t cchDest, const char* pszFormat, ...)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          va_list argList;
  
          va_start(argList, pszFormat);
  
          hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
  
          va_end(argList);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchPrintfW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, ...)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          va_list argList;
  
          va_start(argList, pszFormat);
  
          hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
  
          va_end(argList);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbPrintf(
      OUT LPTSTR  pszDest,
      IN  size_t  cbDest,
      IN  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
  
  --*/
  
  STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...);
  STRSAFEAPI StringCbPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, ...);
  #ifdef UNICODE
  #define StringCbPrintf  StringCbPrintfW
  #else
  #define StringCbPrintf  StringCbPrintfA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbPrintfA(char* pszDest, size_t cbDest, const char* pszFormat, ...)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          va_list argList;
  
          va_start(argList, pszFormat);
  
          hr = StringVPrintfWorkerA(pszDest, cchDest, pszFormat, argList);
  
          va_end(argList);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCbPrintfW(wchar_t* pszDest, size_t cbDest, const wchar_t* pszFormat, ...)
  {
      HRESULT hr;
      size_t cchDest;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          va_list argList;
  
          va_start(argList, pszFormat);
  
          hr = StringVPrintfWorkerW(pszDest, cchDest, pszFormat, argList);
  
          va_end(argList);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchPrintfEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cchDest,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT size_t* pcchRemaining   OPTIONAL,
      IN  DWORD   dwFlags,
      IN  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 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
  
  --*/
  
  STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...);
  STRSAFEAPI StringCchPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...);
  #ifdef UNICODE
  #define StringCchPrintfEx  StringCchPrintfExW
  #else
  #define StringCchPrintfEx  StringCchPrintfExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, ...)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
          va_list argList;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
          va_start(argList, pszFormat);
  
          hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
  
          va_end(argList);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
          va_list argList;
  
          // safe to multiply cchDest * sizeof(wchar_t) since cchDest < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
          cbDest = cchDest * sizeof(wchar_t);
          va_start(argList, pszFormat);
  
          hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
  
          va_end(argList);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbPrintfEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cbDest,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT size_t* pcbRemaining    OPTIONAL,
      IN  DWORD   dwFlags,
      IN  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
  
  --*/
  
  STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...);
  STRSAFEAPI StringCbPrintfExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...);
  #ifdef UNICODE
  #define StringCbPrintfEx  StringCbPrintfExW
  #else
  #define StringCbPrintfEx  StringCbPrintfExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, ...)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          va_list argList;
  
          va_start(argList, pszFormat);
  
          hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
  
          va_end(argList);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, ...)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          va_list argList;
  
          va_start(argList, pszFormat);
  
          hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
  
          va_end(argList);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchVPrintfEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cchDest,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT size_t* pcchRemaining   OPTIONAL,
      IN  DWORD   dwFlags,
      IN  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
  
  --*/
  
  STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
  STRSAFEAPI StringCchVPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList);
  #ifdef UNICODE
  #define StringCchVPrintfEx  StringCchVPrintfExW
  #else
  #define StringCchVPrintfEx  StringCchVPrintfExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchVPrintfExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
  
          hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchVPrintfExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          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);
  
          hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, argList);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbVPrintfEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cbDest,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT size_t* pcbRemaining    OPTIONAL,
      IN  DWORD   dwFlags,
      IN  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
  
  --*/
  
  STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList);
  STRSAFEAPI StringCbVPrintfExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList);
  #ifdef UNICODE
  #define StringCbVPrintfEx  StringCbVPrintfExW
  #else
  #define StringCbVPrintfEx  StringCbVPrintfExA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbVPrintfExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringVPrintfExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringVPrintfExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags, pszFormat, argList);
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          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_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchGets(
      OUT 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.
  
  --*/
  
  #ifndef STRSAFE_LIB_IMPL
  STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest);
  STRSAFE_INLINE_API StringCchGetsW(wchar_t* pszDest, size_t cchDest);
  #ifdef UNICODE
  #define StringCchGets  StringCchGetsW
  #else
  #define StringCchGets  StringCchGetsA
  #endif // !UNICODE
  
  STRSAFE_INLINE_API StringCchGetsA(char* pszDest, size_t cchDest)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
  
          hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0);
      }
  
      return hr;
  }
  
  STRSAFE_INLINE_API StringCchGetsW(wchar_t* pszDest, size_t cchDest)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          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);
  
          hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, NULL, NULL, 0);
      }
  
      return hr;
  }
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  #endif  // !STRSAFE_LIB_IMPL
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbGets(
      OUT 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.
  
  --*/
  
  #ifndef STRSAFE_LIB_IMPL
  STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest);
  STRSAFE_INLINE_API StringCbGetsW(wchar_t* pszDest, size_t cbDest);
  #ifdef UNICODE
  #define StringCbGets  StringCbGetsW
  #else
  #define StringCbGets  StringCbGetsA
  #endif // !UNICODE
  
  STRSAFE_INLINE_API StringCbGetsA(char* pszDest, size_t cbDest)
  {
      HRESULT hr;
      size_t cchDest;
  
      // convert to count of characters
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, NULL, NULL, 0);
      }
  
      return hr;
  }
  
  STRSAFE_INLINE_API StringCbGetsW(wchar_t* pszDest, size_t cbDest)
  {
      HRESULT hr;
      size_t cchDest;
  
      // convert to count of characters
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, NULL, NULL, 0);
      }
  
      return hr;
  }
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  #endif  // !STRSAFE_LIB_IMPL
  
  #ifndef STRSAFE_NO_CCH_FUNCTIONS
  /*++
  
  STDAPI
  StringCchGetsEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cchDest,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT 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.
  
  --*/
  
  #ifndef STRSAFE_LIB_IMPL
  STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  STRSAFE_INLINE_API StringCchGetsExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCchGetsEx  StringCchGetsExW
  #else
  #define StringCchGetsEx  StringCchGetsExA
  #endif // !UNICODE
  
  STRSAFE_INLINE_API StringCchGetsExA(char* pszDest, size_t cchDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cbDest;
  
          // safe to multiply cchDest * sizeof(char) since cchDest < STRSAFE_MAX_CCH and sizeof(char) is 1
          cbDest = cchDest * sizeof(char);
  
          hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  
  STRSAFE_INLINE_API StringCchGetsExW(wchar_t* pszDest, size_t cchDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          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);
  
          hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, pcchRemaining, dwFlags);
      }
  
      return hr;
  }
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  #endif  // !STRSAFE_LIB_IMPL
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbGetsEx(
      OUT LPTSTR  pszDest         OPTIONAL,
      IN  size_t  cbDest,
      OUT LPTSTR* ppszDestEnd     OPTIONAL,
      OUT 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 pbRemaining 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.
  
  --*/
  
  #ifndef STRSAFE_LIB_IMPL
  STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pbRemaining, unsigned long dwFlags);
  STRSAFE_INLINE_API StringCbGetsExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags);
  #ifdef UNICODE
  #define StringCbGetsEx  StringCbGetsExW
  #else
  #define StringCbGetsEx  StringCbGetsExA
  #endif // !UNICODE
  
  STRSAFE_INLINE_API StringCbGetsExA(char* pszDest, size_t cbDest, char** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(char);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringGetsExWorkerA(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) ||
          (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
          (hr == STRSAFE_E_END_OF_FILE))
      {
          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;
  }
  
  STRSAFE_INLINE_API StringCbGetsExW(wchar_t* pszDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcbRemaining, unsigned long dwFlags)
  {
      HRESULT hr;
      size_t cchDest;
      size_t cchRemaining = 0;
  
      cchDest = cbDest / sizeof(wchar_t);
  
      if (cchDest > STRSAFE_MAX_CCH)
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringGetsExWorkerW(pszDest, cchDest, cbDest, ppszDestEnd, &cchRemaining, dwFlags);
      }
  
      if (SUCCEEDED(hr) ||
          (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
          (hr == STRSAFE_E_END_OF_FILE))
      {
          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  size_t  cchMax,
      OUT size_t* pcch    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.
  
  --*/
  
  STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch);
  STRSAFEAPI StringCchLengthW(const wchar_t* psz, size_t cchMax, size_t* pcch);
  #ifdef UNICODE
  #define StringCchLength  StringCchLengthW
  #else
  #define StringCchLength  StringCchLengthA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch)
  {
      HRESULT hr;
  
      if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringLengthWorkerA(psz, cchMax, pcch);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCchLengthW(const wchar_t* psz, size_t cchMax, size_t* pcch)
  {
      HRESULT hr;
  
      if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringLengthWorkerW(psz, cchMax, pcch);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CCH_FUNCTIONS
  
  
  #ifndef STRSAFE_NO_CB_FUNCTIONS
  /*++
  
  STDAPI
  StringCbLength(
      IN  LPCTSTR psz,
      IN  size_t  cbMax,
      OUT size_t* pcb     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.
  
  --*/
  
  STRSAFEAPI StringCbLengthA(const char* psz, size_t cchMax, size_t* pcch);
  STRSAFEAPI StringCbLengthW(const wchar_t* psz, size_t cchMax, size_t* pcch);
  #ifdef UNICODE
  #define StringCbLength  StringCbLengthW
  #else
  #define StringCbLength  StringCbLengthA
  #endif // !UNICODE
  
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCbLengthA(const char* psz, size_t cbMax, size_t* pcb)
  {
      HRESULT hr;
      size_t cchMax;
      size_t cch = 0;
  
      cchMax = cbMax / sizeof(char);
  
      if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringLengthWorkerA(psz, cchMax, &cch);
      }
  
      if (SUCCEEDED(hr) && pcb)
      {
          // safe to multiply cch * sizeof(char) since cch < STRSAFE_MAX_CCH and sizeof(char) is 1
          *pcb = cch * sizeof(char);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCbLengthW(const wchar_t* psz, size_t cbMax, size_t* pcb)
  {
      HRESULT hr;
      size_t cchMax;
      size_t cch = 0;
  
      cchMax = cbMax / sizeof(wchar_t);
  
      if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          hr = StringLengthWorkerW(psz, cchMax, &cch);
      }
  
      if (SUCCEEDED(hr) && pcb)
      {
          // safe to multiply cch * sizeof(wchar_t) since cch < STRSAFE_MAX_CCH and sizeof(wchar_t) is 2
          *pcb = cch * sizeof(wchar_t);
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  #endif  // !STRSAFE_NO_CB_FUNCTIONS
  
  
  // these are the worker functions that actually do the work
  #ifdef STRSAFE_INLINE
  STRSAFEAPI StringCopyWorkerA(char* pszDest, size_t cchDest, const char* pszSrc)
  {
      HRESULT hr = S_OK;
  
      if (cchDest == 0)
      {
          // can not null terminate a zero-byte dest buffer
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          while (cchDest && (*pszSrc != '\0'))
          {
              *pszDest++ = *pszSrc++;
              cchDest--;
          }
  
          if (cchDest == 0)
          {
              // we are going to truncate pszDest
              pszDest--;
              hr = STRSAFE_E_INSUFFICIENT_BUFFER;
          }
  
          *pszDest= '\0';
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCopyWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
  {
      HRESULT hr = S_OK;
  
      if (cchDest == 0)
      {
          // can not null terminate a zero-byte dest buffer
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          while (cchDest && (*pszSrc != L'\0'))
          {
              *pszDest++ = *pszSrc++;
              cchDest--;
          }
  
          if (cchDest == 0)
          {
              // we are going to truncate pszDest
              pszDest--;
              hr = STRSAFE_E_INSUFFICIENT_BUFFER;
          }
  
          *pszDest= L'\0';
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCopyExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      char* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(char))    ||
      //        cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = "";
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = 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
              {
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  while (cchRemaining && (*pszSrc != '\0'))
                  {
                      *pszDestEnd++= *pszSrc++;
                      cchRemaining--;
                  }
  
                  if (cchRemaining > 0)
                  {
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
                      }
                  }
                  else
                  {
                      // we are going to truncate pszDest
                      pszDestEnd--;
                      cchRemaining++;
  
                      hr = STRSAFE_E_INSUFFICIENT_BUFFER;
                  }
  
                  *pszDestEnd = '\0';
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = '\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = '\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCopyExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      wchar_t* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
      //        cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = L"";
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = 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
              {
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  while (cchRemaining && (*pszSrc != L'\0'))
                  {
                      *pszDestEnd++= *pszSrc++;
                      cchRemaining--;
                  }
  
                  if (cchRemaining > 0)
                  {
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
                      }
                  }
                  else
                  {
                      // we are going to truncate pszDest
                      pszDestEnd--;
                      cchRemaining++;
  
                      hr = STRSAFE_E_INSUFFICIENT_BUFFER;
                  }
  
                  *pszDestEnd = L'\0';
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = L'\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = L'\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCopyNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchSrc)
  {
      HRESULT hr = S_OK;
  
      if (cchDest == 0)
      {
          // can not null terminate a zero-byte dest buffer
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          while (cchDest && cchSrc && (*pszSrc != '\0'))
          {
              *pszDest++= *pszSrc++;
              cchDest--;
              cchSrc--;
          }
  
          if (cchDest == 0)
          {
              // we are going to truncate pszDest
              pszDest--;
              hr = STRSAFE_E_INSUFFICIENT_BUFFER;
          }
  
          *pszDest= '\0';
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCopyNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchSrc)
  {
      HRESULT hr = S_OK;
  
      if (cchDest == 0)
      {
          // can not null terminate a zero-byte dest buffer
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          while (cchDest && cchSrc && (*pszSrc != L'\0'))
          {
              *pszDest++= *pszSrc++;
              cchDest--;
              cchSrc--;
          }
  
          if (cchDest == 0)
          {
              // we are going to truncate pszDest
              pszDest--;
              hr = STRSAFE_E_INSUFFICIENT_BUFFER;
          }
  
          *pszDest= L'\0';
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCopyNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      char* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(char))    ||
      //        cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = "";
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = 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
              {
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  while (cchRemaining && cchSrc && (*pszSrc != '\0'))
                  {
                      *pszDestEnd++= *pszSrc++;
                      cchRemaining--;
                      cchSrc--;
                  }
  
                  if (cchRemaining > 0)
                  {
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
                      }
                  }
                  else
                  {
                      // we are going to truncate pszDest
                      pszDestEnd--;
                      cchRemaining++;
  
                      hr = STRSAFE_E_INSUFFICIENT_BUFFER;
                  }
  
                  *pszDestEnd = '\0';
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = '\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = '\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCopyNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      wchar_t* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
      //        cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = L"";
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = 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
              {
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  while (cchRemaining && cchSrc && (*pszSrc != L'\0'))
                  {
                      *pszDestEnd++= *pszSrc++;
                      cchRemaining--;
                      cchSrc--;
                  }
  
                  if (cchRemaining > 0)
                  {
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
                      }
                  }
                  else
                  {
                      // we are going to truncate pszDest
                      pszDestEnd--;
                      cchRemaining++;
  
                      hr = STRSAFE_E_INSUFFICIENT_BUFFER;
                  }
  
                  *pszDestEnd = L'\0';
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = L'\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = L'\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCatWorkerA(char* pszDest, size_t cchDest, const char* pszSrc)
  {
     HRESULT hr;
     size_t cchDestCurrent;
  
     hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
  
     if (SUCCEEDED(hr))
     {
         hr = StringCopyWorkerA(pszDest + cchDestCurrent,
                                cchDest - cchDestCurrent,
                                pszSrc);
     }
  
     return hr;
  }
  
  STRSAFEAPI StringCatWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc)
  {
     HRESULT hr;
     size_t cchDestCurrent;
  
     hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
  
     if (SUCCEEDED(hr))
     {
         hr = StringCopyWorkerW(pszDest + cchDestCurrent,
                                cchDest - cchDestCurrent,
                                pszSrc);
     }
  
     return hr;
  }
  
  STRSAFEAPI StringCatExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      char* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(char))    ||
      //        cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cchDestCurrent;
  
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest == 0) && (cbDest == 0))
                  {
                      cchDestCurrent = 0;
                  }
                  else
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
              else
              {
                  hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
  
                  if (SUCCEEDED(hr))
                  {
                      pszDestEnd = pszDest + cchDestCurrent;
                      cchRemaining = cchDest - cchDestCurrent;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = "";
              }
          }
          else
          {
              hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
  
              if (SUCCEEDED(hr))
              {
                  pszDestEnd = pszDest + cchDestCurrent;
                  cchRemaining = cchDest - cchDestCurrent;
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  // 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
              {
                  // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
                  // those flags through
                  hr = StringCopyExWorkerA(pszDestEnd,
                                           cchRemaining,
                                           (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)),
                                           pszSrc,
                                           &pszDestEnd,
                                           &cchRemaining,
                                           dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              // STRSAFE_NO_TRUNCATION is taken care of by StringCopyExWorkerA()
  
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = '\0';
                  }
              }
  
              if (dwFlags & STRSAFE_NULL_ON_FAILURE)
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = '\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCatExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      wchar_t* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
      //        cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          size_t cchDestCurrent;
  
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest == 0) && (cbDest == 0))
                  {
                      cchDestCurrent = 0;
                  }
                  else
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
              else
              {
                  hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
  
                  if (SUCCEEDED(hr))
                  {
                      pszDestEnd = pszDest + cchDestCurrent;
                      cchRemaining = cchDest - cchDestCurrent;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = L"";
              }
          }
          else
          {
              hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
  
              if (SUCCEEDED(hr))
              {
                  pszDestEnd = pszDest + cchDestCurrent;
                  cchRemaining = cchDest - cchDestCurrent;
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  // 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
              {
                  // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
                  // those flags through
                  hr = StringCopyExWorkerW(pszDestEnd,
                                           cchRemaining,
                                           (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)),
                                           pszSrc,
                                           &pszDestEnd,
                                           &cchRemaining,
                                           dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              // STRSAFE_NO_TRUNCATION is taken care of by StringCopyExWorkerW()
  
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = L'\0';
                  }
              }
  
              if (dwFlags & STRSAFE_NULL_ON_FAILURE)
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = L'\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCatNWorkerA(char* pszDest, size_t cchDest, const char* pszSrc, size_t cchMaxAppend)
  {
      HRESULT hr;
      size_t cchDestCurrent;
  
      hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
  
      if (SUCCEEDED(hr))
      {
          hr = StringCopyNWorkerA(pszDest + cchDestCurrent,
                                  cchDest - cchDestCurrent,
                                  pszSrc,
                                  cchMaxAppend);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCatNWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszSrc, size_t cchMaxAppend)
  {
      HRESULT hr;
      size_t cchDestCurrent;
  
      hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
  
      if (SUCCEEDED(hr))
      {
          hr = StringCopyNWorkerW(pszDest + cchDestCurrent,
                                  cchDest - cchDestCurrent,
                                  pszSrc,
                                  cchMaxAppend);
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCatNExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, const char* pszSrc, size_t cchMaxAppend, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      char* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
      size_t cchDestCurrent = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(char))    ||
      //        cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest == 0) && (cbDest == 0))
                  {
                      cchDestCurrent = 0;
                  }
                  else
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
              else
              {
                  hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
  
                  if (SUCCEEDED(hr))
                  {
                      pszDestEnd = pszDest + cchDestCurrent;
                      cchRemaining = cchDest - cchDestCurrent;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = "";
              }
          }
          else
          {
              hr = StringLengthWorkerA(pszDest, cchDest, &cchDestCurrent);
  
              if (SUCCEEDED(hr))
              {
                  pszDestEnd = pszDest + cchDestCurrent;
                  cchRemaining = cchDest - cchDestCurrent;
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  // 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
              {
                  // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
                  // those flags through
                  hr = StringCopyNExWorkerA(pszDestEnd,
                                            cchRemaining,
                                            (cchRemaining * sizeof(char)) + (cbDest % sizeof(char)),
                                            pszSrc,
                                            cchMaxAppend,
                                            &pszDestEnd,
                                            &cchRemaining,
                                            dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              // STRSAFE_NO_TRUNCATION is taken care of by StringCopyNExWorkerA()
  
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = '\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = '\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringCatNExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, const wchar_t* pszSrc, size_t cchMaxAppend, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      wchar_t* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
      size_t cchDestCurrent = 0;
  
  
      // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
      //        cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest == 0) && (cbDest == 0))
                  {
                      cchDestCurrent = 0;
                  }
                  else
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
              else
              {
                  hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
  
                  if (SUCCEEDED(hr))
                  {
                      pszDestEnd = pszDest + cchDestCurrent;
                      cchRemaining = cchDest - cchDestCurrent;
                  }
              }
  
              if (pszSrc == NULL)
              {
                  pszSrc = L"";
              }
          }
          else
          {
              hr = StringLengthWorkerW(pszDest, cchDest, &cchDestCurrent);
  
              if (SUCCEEDED(hr))
              {
                  pszDestEnd = pszDest + cchDestCurrent;
                  cchRemaining = cchDest - cchDestCurrent;
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  // 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
              {
                  // we handle the STRSAFE_FILL_ON_FAILURE and STRSAFE_NULL_ON_FAILURE cases below, so do not pass
                  // those flags through
                  hr = StringCopyNExWorkerW(pszDestEnd,
                                            cchRemaining,
                                            (cchRemaining * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)),
                                            pszSrc,
                                            cchMaxAppend,
                                            &pszDestEnd,
                                            &cchRemaining,
                                            dwFlags & (~(STRSAFE_FILL_ON_FAILURE | STRSAFE_NULL_ON_FAILURE)));
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              // STRSAFE_NO_TRUNCATION is taken care of by StringCopyNExWorkerW()
  
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = L'\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = L'\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringVPrintfWorkerA(char* pszDest, size_t cchDest, const char* pszFormat, va_list argList)
  {
      HRESULT hr = S_OK;
  
      if (cchDest == 0)
      {
          // can not null terminate a zero-byte dest buffer
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          int iRet;
          size_t cchMax;
  
          // leave the last space for the null terminator
          cchMax = cchDest - 1;
  
          iRet = _vsnprintf(pszDest, cchMax, pszFormat, argList);
          // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
  
          if ((iRet < 0) || (((size_t)iRet) > cchMax))
          {
              // need to null terminate the string
              pszDest += cchMax;
              *pszDest = '\0';
  
              // 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';
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringVPrintfWorkerW(wchar_t* pszDest, size_t cchDest, const wchar_t* pszFormat, va_list argList)
  {
      HRESULT hr = S_OK;
  
      if (cchDest == 0)
      {
          // can not null terminate a zero-byte dest buffer
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          int iRet;
          size_t cchMax;
  
          // leave the last space for the null terminator
          cchMax = cchDest - 1;
  
          iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
          // 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';
  
              // 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';
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringVPrintfExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const char* pszFormat, va_list argList)
  {
      HRESULT hr = S_OK;
      char* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(char))    ||
      //        cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
  
              if (pszFormat == NULL)
              {
                  pszFormat = "";
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = 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
              {
                  int iRet;
                  size_t cchMax;
  
                  // leave the last space for the null terminator
                  cchMax = cchDest - 1;
  
                  iRet = _vsnprintf(pszDest, cchMax, pszFormat, argList);
                  // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
  
                  if ((iRet < 0) || (((size_t)iRet) > cchMax))
                  {
                      // we have truncated pszDest
                      pszDestEnd = pszDest + cchMax;
                      cchRemaining = 1;
  
                      // need to null terminate the string
                      *pszDestEnd = '\0';
  
                      hr = STRSAFE_E_INSUFFICIENT_BUFFER;
                  }
                  else if (((size_t)iRet) == cchMax)
                  {
                      // string fit perfectly
                      pszDestEnd = pszDest + cchMax;
                      cchRemaining = 1;
  
                      // need to null terminate the string
                      *pszDestEnd = '\0';
                  }
                  else if (((size_t)iRet) < cchMax)
                  {
                      // there is extra room
                      pszDestEnd = pszDest + iRet;
                      cchRemaining = cchDest - iRet;
  
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
                      }
                  }
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = '\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = '\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringVPrintfExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags, const wchar_t* pszFormat, va_list argList)
  {
      HRESULT hr = S_OK;
      wchar_t* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(wchar_t)) ||
      //        cbDest == (cchDest * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
  
              if (pszFormat == NULL)
              {
                  pszFormat = L"";
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest == 0)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = 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
              {
                  int iRet;
                  size_t cchMax;
  
                  // leave the last space for the null terminator
                  cchMax = cchDest - 1;
  
                  iRet = _vsnwprintf(pszDest, cchMax, pszFormat, argList);
                  // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax));
  
                  if ((iRet < 0) || (((size_t)iRet) > cchMax))
                  {
                      // we have truncated pszDest
                      pszDestEnd = pszDest + cchMax;
                      cchRemaining = 1;
  
                      // need to null terminate the string
                      *pszDestEnd = L'\0';
  
                      hr = STRSAFE_E_INSUFFICIENT_BUFFER;
                  }
                  else if (((size_t)iRet) == cchMax)
                  {
                      // string fit perfectly
                      pszDestEnd = pszDest + cchMax;
                      cchRemaining = 1;
  
                      // need to null terminate the string
                      *pszDestEnd = L'\0';
                  }
                  else if (((size_t)iRet) < cchMax)
                  {
                      // there is extra room
                      pszDestEnd = pszDest + iRet;
                      cchRemaining = cchDest - iRet;
  
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
                      }
                  }
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = L'\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = L'\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) || (hr == STRSAFE_E_INSUFFICIENT_BUFFER))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFEAPI StringLengthWorkerA(const char* psz, size_t cchMax, size_t* pcch)
  {
      HRESULT hr = S_OK;
      size_t cchMaxPrev = cchMax;
  
      while (cchMax && (*psz != '\0'))
      {
          psz++;
          cchMax--;
      }
  
      if (cchMax == 0)
      {
          // the string is longer than cchMax
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
  
      if (SUCCEEDED(hr) && pcch)
      {
          *pcch = cchMaxPrev - cchMax;
      }
  
      return hr;
  }
  
  STRSAFEAPI StringLengthWorkerW(const wchar_t* psz, size_t cchMax, size_t* pcch)
  {
      HRESULT hr = S_OK;
      size_t cchMaxPrev = cchMax;
  
      while (cchMax && (*psz != L'\0'))
      {
          psz++;
          cchMax--;
      }
  
      if (cchMax == 0)
      {
          // the string is longer than cchMax
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
  
      if (SUCCEEDED(hr) && pcch)
      {
          *pcch = cchMaxPrev - cchMax;
      }
  
      return hr;
  }
  #endif  // STRSAFE_INLINE
  
  #ifndef STRSAFE_LIB_IMPL
  STRSAFE_INLINE_API StringGetsExWorkerA(char* pszDest, size_t cchDest, size_t cbDest, char** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      char* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(char))    ||
      //        cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest <= 1)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  if (cchDest == 1)
                  {
                      *pszDestEnd = '\0';
                  }
  
                  hr = STRSAFE_E_INSUFFICIENT_BUFFER;
              }
              else
              {
                  char ch;
  
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  while ((cchRemaining > 1) && (ch = (char)getc(stdin)) != '\n')
                  {
                      if (ch == EOF)
                      {
                          if (pszDestEnd == pszDest)
                          {
                              // we failed to read anything from stdin
                              hr = STRSAFE_E_END_OF_FILE;
                          }
                          break;
                      }
  
                      *pszDestEnd = ch;
  
                      pszDestEnd++;
                      cchRemaining--;
                  }
  
                  if (cchRemaining > 0)
                  {
                      // there is extra room
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(char)) + (cbDest % sizeof(char)));
                      }
                  }
  
                  *pszDestEnd = '\0';
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = '\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = '\0';
                  }
              }
          }
      }
  
      if (SUCCEEDED(hr) ||
          (hr == STRSAFE_E_INSUFFICIENT_BUFFER) ||
          (hr == STRSAFE_E_END_OF_FILE))
      {
          if (ppszDestEnd)
          {
              *ppszDestEnd = pszDestEnd;
          }
  
          if (pcchRemaining)
          {
              *pcchRemaining = cchRemaining;
          }
      }
  
      return hr;
  }
  
  STRSAFE_INLINE_API StringGetsExWorkerW(wchar_t* pszDest, size_t cchDest, size_t cbDest, wchar_t** ppszDestEnd, size_t* pcchRemaining, unsigned long dwFlags)
  {
      HRESULT hr = S_OK;
      wchar_t* pszDestEnd = pszDest;
      size_t cchRemaining = 0;
  
      // ASSERT(cbDest == (cchDest * sizeof(char))    ||
      //        cbDest == (cchDest * sizeof(char)) + (cbDest % sizeof(char)));
  
      // only accept valid flags
      if (dwFlags & (~STRSAFE_VALID_FLAGS))
      {
          hr = STRSAFE_E_INVALID_PARAMETER;
      }
      else
      {
          if (dwFlags & STRSAFE_IGNORE_NULLS)
          {
              if (pszDest == NULL)
              {
                  if ((cchDest != 0) || (cbDest != 0))
                  {
                      // NULL pszDest and non-zero cchDest/cbDest is invalid
                      hr = STRSAFE_E_INVALID_PARAMETER;
                  }
              }
          }
  
          if (SUCCEEDED(hr))
          {
              if (cchDest <= 1)
              {
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  if (cchDest == 1)
                  {
                      *pszDestEnd = L'\0';
                  }
  
                  hr = STRSAFE_E_INSUFFICIENT_BUFFER;
              }
              else
              {
                  wchar_t ch;
  
                  pszDestEnd = pszDest;
                  cchRemaining = cchDest;
  
                  while ((cchRemaining > 1) && (ch = (wchar_t)getwc(stdin)) != L'\n')
                  {
                      if (ch == EOF)
                      {
                          if (pszDestEnd == pszDest)
                          {
                              // we failed to read anything from stdin
                              hr = STRSAFE_E_END_OF_FILE;
                          }
                          break;
                      }
  
                      *pszDestEnd = ch;
  
                      pszDestEnd++;
                      cchRemaining--;
                  }
  
                  if (cchRemaining > 0)
                  {
                      // there is extra room
                      if (dwFlags & STRSAFE_FILL_BEHIND_NULL)
                      {
                          memset(pszDestEnd + 1, STRSAFE_GET_FILL_PATTERN(dwFlags), ((cchRemaining - 1) * sizeof(wchar_t)) + (cbDest % sizeof(wchar_t)));
                      }
                  }
  
                  *pszDestEnd = L'\0';
              }
          }
      }
  
      if (FAILED(hr))
      {
          if (pszDest)
          {
              if (dwFlags & STRSAFE_FILL_ON_FAILURE)
              {
                  memset(pszDest, STRSAFE_GET_FILL_PATTERN(dwFlags), cbDest);
  
                  if (STRSAFE_GET_FILL_PATTERN(dwFlags) == 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
                  }
                  else if (cchDest > 0)
                  {
                      pszDestEnd = pszDest + cchDest - 1;
                      cchRemaining = 1;
  
                      // null terminate the end of the string
                      *pszDestEnd = L'\0';
                  }
              }
  
              if (dwFlags & (STRSAFE_NULL_ON_FAILURE | STRSAFE_NO_TRUNCATION))
              {
                  if (cchDest > 0)
                  {
                      pszDestEnd = pszDest;
                      cchRemaining = cchDest;
  
                      // null terminate the beginning of the string
                      *pszDestEnd = L'\0';
                  }
              }
          }
      }
  
      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_LIB_IMPL
  
  
  // Do not call these functions, they are worker functions for internal use within this file
  #ifdef DEPRECATE_SUPPORTED
  #pragma deprecated(StringCopyWorkerA)
  #pragma deprecated(StringCopyWorkerW)
  #pragma deprecated(StringCopyExWorkerA)
  #pragma deprecated(StringCopyExWorkerW)
  #pragma deprecated(StringCatWorkerA)
  #pragma deprecated(StringCatWorkerW)
  #pragma deprecated(StringCatExWorkerA)
  #pragma deprecated(StringCatExWorkerW)
  #pragma deprecated(StringCatNWorkerA)
  #pragma deprecated(StringCatNWorkerW)
  #pragma deprecated(StringCatNExWorkerA)
  #pragma deprecated(StringCatNExWorkerW)
  #pragma deprecated(StringVPrintfWorkerA)
  #pragma deprecated(StringVPrintfWorkerW)
  #pragma deprecated(StringVPrintfExWorkerA)
  #pragma deprecated(StringVPrintfExWorkerW)
  #pragma deprecated(StringLengthWorkerA)
  #pragma deprecated(StringLengthWorkerW)
  #else
  #define StringCopyWorkerA        StringCopyWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;
  #define StringCopyWorkerW        StringCopyWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW;
  #define StringCopyExWorkerA      StringCopyExWorkerA_instead_use_StringCchCopyA_or_StringCchCopyExA;
  #define StringCopyExWorkerW      StringCopyExWorkerW_instead_use_StringCchCopyW_or_StringCchCopyExW;
  #define StringCatWorkerA         StringCatWorkerA_instead_use_StringCchCatA_or_StringCchCatExA;
  #define StringCatWorkerW         StringCatWorkerW_instead_use_StringCchCatW_or_StringCchCatExW;
  #define StringCatExWorkerA       StringCatExWorkerA_instead_use_StringCchCatA_or_StringCchCatExA;
  #define StringCatExWorkerW       StringCatExWorkerW_instead_use_StringCchCatW_or_StringCchCatExW;
  #define StringCatNWorkerA        StringCatNWorkerA_instead_use_StringCchCatNA_or_StrincCbCatNA;
  #define StringCatNWorkerW        StringCatNWorkerW_instead_use_StringCchCatNW_or_StringCbCatNW;
  #define StringCatNExWorkerA      StringCatNExWorkerA_instead_use_StringCchCatNExA_or_StringCbCatNExA;
  #define StringCatNExWorkerW      StringCatNExWorkerW_instead_use_StringCchCatNExW_or_StringCbCatNExW;
  #define StringVPrintfWorkerA     StringVPrintfWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA;
  #define StringVPrintfWorkerW     StringVPrintfWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW;
  #define StringVPrintfExWorkerA   StringVPrintfExWorkerA_instead_use_StringCchVPrintfA_or_StringCchVPrintfExA;
  #define StringVPrintfExWorkerW   StringVPrintfExWorkerW_instead_use_StringCchVPrintfW_or_StringCchVPrintfExW;
  #define StringLengthWorkerA      StringLengthWorkerA_instead_use_StringCchLengthA_or_StringCbLengthA;
  #define StringLengthWorkerW      StringLengthWorkerW_instead_use_StringCchLengthW_or_StringCbLengthW;
  #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 - we need to undef and redef based on UNICODE setting
  #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 - they key off UNICODE also.
  #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
  
  // Then all the CRT names - we need to undef/redef based on _UNICODE value.
  #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_StringCbCopyA_or_StringCchCopyA;
  
  #undef wcscpy
  #define wcscpy      wcscpy_instead_use_StringCbCopyW_or_StringCchCopyW;
  
  #undef strcat
  #define strcat      strcat_instead_use_StringCbCatA_or_StringCchCatA;
  
  #undef wcscat
  #define wcscat      wcscat_instead_use_StringCbCatW_or_StringCchCatW;
  
  #undef sprintf
  #define sprintf     sprintf_instead_use_StringCbPrintfA_or_StringCchPrintfA;
  
  #undef swprintf
  #define swprintf    swprintf_instead_use_StringCbPrintfW_or_StringCchPrintfW;
  
  #undef vsprintf
  #define vsprintf    vsprintf_instead_use_StringCbVPrintfA_or_StringCchVPrintfA;
  
  #undef vswprintf
  #define vswprintf   vswprintf_instead_use_StringCbVPrintfW_or_StringCchVPrintfW;
  
  #undef _snprintf
  #define _snprintf   _snprintf_instead_use_StringCbPrintfA_or_StringCchPrintfA;
  
  #undef _snwprintf
  #define _snwprintf  _snwprintf_instead_use_StringCbPrintfW_or_StringCchPrintfW;
  
  #undef _vsnprintf
  #define _vsnprintf  _vsnprintf_instead_use_StringCbVPrintfA_or_StringCchVPrintfA;
  
  #undef _vsnwprintf
  #define _vsnwprintf _vsnwprintf_instead_use_StringCbVPrintfW_or_StringCchVPrintfW;
  
  #undef strcpyA
  #define strcpyA     strcpyA_instead_use_StringCbCopyA_or_StringCchCopyA;
  
  #undef strcpyW
  #define strcpyW     strcpyW_instead_use_StringCbCopyW_or_StringCchCopyW;
  
  #undef lstrcpy
  #define lstrcpy     lstrcpy_instead_use_StringCbCopy_or_StringCchCopy;
  
  #undef lstrcpyA
  #define lstrcpyA    lstrcpyA_instead_use_StringCbCopyA_or_StringCchCopyA;
  
  #undef lstrcpyW
  #define lstrcpyW    lstrcpyW_instead_use_StringCbCopyW_or_StringCchCopyW;
  
  #undef StrCpy
  #define StrCpy      StrCpy_instead_use_StringCbCopy_or_StringCchCopy;
  
  #undef StrCpyA
  #define StrCpyA     StrCpyA_instead_use_StringCbCopyA_or_StringCchCopyA;
  
  #undef StrCpyW
  #define StrCpyW     StrCpyW_instead_use_StringCbCopyW_or_StringCchCopyW;
  
  #undef _tcscpy
  #define _tcscpy     _tcscpy_instead_use_StringCbCopy_or_StringCchCopy;
  
  #undef _ftcscpy
  #define _ftcscpy    _ftcscpy_instead_use_StringCbCopy_or_StringCchCopy;
  
  #undef lstrcat
  #define lstrcat     lstrcat_instead_use_StringCbCat_or_StringCchCat;
  
  #undef lstrcatA
  #define lstrcatA    lstrcatA_instead_use_StringCbCatA_or_StringCchCatA;
  
  #undef lstrcatW
  #define lstrcatW    lstrcatW_instead_use_StringCbCatW_or_StringCchCatW;
  
  #undef StrCat
  #define StrCat      StrCat_instead_use_StringCbCat_or_StringCchCat;
  
  #undef StrCatA
  #define StrCatA     StrCatA_instead_use_StringCbCatA_or_StringCchCatA;
  
  #undef StrCatW
  #define StrCatW     StrCatW_instead_use_StringCbCatW_or_StringCchCatW;
  
  #undef StrNCat
  #define StrNCat     StrNCat_instead_use_StringCbCatN_or_StringCchCatN;
  
  #undef StrNCatA
  #define StrNCatA    StrNCatA_instead_use_StringCbCatNA_or_StringCchCatNA;
  
  #undef StrNCatW
  #define StrNCatW    StrNCatW_instead_use_StringCbCatNW_or_StringCchCatNW;
  
  #undef StrCatN
  #define StrCatN     StrCatN_instead_use_StringCbCatN_or_StringCchCatN;
  
  #undef StrCatNA
  #define StrCatNA    StrCatNA_instead_use_StringCbCatNA_or_StringCchCatNA;
  
  #undef StrCatNW
  #define StrCatNW    StrCatNW_instead_use_StringCbCatNW_or_StringCchCatNW;
  
  #undef _tcscat
  #define _tcscat     _tcscat_instead_use_StringCbCat_or_StringCchCat;
  
  #undef _ftcscat
  #define _ftcscat    _ftcscat_instead_use_StringCbCat_or_StringCchCat;
  
  #undef wsprintf
  #define wsprintf    wsprintf_instead_use_StringCbPrintf_or_StringCchPrintf;
  
  #undef wsprintfA
  #define wsprintfA   wsprintfA_instead_use_StringCbPrintfA_or_StringCchPrintfA;
  
  #undef wsprintfW
  #define wsprintfW   wsprintfW_instead_use_StringCbPrintfW_or_StringCchPrintfW;
  
  #undef wvsprintf
  #define wvsprintf   wvsprintf_instead_use_StringCbVPrintf_or_StringCchVPrintf;
  
  #undef wvsprintfA
  #define wvsprintfA  wvsprintfA_instead_use_StringCbVPrintfA_or_StringCchVPrintfA;
  
  #undef wvsprintfW
  #define wvsprintfW  wvsprintfW_instead_use_StringCbVPrintfW_or_StringCchVPrintfW;
  
  #undef _vstprintf
  #define _vstprintf  _vstprintf_instead_use_StringCbVPrintf_or_StringCchVPrintf;
  
  #undef _vsntprintf
  #define _vsntprintf _vsntprintf_instead_use_StringCbVPrintf_or_StringCchVPrintf;
  
  #undef _stprintf
  #define _stprintf   _stprintf_instead_use_StringCbPrintf_or_StringCchPrintf;
  
  #undef _sntprintf
  #define _sntprintf  _sntprintf_instead_use_StringCbPrintf_or_StringCchPrintf;
  
  #undef _getts
  #define _getts      _getts_instead_use_StringCbGets_or_StringCchGets;
  
  #undef gets
  #define gets        _gets_instead_use_StringCbGetsA_or_StringCchGetsA;
  
  #undef _getws
  #define _getws      _getws_instead_use_StringCbGetsW_or_StringCchGetsW;
  
  #endif  // !DEPRECATE_SUPPORTED
  #endif  // !STRSAFE_NO_DEPRECATE
  
  #ifdef _NTSTRSAFE_H_INCLUDED_
  #pragma warning(pop)
  #endif // _NTSTRSAFE_H_INCLUDED_
  
  #endif  // _STRSAFE_H_INCLUDED_
  