项目中使用一个动态库,导出了一个方法void Test(string tt); 这个方法包含一个string类型的参数。
在测试项目1中使用该动态库的Test导出方法,无论如何都会在释放参数tt的时候报错。
当然,上面的定位到的结果是调试了一天的成果。
最后实在没有办法,新建一个动态库项目与测试项目2,使用导出Test,却发现没有报错。
然后将新建的动态库使用到测试项目1中,毫不犹豫地报错了。
对比项目设置才发现,只要运行库中使用了多线程静态编译(/MT或/MTD选项),此错误必现。
回家后在《windows核心编程(第五版)》Page511中找到了答案。
若使用C/C++运行库的静态版本,一个DLL与EXE中会分别使用的两个不同的运行库,那么意味着在EXE中分配的内存,不可在DLL中释放。
书中的解决方案是对每一个DLL申请的内存,都提供DLL 中的函数进行释放。
然而在我项目中的实际情况,参数tt传入前在exe中分配,在dll的函数执行完毕后,释放使用dll中的运行库。
所以,只有将引用的动态库使用/MD或/MDd编译来解决该问题
=================================================================================
补充下参数传入的问题:
写完blog后其实我还有一事不明,参数传入后不应该由DLL函数在栈上分配吗?
带着这个疑惑,使用vs做了调试,总算清楚了。程序在WIn32 Debug下编译并运行。
以下是Main方法中调用的反汇编代码(部分)
010C1718 83 EC 20 sub esp,20h
010C171B 8B CC mov ecx,esp
010C171D 89 A5 E4 FE FF FF mov dword ptr [ebp-11Ch],esp
010C1723 8D 45 BC lea eax,[ebp-44h]
010C1726 50 push eax ; 临时变量字符串地址入栈
010C1727 E8 81 FB FF FF call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (10C12ADh) ; 创建新的字符串实例
010C172C 89 85 DC FE FF FF mov dword ptr [ebp-124h],eax ; 将字符串实例保存
010C1732 FF 15 74 C2 0C 01 call dword ptr [__imp_CFactory::Test (10CC274h)] ;调用Test方法
010C1738 83 C4 20 add esp,20h
以下是Test方法的反汇编
void CFactory::Test(std::string name)
{
0F948490 55 push ebp
0F948491 8B EC mov ebp,esp
0F948493 81 EC CC 00 00 00 sub esp,0CCh
0F948499 53 push ebx
0F94849A 56 push esi
0F94849B 57 push edi
0F94849C 8D BD 34 FF FF FF lea edi,[ebp-0CCh]
0F9484A2 B9 33 00 00 00 mov ecx,33h
0F9484A7 B8 CC CC CC CC mov eax,0CCCCCCCCh
0F9484AC F3 AB rep stos dword ptr es:[edi]
}
0F9484AE 8D 4D 08 lea ecx,[name] // name地址传入exc作为析构方法的参数
0F9484B1 E8 20 E7 FF FF call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (0F946BD6h) ; 调用std::string的析构方法,释放参数内存。在使用静态运行库编译后调用会报错!
0F9484B6 52 push edx
0F9484B7 8B CD mov ecx,ebp
0F9484B9 50 push eax
0F9484BA 8D 15 DC 84 94 0F lea edx,[ (0F9484DCh)]
0F9484C0 E8 81 E0 FF FF call @ILT+1345(@_RTC_CheckStackVars@8) (0F946546h)
0F9484C5 58 pop eax
0F9484C6 5A pop edx
0F9484C7 5F pop edi
0F9484C8 5E pop esi
0F9484C9 5B pop ebx
0F9484CA 81 C4 CC 00 00 00 add esp,0CCh
0F9484D0 3B EC cmp ebp,esp
0F9484D2 E8 8B E7 FF FF call @ILT+3165(__RTC_CheckEsp) (0F946C62h) ;<span style="font-family: Arial, Helvetica, sans-serif;">Esp</span>检查
0F9484D7 8B E5 mov esp,ebp
0F9484D9 5D pop ebp
0F9484DA C3 ret 通过对上面代码反汇编的分析,可以得出结论:
导出类CFactory的 Test方法在被调用时,由exe的main函数在栈上分配参数,然后在Test方法调用之后,由编译器生成的代码释放。
所以之前的结论是正确的,<strong>在调用方法之前,参数已经被调用方压入栈中!</strong>

本文探讨了在使用静态链接的C/C++运行库时,DLL与EXE间因独立的运行库导致的内存释放问题。通过反汇编分析,揭示了参数在栈上的分配与释放过程,并给出了通过调整编译选项解决该问题的方法。
907

被折叠的 条评论
为什么被折叠?



