需要 #define 来包含安全字符串函数的 Visual Studio 版本(以避免 _CRT_SECURE_NO_DEPRECATE)

发布于 2024-11-13 08:01:01 字数 871 浏览 1 评论 0原文

不久前,我尝试使用 Visual Studio 2010 编译一个 MFC 程序,该程序使用了我在 Visual Studio 2003 中编写的库。毫不奇怪,我收到了一堆关于弃用和使用各种字符串函数的安全版本的警告。

然后我更新了库中的相关函数以使用安全函数,并且它编译得很好。

后来我尝试在另一个系统上使用 Visual Studio 2003 再次编译它,但因为安全功能不存在而烦恼。


我决定创建一种混合方法,使我能够编译在任一环境中使用该库的程序,如果可用,则使用安全函数,如果不可用,则将它们别名为旧函数。

起初,我考虑检查每个函数以查看是否存在安全版本,但这行不通,并且需要为每个函数单独工作:

#ifndef strcpy_s
    #define strcpy_s(a,b,c) strcpy(a,c)
#endif

#ifndef strcat_s
    #define strcat_s(a,b,c) strcat(a,c)
#endif

…

所以我试图找出一种确定安全函数是否存在的方法。我知道它们是在 Visual Studio 2005 中引入的,但是是否有 #define 或可以按如下方式使用的东西?

#ifndef SECURE_FUNCTIONS  // or #ifdef VS_VER_2005, #if (VS_VER >= 0x2005) etc.
    #define strcpy_s(a,b,c) strcpy(a,c)
    #define strcat_s(a,b,c) strcat(a,c)
    …
#endif

我检查了 crtdefs.h 但没有发现任何有用的东西。

A while back I tried to use Visual Studio 2010 to compile an MFC program that used a library I had written in Visual Studio 2003. Not surprisingly, I got a bunch of warnings about deprecation and using the secure versions of various string functions.

I then updated the relevant functions in the library to use the secure functions and it compiled fine.

I later tried to compile it again on the other system with Visual Studio 2003 and got nagged about the secure functions not existing.

I decided to create a hybrid approach that would allow me to compile programs that use the library in either environment, making use of the secure functions if available, and if not, aliasing them to the old ones.

At first I considered checking each function to see if a secure version exists, but that won’t work and requires separate work for each and every function:

#ifndef strcpy_s
    #define strcpy_s(a,b,c) strcpy(a,c)
#endif

#ifndef strcat_s
    #define strcat_s(a,b,c) strcat(a,c)
#endif

…

So what I’m trying to figure out is a way to determine if the secure functions exist. I know that they were introduced in Visual Studio 2005, but is there a #define or something that can be used as follows?

#ifndef SECURE_FUNCTIONS  // or #ifdef VS_VER_2005, #if (VS_VER >= 0x2005) etc.
    #define strcpy_s(a,b,c) strcpy(a,c)
    #define strcat_s(a,b,c) strcat(a,c)
    …
#endif

I checked crtdefs.h but found nothing useful.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我不是你的备胎 2024-11-20 08:01:01

我找到了解决办法; _MSC_VER 宏/定义使这变得简单。由于安全字符串函数已添加到 Visual Studio 2005(VC++版本1400,那么就足够了做这样的事情:

#if _MSC_VER < 1400
    #define  _itoa_s(a,b,c)             _itoa(a,b,c)
    #define  wcscpy_s(a,b,c)            wcscpy(a,c)
    #define  _tprintf_s                 _tprintf
    #define  _sntprintf_s(a,b,c,d,...)  _sntprintf(a,c,d,...)
    …
#endif

现在,当代码在 VS2005+ 下编译时,它将具有额外的安全性,而当在 VS2003- 上编译时,它仍然会在不修改的情况下编译,尽管没有额外的安全性 。

这使得移植和更新变得更加容易,因为即使您还无法使用 VS2005+ 编译它们,您也可以更新库函数并在代码中使用安全字符串函数,这样当您升级编译器时,您就不会这样做 必须对库或代码进行任何更改才能获得好处,这也使得在旧版本和新版本的 Visual Studio 上同时处理相同的代码库变得更容易(至少在某种程度上)。

I found a solution; the _MSC_VER macro/define makes this simple. Since the secure string functions were added in Visual Studio 2005 (VC++ version 1400, then it is sufficient to do something like this:

#if _MSC_VER < 1400
    #define  _itoa_s(a,b,c)             _itoa(a,b,c)
    #define  wcscpy_s(a,b,c)            wcscpy(a,c)
    #define  _tprintf_s                 _tprintf
    #define  _sntprintf_s(a,b,c,d,...)  _sntprintf(a,c,d,...)
    …
#endif

Now when the code is compiled under VS2005+, it will have the added security, and when compiled on VS2003-, it will still compile without modification, albeit without the extra security.

This makes porting and updating easier because you can update the library functions and use the secure string functions in the code even if you can’t compile them with VS2005+ just yet. This way when you do upgrade the compiler, you won’t have to make any changes to the library or the code to reap the benefits. It also makes it easier to work on the same code-base on older and newer versions of Visual Studio concurrently (at least to some degree).

苏佲洛 2024-11-20 08:01:01

Microsoft 的一些安全函数是 C++11 的一部分,因此它们现在应该是可移植的。
安全函数与传统函数之间的一个重要区别是异常行为,有时还包括返回值。许多程序员都不关注其中任何一个;这些差异常常被忽视。
例如,snprintf 的异常行为与 _snprintf_s 不同:

snprintf 返回打印字符串所需的字符数,而不是
无论缓冲区的大小如何,都对终止空字符进行计数。
我不认为 snprintf 本身会引发异常,而是无效的内存访问
会。

如果 buff 足够大,_snprintf_s 返回与 snprintf 相同的值,但是
如果 buff 太小,或者 buff 或 fmt 是 NULL 指针,则 _snprintf_s 调用
无效参数处理程序,分别设置 errno = ERANGE 或 EINVAL,
并返回-1。
如果异常行为在您的遗留代码中很重要,那么当您从旧的传统函数转换为安全版本时请注意。

我在 Microsoft 的安全“_s”函数上苦苦挣扎了几年,尤其是在 Visual Studio 中编写为 Windows 平台编译的代码以及使用 gcc/g++ 为 'nix 平台编译的代码时。重用旧源代码时也很痛苦,因为将 fprintf() 更改为 fprintf_s() 等代码是一件苦差事。 _CRT_SECURE_NO_DEPRICAT 宏会抑制弃用警告,但我从来不喜欢关闭编译器警告而不解决根本问题;发出警告是有原因的。

我的临时补丁(我承认我仍然不时使用)是一个包含文件,其中充满了宏和一些内联函数来映射传统函数和安全函数。当然,映射不会模仿异常行为和返回值。如果您愿意,您也可以编写函数来模仿它,但在某些时候,使用安全函数并更改旧代码来执行相同的操作会更容易。此外,某些安全函数无法轻松映射,例如 sprinf 到 sprintf_s。

这是我的包含文件(注释比代码多,但值得一读,恕我直言):

#pragma once
#if !defined(FCN_S_MACROS_H)
   #define   FCN_S_MACROS_H 

///////////////////////////////////////////////////////////////////////////////
//
// These macros provide (partial) compatibility of source code developed 
// for older MSVC versions and non-MSVC c++ compilers for some of Microsoft's
// security enhanced funcions, e.g. fscanf_s, sscanf_s, printf_s, strcpy_s,
// fopen_s.... Of course the standard functions still work in MSVS, but
// the choice is either to live with the annoying warning messages (bad idea)
// or set a compiler directive to stop the warnings (bad idea--there might
// important warnings as well as the annoying ones).
//
// It looks like a lot of the secure functions are now part of C++11. Those
// functions should be used in new code. The macros below can be used for
// for as a bridge for older code, but at some point it would be best to
// upgrade the code with the more secure functions. Eventually, the depricated
// functions may be removed, but probably not for a long time.
//
// Bill Brinson
// 21 February 2011 (updated once or twice since then).
//
///////////////////////////////////////////////////////////////////////////////
// Does It Work:
//
//    *** No warranty expresed nor implied. Use at your own risk. ***
//
// I've tested most of the standard function to MS specific macros. They
// work in my codes so far, but Murphy says ...
//
// I usually write code in MSVS, using the standard functions, then port to
// linux if needed. I haven't though as much about the inverse macros,
// nor have I tested all of them. They seem unnecessary anyway. Too bad: they
// tend to be simpler.
// Test the macros yourself, and investigate exception behaviors before using
// them.
//
///////////////////////////////////////////////////////////////////////////////
//
// String Functions With No Count Parameter:
//
// The string functions that don't specify the maximum number of bytes to copy
// into the buffer (sprintf, strcpy, ...) are a problem. Using the sizeof()
// operator is a terrible idea (I should know--I though of it myself.
// Fortunately sanity prevailed before I used it in real code.
// In case you are tempted: the sizeof(buff) method WILL FAIL at runtime
// if buffer is not defined as an array (char cstring[32] or similar). For
// dynamically allocated memory, sizeof(ptr) returns the size of the pointer
// itself, not the allocated memory, so if your are copying no more than four
// bytes and you allocated at least that many, it would work due to blind luck.
// _memsize() (MS specific, but that's were it's needed) can be used for
// memory allocated with malloc, calloc, or realloc, but it doesn't work
// for char buff[size] or memory allocated with the new opperator. Does anyone
// still use malloc()?
// Overloaded functions taking char[] and *char to differentiate them might
// work for arrays and pointers to memory allocated by malloc et. al., but not
// for pointers to memory allocated by new (which have the same type, so not
// differentiated by the overloaded functions).
// If someone an idea, please let me know.
//
// This should only be an issue for legacy code; use snprintf, strncpy, etc.
// in new code (which you already do, right?), and make sure count has an
// appropriate value. For legacy code containing sprintf, strcpy, etc.,
// I've decided to just bite the bullet: let the MS compiler point out the
// unsafe functions, then change them to the safer (but standard) versions
// that specify the allowable number of bytes to copy.
//
///////////////////////////////////////////////////////////////////////////////
// Exception Behavior:
//
// This is an important difference between the MS decreed safe functions and
// the traditional C/C++ functions.
// I suspect all of the MS specific functions have different exception behaviors.
// For example the exception behavior of snprintf is different from _snprintf_s:
//    snprintf returns the number of characters required to print the string, not
//    counting the terminating null character, regardless of the size of the buffer.
//    I don't think snprintf raises exceptions.
//    
//    _snprintf_s returns same value as snprintf if buff is sufficiently large, but
//    if buff is too small, or buff or fmt is a NULL pointer, _snprintf_s invokes the 
//    invalid parameter handler, sets errno = ERANGE or EINVAL, respectively,
//    and returns -1.
// If return values and exception behaviors are important in your code, create
// your own functions to handle the conversions.
//
///////////////////////////////////////////////////////////////////////////////
// Overloads:
//
// The macros below handle only the most common (for me, at least) overloads.
//
///////////////////////////////////////////////////////////////////////////////
// Suggetions:
//
// Yes please. There are a ton of these MS specific "safe" functions. I've
// only done a few.
//
///////////////////////////////////////////////////////////////////////////////
// License:
//
// I suppose someone might care about this.
// Sure, use what you like, delete what you don't. Modify it to your hearts
// content.
// I wouldn't mind getting an attaboy or something if it works (not required).
// If it doesn't work, blame MS.
//
///////////////////////////////////////////////////////////////////////////////

// #include <cstdlib> // Do I need cstdlib? Hmm...maybe for sizeof()?
   #include <cstdio>
   #include <string> // Need this for _stricmp
   using namespace std;

   // _MSC_VER = 1400 is MSVC 2005. _MSC_VER = 1600 (MSVC 2010) was the current
   // value when I wrote (some of) these macros.
   #if (defined(_MSC_VER) && (_MSC_VER >= 1400) )

      // The function plus macro strategy could be used for most of the offending
      // MS functions, particularly for maintaining consistent exception behaviors
      // and return values. T
      // inline is for run time efficiency, but the compiler is not
      // constrained to comply.
      inline extern
      FILE*   fcnSMacro_fopen_s(char *fname, char *mode)
      {  FILE *fptr;
         fopen_s(&fptr, fname, mode);
         return fptr;
      }
      #define fopen(fname, mode)            fcnSMacro_fopen_s((fname), (mode))

      inline extern
      char* fcnSMacro_strtok_s(char *strng, char *delimiters)
      {  static char *cntx; // This static variable causes the same problem
                            // as the original strtok: can't alternate search
                            // strings in the same process (MS says "thread").
         if(strng != NULL) *cntx = NULL;
         char *cptr = strtok_s(strng, delimiters, &cntx);
         return cptr;
      }
      #define strtok(strng, delim)          fcnSMacro_strtok_s((strng), (delim))

      #define fcloseall()                   _fcloseall()

      // I substituded count+1 for snprintf's buffer size argument. For well
      // written code, the buffer size should be at least one more than count
      // to leave room for the terminating '\0'.
      #define snprintf(buff, count, ...)    _snprintf_s((buff), (count+1), (count), __VA_ARGS__)

      #define printf(...)                   printf_s(__VA_ARGS__)
      #define fprintf(fptr, ...)            fprintf_s((fptr), __VA_ARGS__)

      // I don't have a solution for mapping sprinf to sprintf_s. There are other
      // functions like this.
//    #define sprintf                       ???
//    #define strcpy(s1, s2)                ???

      // These mappings look trivial, but the secure functions likely have different
      // exception behaviors and maybe different return values.
      #define fscanf                        fscanf_s
      #define sscanf                        sscanf_s
      #define scanf                         scanf_s                        

      // strcmpi is deprecated in VS 2015. Don't know about 2013 or 2014
      #define strcmpi                       _stricmp

      // No conversion needed for strncmp (yet). I guess MS hasn't gotten around
      // to it yet.
//    #define strncmp                       ???

      #define strncpy(dest, source, count)  strcpy_s((dest), (count), (source))
      #define strncat(dest, source, count)  strcat_s((dest), (count), (source))

   #else
      // I usually write code in MSVS, using the standard functions, then port to linux if needed.
      // I haven't though as much about the inverse macros, nor have I tested all of them.
      // Test them yourself and investigate exception behaviors before using them.

      #define fscanf_s                         fscanf
      #define sscanf_s                         sscanf
      #define scanf_s                          scanf
      #define printf_s                         printf
      #define sprintf_s                        snprintf
      #define fprintf_s                        fprintf
      #define strcpy_s(dest, count, source)    strncpy( (dest), (source), (count) )
      #define fopen_s(fp, fmt, mode)          *(fp)=fopen( (fmt), (mode))
      #define _fcloseall                       fcloseall
      #define strtok_s                         strtok
      #define _strcmpi                         strcmpi
   #endif //_MSC_VER
#endif // FCN_S_MACROS_H

Some of Microsoft's secure functions are part of C++11, so they should be portable now.
An important difference between the secure functions and the traditional ones are the exception behaviors, and sometimes the return values. Many programmers don't pay attention to either; those differences are often overlooked.
For example the exception behavior of snprintf is different from _snprintf_s:

snprintf returns the number of characters required to print the string, not
counting the terminating null character, regardless of the size of the buffer.
I don't think snprintf itself raises exceptions, but an invalid memory access
would.

_snprintf_s returns same value as snprintf if buff is sufficiently large, but
if buff is too small, or buff or fmt is a NULL pointer, _snprintf_s invokes the
invalid parameter handler, sets errno = ERANGE or EINVAL, respectively,
and returns -1.
If exception behavior is important in your legacy code, pay attention when you convert from the old traditional functions to the secure versions.

I struggled with Microsoft's secure "_s" functions for a few years, especially when writing code that was compiled for Windows platforms in Visual Studio and for 'nix platforms using gcc/g++. It was also an pain when reusing old source code because it's a chore to go through the code changing fprintf() to fprintf_s(), etc. The _CRT_SECURE_NO_DEPRICAT macro suppresses the deprecation warnings, but I've never been a fan of shutting up compiler warnings without fixing the underlying problems; the warnings are issued for a reason.

My temporary patch (which I'll admit I still use from time to time) was an include file full of macros and a few inline functions to map the traditional and secure functions. Of course the mappings don't mimic the exception behaviors and return values. If you want, you could write the functions to mimic that also, but at some point it's just going to be easier to use the secure functions and change your old code to do the same. Also, some of the secure functions cannot be readily mapped, e.g. sprinf to sprintf_s.

Here's my include file (more comments than code, but worth reading them IMHO):

#pragma once
#if !defined(FCN_S_MACROS_H)
   #define   FCN_S_MACROS_H 

///////////////////////////////////////////////////////////////////////////////
//
// These macros provide (partial) compatibility of source code developed 
// for older MSVC versions and non-MSVC c++ compilers for some of Microsoft's
// security enhanced funcions, e.g. fscanf_s, sscanf_s, printf_s, strcpy_s,
// fopen_s.... Of course the standard functions still work in MSVS, but
// the choice is either to live with the annoying warning messages (bad idea)
// or set a compiler directive to stop the warnings (bad idea--there might
// important warnings as well as the annoying ones).
//
// It looks like a lot of the secure functions are now part of C++11. Those
// functions should be used in new code. The macros below can be used for
// for as a bridge for older code, but at some point it would be best to
// upgrade the code with the more secure functions. Eventually, the depricated
// functions may be removed, but probably not for a long time.
//
// Bill Brinson
// 21 February 2011 (updated once or twice since then).
//
///////////////////////////////////////////////////////////////////////////////
// Does It Work:
//
//    *** No warranty expresed nor implied. Use at your own risk. ***
//
// I've tested most of the standard function to MS specific macros. They
// work in my codes so far, but Murphy says ...
//
// I usually write code in MSVS, using the standard functions, then port to
// linux if needed. I haven't though as much about the inverse macros,
// nor have I tested all of them. They seem unnecessary anyway. Too bad: they
// tend to be simpler.
// Test the macros yourself, and investigate exception behaviors before using
// them.
//
///////////////////////////////////////////////////////////////////////////////
//
// String Functions With No Count Parameter:
//
// The string functions that don't specify the maximum number of bytes to copy
// into the buffer (sprintf, strcpy, ...) are a problem. Using the sizeof()
// operator is a terrible idea (I should know--I though of it myself.
// Fortunately sanity prevailed before I used it in real code.
// In case you are tempted: the sizeof(buff) method WILL FAIL at runtime
// if buffer is not defined as an array (char cstring[32] or similar). For
// dynamically allocated memory, sizeof(ptr) returns the size of the pointer
// itself, not the allocated memory, so if your are copying no more than four
// bytes and you allocated at least that many, it would work due to blind luck.
// _memsize() (MS specific, but that's were it's needed) can be used for
// memory allocated with malloc, calloc, or realloc, but it doesn't work
// for char buff[size] or memory allocated with the new opperator. Does anyone
// still use malloc()?
// Overloaded functions taking char[] and *char to differentiate them might
// work for arrays and pointers to memory allocated by malloc et. al., but not
// for pointers to memory allocated by new (which have the same type, so not
// differentiated by the overloaded functions).
// If someone an idea, please let me know.
//
// This should only be an issue for legacy code; use snprintf, strncpy, etc.
// in new code (which you already do, right?), and make sure count has an
// appropriate value. For legacy code containing sprintf, strcpy, etc.,
// I've decided to just bite the bullet: let the MS compiler point out the
// unsafe functions, then change them to the safer (but standard) versions
// that specify the allowable number of bytes to copy.
//
///////////////////////////////////////////////////////////////////////////////
// Exception Behavior:
//
// This is an important difference between the MS decreed safe functions and
// the traditional C/C++ functions.
// I suspect all of the MS specific functions have different exception behaviors.
// For example the exception behavior of snprintf is different from _snprintf_s:
//    snprintf returns the number of characters required to print the string, not
//    counting the terminating null character, regardless of the size of the buffer.
//    I don't think snprintf raises exceptions.
//    
//    _snprintf_s returns same value as snprintf if buff is sufficiently large, but
//    if buff is too small, or buff or fmt is a NULL pointer, _snprintf_s invokes the 
//    invalid parameter handler, sets errno = ERANGE or EINVAL, respectively,
//    and returns -1.
// If return values and exception behaviors are important in your code, create
// your own functions to handle the conversions.
//
///////////////////////////////////////////////////////////////////////////////
// Overloads:
//
// The macros below handle only the most common (for me, at least) overloads.
//
///////////////////////////////////////////////////////////////////////////////
// Suggetions:
//
// Yes please. There are a ton of these MS specific "safe" functions. I've
// only done a few.
//
///////////////////////////////////////////////////////////////////////////////
// License:
//
// I suppose someone might care about this.
// Sure, use what you like, delete what you don't. Modify it to your hearts
// content.
// I wouldn't mind getting an attaboy or something if it works (not required).
// If it doesn't work, blame MS.
//
///////////////////////////////////////////////////////////////////////////////

// #include <cstdlib> // Do I need cstdlib? Hmm...maybe for sizeof()?
   #include <cstdio>
   #include <string> // Need this for _stricmp
   using namespace std;

   // _MSC_VER = 1400 is MSVC 2005. _MSC_VER = 1600 (MSVC 2010) was the current
   // value when I wrote (some of) these macros.
   #if (defined(_MSC_VER) && (_MSC_VER >= 1400) )

      // The function plus macro strategy could be used for most of the offending
      // MS functions, particularly for maintaining consistent exception behaviors
      // and return values. T
      // inline is for run time efficiency, but the compiler is not
      // constrained to comply.
      inline extern
      FILE*   fcnSMacro_fopen_s(char *fname, char *mode)
      {  FILE *fptr;
         fopen_s(&fptr, fname, mode);
         return fptr;
      }
      #define fopen(fname, mode)            fcnSMacro_fopen_s((fname), (mode))

      inline extern
      char* fcnSMacro_strtok_s(char *strng, char *delimiters)
      {  static char *cntx; // This static variable causes the same problem
                            // as the original strtok: can't alternate search
                            // strings in the same process (MS says "thread").
         if(strng != NULL) *cntx = NULL;
         char *cptr = strtok_s(strng, delimiters, &cntx);
         return cptr;
      }
      #define strtok(strng, delim)          fcnSMacro_strtok_s((strng), (delim))

      #define fcloseall()                   _fcloseall()

      // I substituded count+1 for snprintf's buffer size argument. For well
      // written code, the buffer size should be at least one more than count
      // to leave room for the terminating '\0'.
      #define snprintf(buff, count, ...)    _snprintf_s((buff), (count+1), (count), __VA_ARGS__)

      #define printf(...)                   printf_s(__VA_ARGS__)
      #define fprintf(fptr, ...)            fprintf_s((fptr), __VA_ARGS__)

      // I don't have a solution for mapping sprinf to sprintf_s. There are other
      // functions like this.
//    #define sprintf                       ???
//    #define strcpy(s1, s2)                ???

      // These mappings look trivial, but the secure functions likely have different
      // exception behaviors and maybe different return values.
      #define fscanf                        fscanf_s
      #define sscanf                        sscanf_s
      #define scanf                         scanf_s                        

      // strcmpi is deprecated in VS 2015. Don't know about 2013 or 2014
      #define strcmpi                       _stricmp

      // No conversion needed for strncmp (yet). I guess MS hasn't gotten around
      // to it yet.
//    #define strncmp                       ???

      #define strncpy(dest, source, count)  strcpy_s((dest), (count), (source))
      #define strncat(dest, source, count)  strcat_s((dest), (count), (source))

   #else
      // I usually write code in MSVS, using the standard functions, then port to linux if needed.
      // I haven't though as much about the inverse macros, nor have I tested all of them.
      // Test them yourself and investigate exception behaviors before using them.

      #define fscanf_s                         fscanf
      #define sscanf_s                         sscanf
      #define scanf_s                          scanf
      #define printf_s                         printf
      #define sprintf_s                        snprintf
      #define fprintf_s                        fprintf
      #define strcpy_s(dest, count, source)    strncpy( (dest), (source), (count) )
      #define fopen_s(fp, fmt, mode)          *(fp)=fopen( (fmt), (mode))
      #define _fcloseall                       fcloseall
      #define strtok_s                         strtok
      #define _strcmpi                         strcmpi
   #endif //_MSC_VER
#endif // FCN_S_MACROS_H
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文