Introduction
This tip explains a simpler way of using Safe APIs which helps in easy code migration to newer versions of Visual Studio (i.e., newer versions of VC Compiler).
Background
While using the APIs strcat
, strcpy
, etc., many a times, there are chances of application crash due to buffer overflow. Due to this, Microsoft has come up with a newer set of safe APIs StringCb*
APIs. When it comes to code migration to newer versions of Visual Studio, we do get lot of warnings regarding usage of safe APIs. Most of the times, due to scarcity of time, we tend to live with the old code since it takes time to change the old code and migrate it to the newer APIs and even we are worried about the testing part of the same. Like when we get warning from the compiler for using strcat_s
, we do ignore it most of the times and try to live with existing strcat
. With this tip, it would be easier to make such changes to the code in a simple and efficient manner.
StringCbCopy
Let's start with StringCbCopy
API, it has the following signature:
HRESULT StringCbCopy(
_Out_ LPTSTR pszDest,
_In_ size_t cbDest,
_In_ LPCTSTR pszSrc
);
Cb
in StringCbCopy
stands for "Count of bytes":
pszDest
: The destination buffer, which receives the copied stringpszSrc
: The source string cbDest
: This is the maximum length of bytes allowed for copying the data
Parameters pszDest
and pszSrc
remain the same as strcpy
so we need to just concentrate on "cbDest
" parameter.
Let's introduce a macro which will do the job for us:
#ifdef _UNICODE
#define STRING_SIZE_IN_BYTES(x) (_countof(x) * sizeof(WCHAR))
#else
#define STRING_SIZE_IN_BYTES(x) (_countof(x) * sizeof(CHAR))
#endif
Macro STRING_SIZE_IN_BYTES
calculates the number of characters in the string
array and then multiplies it by type of character and hence gets the complete size of the Array in Bytes.
Example
WCHAR wszTemp[1024] = {0}
STRING_SIZE_IN_BYTES(wszTemp)
will give the size as 1024 * 2 = 2048 bytes, so maximum of 2048 bytes can be copied to this array.
CHAR szTemp[1024] = {0}
STRING_SIZE_IN_BYTES(szTemp)
will give the size as 1024 * 1 = 1024 bytes, so maximum of 1024 bytes can be copied to this array.
Now let's add the macro which will replace the operation of StringCbCopy
.
#define SafeStrCbCopy(x, y) StringCbCopy(x, STRING_SIZE_IN_BYTES(x), y)
Example
SafeStrCbCopy(wszDest, wszSource);
This will do the job of safe copy for us.
StringCbCat
On similar lines, we can create Macro for StringCbCat
API.
#define SafeStrCbCat(x, y) StringCbCat(x, STRING_SIZE_IN_BYTES(x), y)
Example
SafeStrCbCat(wszDest, wszSource);
StringCchCopy
On similar lines as StringCbCopy
, let's check with StringCchCopy
:
HRESULT StringCchCopy(
_Out_ LPTSTR pszDest,
_In_ size_t cchDest,
_In_ LPCTSTR pszSrc
);
The only difference is size_t cchDest
which is the size of string
in "Count of Characters" rather than "Count of Bytes". Let's define a similar macro for the same.
#define STRING_SIZE_IN_CHARACTERS(x) (_countof(x))
This will give the number of characters in the string
irrespective of type of string
, i.e., WCHAR
or CHAR
macro to replace StrinCchCopy
.
#define SafeStrCchCopy(x, y) StringCchCopy(x, STRING_SIZE_IN_CHARACTERS(x), y)
Example
SafeStrCchCopy(wszDest, wszSource);
StringCchCat
#define SafeStrCchCat(x, y) StringCchCat(x, STRING_SIZE_IN_CHARACTERS(x), y)
Example
SafeStrCchCat(wszDest, wszSource);
Limitations
One drawback with the above approach is that it cannot be used for pointer to string
s, e.g.: char* pszString
since, we are using _countof
API call which works only on static
arrays. For pointer to string
s, different set of macros needs to be defined which will define the size of the array.
#define MAX_BUFF_SIZE 1024 //Max size which the buffer can hold
#define SafeStrCbCopy(x, y) StringCbCopy(x, MAX_BUFF_SIZE, y)
Summary
Overall, now it should be easier to migrate the code to Safe APIs.
History
- 2013-06-10: Initial version