2013-03-08 128 views
4

我今天有這個錯誤,原因是因爲我在調用FreeLibrary()之後使用從我的DLL內部分配的字符串爲什麼在FreeLibrary()之後從DLL內部分配的內存變得無效?

這是一個複製崩潰的簡單示例。這正好DLL:

void dllFunc(char **output) 
{ 
    *output = strdup("Hello"); // strdup uses malloc 
} 

這是在加載DLL的EXE:

void exeFunc() 
{ 
    char *output; 
    dllFunc(&output); 
    std::string s1 = output; // This succeeds. 
    FreeLibrary(dll); 
    std::string s2 = output; // This crashes with access violation. 
} 

我讀的FreeLibrary()的文檔,但我找不到任何關於內存什麼成爲無效的,它被稱爲後。

編輯

我才意識到,我一直在使用VS2008工具鏈的DLL,同時使用VS2010工具鏈爲EXE(我使用VS2010的IDE兩種,但你可以選擇從項目工具鏈設置)。爲DLL設置工具鏈到VS2010以及刪除崩潰。

+1

每個DLL被分配其FreeLibrary則後釋放堆() – mohaps 2013-03-08 18:37:29

回答

6

如果您選擇與MSVCRT(C運行時間)庫的靜態鏈接,則會得到您描述的行爲。如果您的EXE和DLL動態鏈接到MSVCRT DLL,但使用不同的版本,也會發生同樣的情況。或者,如果它們匹配到相同的版本,但其中一個使用DEBUG,另一個使用RETAIL。換句話說,內存僅與用於分配的MSVCRTxxx.dll的生存期一樣好。我剛剛看到你的問題的更新 - 是的,混合和匹配VS 2008和2010之間的CRT是崩潰的確切原因。

如果您的DLL和EXE都動態鏈接到相同的版本的MSVCRT DLL,那麼您共享內存堆,並避免了您遇到的問題。標準的做法是這樣的:如果你的導出的DLL函數返回任何需要稍後「釋放」或「釋放」的東西,那麼標準做法是提供一個從DLL中導出的附加函數來處理解除分配。

您可以在代碼生成頁面爲項目中的C/C++項目設置配置EXE和DLL的C運行時鏈接。

圖片瀏覽:http://imgur.com/uld4KYF.png

+0

只需添加,這將是一個私有堆,'GetProcessHeap()'返回相同的句柄,無論它是從DLL內還是從EXE調用。 – sashoalm 2013-03-09 08:37:40

+0

@sashoalm不一定。你沒有使用Win32堆函數來分配內存。你正在使用'malloc',它駐留在CRT中,它不僅僅是要求操作系統堆滿足大量內存。 – jalf 2013-03-09 09:29:17

5

這是因爲每個Dll創建自己的內存堆(其中malloc及其C朋友,以及new將在內部使用,通常通過HeapAlloc),並且當Dll被釋放時,其堆也是如此。

參考this MSDN article瞭解更多的Dll內存告誡。除非你使用自定義的內存分配器,在所有的二進制文件中共享,你需要在創建它的模塊中保留動態分配的內存(除非你能100%保證對象不會超過它的創建者)。

+2

通常DLL不創建它自己的堆(除非明確地這樣做,通過調用HeapCreate或東西),它寧可使用加載它的進程的堆。但是,當可執行文件和dll使用相同數據結構的不同實現時,可能會出現此問題:e。g在dll中創建子類的對象,然後在可執行文件中釋放它可能會崩潰,因爲它會調用已經與dll一起卸載的虛擬析構函數。 – Archie 2013-03-08 19:24:42

+0

@Archie:對於每個DLL使用進程堆的語句,你有什麼參考? – 2013-03-08 19:33:28

+2

@Nik Runtime負責進程內存分配(msvcrt ... dll)。這兩個,DLL和可執行文件將共享CRT提供的堆。你可以從DLL和從加載該DLL的可執行文件調用GetProcessHeap,並且您將獲得完全相同的句柄。 – Archie 2013-03-08 19:41:12