1 现象:问题描述
在I产品系统测试时,发现客户端经常弹出 内存错误系统对话框 并异常退出
2 关键过程:根本原因分析
在VC中逐行调试程序,发现运行到红色代码处会出现问题,就是显式引用动态库函数后
typedef void (*LIBFUNC)(string &strPara);
……………….
……………….
HMODULE h = LoadLibrary("xxx.dll");
LIBFUNC theFunc = GetProcAddress(h,"funcName");
{ //用括号括起,令string立刻析构,如果没有正确配置,出了括号就出错
string strPara;
(*theFunc)(strPara);
}
cout << "WRONG" << endl;
而红色部分代码,并无任何对内存的操作,后来将蓝色代码注释掉 ,错误不再出现,于是定位在蓝色的两行。
对于stl的string类,赋值和声明时都会分配内存,只要字串长度不为0,而蓝色部分出了括号后,sting对象 strPara会析构,释放掉分配的内存,错误就出现在析构的时候,而与红色代码并无关系。
《WINDOWS核心编程》第19章1节DLL与进程的地址空间. 书中指出
一个进程的地址空间是由一个可执行模块和若干个DLL模块组成的。这些DLL模块中,有些可以链接到静态版本的C/C++运行期库,有些可以链接到一个DLL版本的C/C++运行期库,而有些模块(如果不是用C/C++编写的话)则根本不需要C/C++运行期库。 DLL函数分配的内存块是由EXE的函数释放是可能的。如果EXE和DLL都链接到动态的C/C++运行期库,那么上面的代码将能够很好地运行。但是,如果两个模块中的一个或者两个都链接到静态C/C++运行期库,这段代码就会出错.
3 结论:解决方案及效果
最初认为,由于动态库中分配了内存,就应该由动态库释放,但是由于sring对象在析构时释放内存的特性,我们无法准确的控制这部分内存。所以将输入参数做了修改,由EXE分配和释放。修改代码
typedef void (*LIBFUNC)(char *&strPara);
……………….
HMODULE h = LoadLibrary("xxx.dll");
LIBFUNC theFunc = GetProcAddress(h,"funcName");
{ //用括号括起,令string立刻析构,如果没有正确配置,出了括号就出错
char *strPara = new char[1024];
(*theFunc)(strPara);
delete []strPara;
}
此后不再出现此问题,但是代码有些不方便,而且每次分配大块内存比较浪费,还有可能
分配的不够。
后来 根据《WINDOWS核心编程》的提示,再次使用原来的代码,并将动态库和EXE部分都链接动态运行库。原有代码不再出现问题。
4 经验总结:预防措施和规范建议
如果在动态库和EXE中有一方采用了静态C运行库,那么dll分配的内存就不能由EXE部分释放,解决方法有: 预先在EXE部分分配好内存,或者由DLL提供释放的函数,但是最简单可行的是 在动态库和EXE中都链接动态C运行库,这样就不必关心谁分配谁释放了。
5 备注
6 考核点
显式引用 WIN32 DLL函数,C运行库 ,内存分配
阅读(469) | 评论(0) | 转发(0) |