VC使用动态库关于/MD与/MT的一个坑

14 篇文章 0 订阅

项目中使用一个动态库,导出了一个方法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>

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值