2011-06-04 26 views
1

前陣子我試圖使用Visual Studio 2010來編譯使用我寫在Visual Studio 2003中不出意外庫MFC程序的Visual Studio版本使用#define ,我收到了一堆關於棄用和使用各種字符串函數的安全版本的警告。極品,其中包括安全字符串函數(避免_CRT_SECURE_NO_DEPRECATE)

然後我更新了相關的功能在圖書館使用的安全功能,它編譯罰款。

我後來又嘗試過使用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 

… 

那麼,我想圖out是確定安全功能是否存在的一種方法。我知道它們是在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,但沒有發現任何用處。

回答

0

我發現的溶液; macro/define的_MSC_VER使這個很簡單。由於secure string functionsadded in的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更容易(至少在某種程度上)。

+0

權,_MSC_VER的適當臨界值是1400,即MSVC 2005年我用MSVC 2010(_MSC_VER = 1600)時,我開始編寫宏來處理不兼容的安全功能。我懷疑我會再次使用2005年,但有人會。精確是件好事。 – riderBill 2016-02-04 03:40:49

+0

謝謝。我在函數宏包含文件中沒有任何四個函數。是否已將_sntprintf_s_s測試爲_sntprintf映射?我沒有想到橢圓在RHS上工作。 – riderBill 2016-02-04 03:53:04

+0

此外,_sntprintf_s到_sntprintf宏應該讀'的#define _sntprintf_s(A,B,C,...)_sntprintf((a)中,(c)中,__VA_ARGS __)'。否則,當格式後面沒有參數時,您將會掛起一個逗號(語法錯誤)。我想這對於sprintf函數調用來說是個不尋常的例子。 strncpy將是可能的選擇。圍繞rhs上的參數的額外括號是函數宏的良好習慣;沒有他們,你會在某天得到意想不到的結果。根據墨菲的說法,這將是最糟糕的一天。 – riderBill 2016-02-04 04:13:12

0

微軟的一些安全功能的C++ 11的一部分,所以他們現在應該可以移植。 安全功能和傳統安全功能之間的一個重要區別是異常行爲,有時是返回值。許多程序員都沒有注意到;這些差異往往被忽視。 例如snprintf的除外行爲是從_snprintf_s不同:

snprintf返回緩衝器的大小無關,打印字符串,而不是 計數終止空字符所需的字符數。 我不認爲snprintf的自籌例外,但無效的內存訪問 會。

_snprintf_s返回相同的值的snprintf如果拋光輪是足夠大的,但 如果淺黃色過少,或淺黃色或FMT是NULL指針,_snprintf_s調用 無效參數處理程序,設置分別的errno = ERANGE或EINVAL, 並返回-1。 如果在遺留代碼中異常行爲很重要,請注意將舊傳統功能轉換爲安全版本。

我用微軟的安全「_s」功能掙扎了幾年,尤其是寫作已編譯在Visual Studio中的Windows平臺和使用gcc /克+ +「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 
+0

我看到@Synetech用於'_MSC_VER <1400'爲截止(這將是'_MSC_VER> = 1400'在我的代碼)。用他的號碼(1400)而不是1600;我將編輯我的解決方案以反映這一點。另外,我在上面的代碼中沒有他的四個函數映射。如果你想添加它們。我不會等到我有機會去測試它們。只有其中兩個可以重寫爲傳統來保護函數映射。 – riderBill 2016-02-04 03:16:21