VC使用動態庫關於/MD與/MT的一個坑

項目中使用一個動態庫,導出了一個方法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>

發佈了46 篇原創文章 · 獲贊 44 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章