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