sprintf_s 在release下仍然会崩溃

本文探讨了在VS2010中使用安全的字符串操作方法,如使用_snprintf_s替代sprintf_s以避免缓冲区溢出问题,并介绍了如何通过设置无效参数处理器来捕获此类异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

开启VS2010的警告之后,将项目中所有的sprintf都换成了sprintf_s,以为这样的话程序在字符串copy时就安全了,结果在实际环境中程序还是崩溃了。

自己在VS中尝试了这样的代码:


char teststr[10] = {0};

char inputstr[21] = {"12345678901234567890"};

sprintf_s(teststr,10,"%s",inputstr);


在之前以为是teststr中应该是123456789,实际上不是这样的,程序直接就崩溃掉了,使用try,catch根本无法抓到这样的异常。


查询MSDN才知道,需要使用_snprintf_s来代替sprintf_s,并且可以捕捉到这样的异常。msdn上提供的例子如下:

// crt_snprintf_s.cpp
// compile with: /MTd

// These #defines enable secure template overloads
// (see last part of Examples() below)
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <crtdbg.h>  // For _CrtSetReportMode
#include <errno.h>

// This example uses a 10-byte destination buffer.

int snprintf_s_tester( const char * fmt, int x, int count )
{
   char dest[10];

   printf( "\n" );

   if ( count == _TRUNCATE )
      printf( "%d-byte buffer; truncation semantics\n",
               _countof(dest) );
   else
      printf( "count = %d; %d-byte buffer\n",
               count, _countof(dest) );

   int ret = _snprintf_s( dest, _countof(dest), count, fmt, x );

   printf( "    new contents of dest: '%s'\n", dest );

   return ret;
}


void Examples()
{
   // formatted output string is 9 characters long: "<<<123>>>"
   snprintf_s_tester( "<<<%d>>>", 121, 8 );
   snprintf_s_tester( "<<<%d>>>", 121, 9 );
   snprintf_s_tester( "<<<%d>>>", 121, 10 );

   printf( "\nDestination buffer too small:\n" );

   snprintf_s_tester( "<<<%d>>>", 1221, 10 );

   printf( "\nTruncation examples:\n" );

   int ret = snprintf_s_tester( "<<<%d>>>", 1221, _TRUNCATE );
   printf( "    truncation %s occur\n", ret == -1 ? "did"
                                                  : "did not" );

   ret = snprintf_s_tester( "<<<%d>>>", 121, _TRUNCATE );
   printf( "    truncation %s occur\n", ret == -1 ? "did"
                                                  : "did not" );
   printf( "\nSecure template overload example:\n" );

   char dest[10];
   _snprintf( dest, 10, "<<<%d>>>", 12321 );
   // With secure template overloads enabled (see #defines
   // at top of file), the preceding line is replaced by
   //    _snprintf_s( dest, _countof(dest), 10, "<<<%d>>>", 12345 );
   // Instead of causing a buffer overrun, _snprintf_s invokes
   // the invalid parameter handler.
   // If secure template overloads were disabled, _snprintf would
   // write 10 characters and overrun the dest buffer.
   printf( "    new contents of dest: '%s'\n", dest );
}

void myInvalidParameterHandler(
   const wchar_t* expression,
   const wchar_t* function,
   const wchar_t* file,
   unsigned int line,
   uintptr_t pReserved)
{
   wprintf(L"Invalid parameter handler invoked: %s\n", expression);
}

int main( void )
{
   _invalid_parameter_handler oldHandler, newHandler;

   newHandler = myInvalidParameterHandler;
   oldHandler = _set_invalid_parameter_handler(newHandler);
   // Disable the message box for assertions.
   _CrtSetReportMode(_CRT_ASSERT, 0);

   Examples();
}



在后来的日子中又尝试了自己项目中其他的字符串copy,结果让我大吃一惊,可能是以前就理解不深入,才导致程序崩溃.

   char teststrcat[10] = {0};
    char inputstrcat[21] = {"12345678901234567890"};
    strcat_s(teststrcat,inputstrcat);

使用strcat_s程序直接崩溃掉,使用上面的_set_invalid_parameter_handler可以捕捉到该异常。


然而使用memcpy时结果却不是那么乐观,

    char teststr[10] = {0};
    char inputstr[21] = {"12345678901234567890"};
    memcpy(teststr,inputstr,strlen(inputstr));

程序直接通过了,而且teststr中包含有inputstr中的数据和乱码。

这样开发人员就再不知情的情况下将该代码执行了。

安全的使用memcpy的方式为

void test(char *pbData, unsigned int cbData) {
    char buf[BUFFER_SIZE];
    memcpy(buf, pbData, min(cbData,BUFFER_SIZE-1));
}

我再使用多进程时,在下列代码中unsigned WINAPI MyThreadFunction(LPVOID lpParam) { while (openFlag) { UF_UI_lock_ug_access(UF_UI_FROM_CUSTOM); //给主线程加锁(不锁上的话,在点击应用后再点击调取命令会崩掉或死循环的可能) char message[256]; //错误信息 getVariableDir("VT_APPLICATION_DIR", czApplicationDir); if (!isDirHas(czApplicationDir)) { UF_UI_open_listing_window(); UF_UI_write_listing_window("读取 VT_APPLICATION_DIR 环境变量失败 \n"); return false; } char czCfgDataP[256]; //表格完整路径 strcpy_s(czCfgDataP, czApplicationDir); strcat_s(czCfgDataP, "\\data\\标准化配置\\设计标准配置表.xls"); if (!isDirHas_Chs(czCfgDataP)) { sprintf_s(message,"(%s)不存在 \n", czCfgDataP); UF_UI_open_listing_window(); UF_UI_write_listing_window(message); return false; } Book* book; if (!load(czCfgDataP, &book)) { sprintf_s(message, "加载(%s) 失败 \n", czCfgDataP); UF_UI_open_listing_window(); UF_UI_write_listing_window(message); book->release(); return false; } Sheet* voiceSheet;//声音数据页 if (!getSheet(book, "材料", &voiceSheet)) { UF_UI_open_listing_window(); UF_UI_write_listing_window("加载 (材料) 数据 失败 \n"); book->release(); return false; } vAllVoiceSt.clear(); vVoiceStr.clear(); if (!readStr_Col(voiceSheet, 0, vVoiceStr)) { UF_UI_open_listing_window(); UF_UI_write_listing_window("加载 (材料) 材质类型 失败 \n"); book->release(); return false; } for (size_t i = 0; i < vVoiceStr.size(); i++) { vector<CString>vTempStr; if (!readStr_Col(voiceSheet, i, vTempStr)) { continue; } vAllVoiceSt.push_back(vTempStr); break; } book->release(); HANDLE g_hMutex = CreateMutex(NULL, FALSE, NULL); if (vAllVoiceSt.size() && tempStrUi && openFlag) { string excelDate = (LPCSTR)(CStringA)(vAllVoiceSt[0][0]); if (!excelDate.empty()) { vector<NXString>tempStr; tempStr.push_back((NXString)excelDate.c_str()); tempStrUi->SetEnable(true); tempStrUi->SetValue(tempStr); tempStrUi->SetEnable(false); } else { vector<NXString>tempStr; tempStr.push_back(""); tempStrUi->SetEnable(true); tempStrUi->SetValue(tempStr); tempStrUi->SetEnable(false); } } ReleaseMutex(g_hMutex); UF_UI_unlock_ug_access(UF_UI_FROM_CUSTOM); //给主线程关锁 Sleep(500); // Simulate work with a sleep call. } return 0; } tempStrUi->SetValue(tempStr);这句代码总是会导致NX2206崩溃该如何解决?
07-24
//duobanbeijianrong // Mandatory UF Includes #include <uf.h> #include <uf_object_types.h> // Internal Includes #include <NXOpen/ListingWindow.hxx> #include <NXOpen/NXMessageBox.hxx> #include <NXOpen/UI.hxx> // Internal+External Includes #include <NXOpen/Annotations.hxx> #include <NXOpen/Assemblies_Component.hxx> #include <NXOpen/Assemblies_ComponentAssembly.hxx> #include <NXOpen/Body.hxx> #include <NXOpen/BodyCollection.hxx> #include <NXOpen/Face.hxx> #include <NXOpen/Line.hxx> #include <NXOpen/NXException.hxx> #include <NXOpen/NXObject.hxx> #include <NXOpen/Part.hxx> #include <NXOpen/PartCollection.hxx> #include <NXOpen/Session.hxx> #include "uf_all.h" #include "HuNXOpen.h" #include <algorithm> // 包含std::sort算法 #include <map> #include <set> // 包含std::set容器 #include <vector> // 包含std::vector容器 #include <cmath> // 包含数学函数 #include <limits> // 包含数值极限 #include <memory> // 包含智能指针 #include <queue> // 添加队列支持 // Std C++ Includes #include <iostream> #include <sstream> #include <Windows.h> // 包含所有Windows API定义 #include <cstdio> // 用于sprintf_s using namespace NXOpen; using std::string; using std::exception; using std::stringstream; using std::endl; using std::cout; using std::cerr; //------------------------------------------------------------------------------ // NXOpen c++ test class //------------------------------------------------------------------------------ class MyClass { // class members public: static Session *theSession; static UI *theUI; MyClass(); ~MyClass(); void do_it(); void print(const NXString &); void print(const string &); void print(const char*); private: BasePart *workPart, *displayPart; NXMessageBox *mb; ListingWindow *lw; LogFile *lf; }; //------------------------------------------------------------------------------ // Initialize static variables //------------------------------------------------------------------------------ Session *(MyClass::theSession) = NULL; UI *(MyClass::theUI) = NULL; //------------------------------------------------------------------------------ // Constructor //------------------------------------------------------------------------------ MyClass::MyClass() { // Initialize the NX Open C++ API environment MyClass::theSession = NXOpen::Session::GetSession(); MyClass::theUI = UI::GetUI(); mb = theUI->NXMessageBox(); lw = theSession->ListingWindow(); lf = theSession->LogFile(); workPart = theSession->Parts()->BaseWork(); displayPart = theSession->Parts()->BaseDisplay(); } //------------------------------------------------------------------------------ // Destructor //------------------------------------------------------------------------------ MyClass::~MyClass() { } //------------------------------------------------------------------------------ // Print string to listing window or stdout //------------------------------------------------------------------------------ void MyClass::print(const NXString &msg) { if(! lw->IsOpen() ) lw->Open(); lw->WriteLine(msg); } void MyClass::print(const string &msg) { if(! lw->IsOpen() ) lw->Open(); lw->WriteLine(msg); } void MyClass::print(const char * msg) { if(! lw->IsOpen() ) lw->Open(); lw->WriteLine(msg); } //------------------------------------------------------------------------------ // Do something //------------------------------------------------------------------------------ void MyClass::do_it() { char* release = NULL; // 声明指针,用于接收版本字符串 // 调用UF_get_release获取版本字符串 int result = UF_get_release(&release); // 检查函数调用是否成功 if (result != 0 || release == NULL) { mb->Show("错误", NXMessageBox::DialogTypeError, "获取NX版本失败!"); return; } // 将版本号转换为字符串便于处理 std::string version(release); // 释放由UF_get_release分配的内存 - 必须调用! UF_free(release); // 输出获取的版本信息到列表窗口 print("当前NX版本: " + version); // 定义DLL路径 const char* dllPath = nullptr; // 检查版本是否匹配NX 12.0 if (version.find("NX V12.0") == 0) { print("检测到NX 12.0版本,加载对应工具"); dllPath = "D:\\MICHTOOLS\\Application\\qiaowei_tool_12.0.dll"; } // 检查版本是否匹配NX 2212系列 else if (version.find("NX V2212") == 0 || version.find("V22.12") == 0 || version.find("NX 2212") != std::string::npos) { print("检测到NX 2212系列版本,加载对应工具"); dllPath = "D:\\MICHTOOLS\\Application\\qiaowei_tool_2212.dll"; } else { // 版本不匹配时显示错误对话框 mb->Show("版本错误", NXMessageBox::DialogTypeError, "未找到匹配的工具版本!\n当前版本: " + version); return; // 终止函数执行 } // 加载对应的DLL HMODULE hModule = LoadLibraryA(dllPath); if (hModule == NULL) { DWORD errorCode = GetLastError(); char errorMsg[256]; sprintf_s(errorMsg, "加载DLL失败! 错误代码: %d", errorCode); mb->Show("DLL错误", NXMessageBox::DialogTypeError, errorMsg); return; } // 获取DLL中的入口函数 typedef void (*ToolEntryFunction)(); ToolEntryFunction runTool = (ToolEntryFunction)GetProcAddress(hModule, "run_tool"); if (runTool == NULL) { DWORD errorCode = GetLastError(); char errorMsg[256]; sprintf_s(errorMsg, "获取函数地址失败! 错误代码: %d", errorCode); mb->Show("函数错误", NXMessageBox::DialogTypeError, errorMsg); FreeLibrary(hModule); // 释放DLL return; } print("版本检查通过,开始执行主程序..."); try { // 调用DLL中的函数 runTool(); } catch (...) { mb->Show("执行错误", NXMessageBox::DialogTypeError, "工具执行过程中发生异常!"); } // 释放DLL FreeLibrary(hModule); } // TODO: add your code here //------------------------------------------------------------------------------ // Entry point(s) for unmanaged internal NXOpen C/C++ programs //------------------------------------------------------------------------------ // Explicit Execution extern "C" DllExport void ufusr( char *parm, int *returnCode, int rlen ) { UF_initialize(); //开发的许可函数,初始化 try { // Create NXOpen C++ class instance MyClass *theMyClass; theMyClass = new MyClass(); theMyClass->do_it(); delete theMyClass; } catch (const NXException& e1) { UI::GetUI()->NXMessageBox()->Show("NXException", NXOpen::NXMessageBox::DialogTypeError, e1.Message()); } catch (const exception& e2) { UI::GetUI()->NXMessageBox()->Show("Exception", NXOpen::NXMessageBox::DialogTypeError, e2.what()); } catch (...) { UI::GetUI()->NXMessageBox()->Show("Exception", NXOpen::NXMessageBox::DialogTypeError, "Unknown Exception."); }UF_terminate(); //释放许可函数,结束化 } //------------------------------------------------------------------------------ // Unload Handler //------------------------------------------------------------------------------ extern "C" DllExport int ufusr_ask_unload() { return (int)NXOpen::Session::LibraryUnloadOptionImmediately; } 具体放在我代码的什么位置。最好给我一份完整的代码,每行中文注解
最新发布
08-22
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值