2009-01-20 219 views
5

如何確保dll在其中存在任何對象時不被卸載?C++:Dll卸載問題

問題是,當我使用顯式內存管理時,我可以在釋放dll之前刪除dll對象,但是使用智能指針我無法控制那裏銷燬的順序,這意味着dll可能首先被釋放導致崩潰設法釋放其他對象之一時:

FlPtr是一個簡單的refrence計數類這就是調用的AddRef,並根據需要

ExampleDll *dll = LoadDll(L"bin\\example.dll"); 
IObject *obj = dll->CreateObject(); 
... 
obj->Release(); 
delete dll;//fine because all objects already deleted 
return 0; 

auto_ptr<ExampleDll> dll = LoadDll(L"bin\\example.dll"); 
FlPtr<IObject> obj = dll->CreateObject(); 
... 
return 0;//crash if dll is destructed before obj since Object::Release needs to call into the dll 

我試圖使該dll手柄卸載itsself釋放,即所有對象後只卸載已被刪除。這項工作是通過創建一個DLL實現的新對象IExampleDll。這與之前的ExampleDll對象相似,但是它們位於dll而不是exe中,並且也被計算在內。 dll中的每個對象都增加了這個構造參考,並在銷燬時將其刪除。這意味着當exe已經釋放它的引用並且所有的dll對象都被銷燬時引用計數只會達到零。然後刪除自己在其析構函數中調用FreeLibrary(GetModuleHandle())。

然而,這在FreeLibrary則,IM asuming崩潰,因爲線程仍然在被卸載這些DLL的代碼...

我不知所措我現在如何確保該dll只卸載時除了在刪除其他所有內容之後明確釋放dll外,沒有其他對象。當DLL文件需要被saftly加載/卸載程序中旬,也就是說,如果從D3D改變了用戶在選擇的OpenGL

int main() 
{ 
    ExampleDll *dll = LoadDll("bin\\example.dll"); 
    restOfProgram(); 
    delete dll; 
} 

這種方法變得困難。

回答

6

假設您不想在卸載庫時終止線程(否則請參閱MSalters),您需要從加載它的調用程序中釋放庫。

COM通過調用全局導出的CanUnloadNow函數,通過一個DLL內實例計數器(與您的實例非常相似)解決該問題,並通過定期檢查它。

另一種選擇是讓你的對象/接口智能指針也參考它們來自的DLL。這會增加客戶端數據大小,但不需要觸摸DLL。您甚至可能會回收LoadLibrary/FreeLibrary引用計數器,但這可能會影響性能。

此外,如果您獲得循環DLL依賴關係(組件DllA.X引用DllB.Y,它引用DllA.Z),這些方案都不會有太大的幫助。對於那些不需要全球知識的人來說,我還沒有找到一個好的解決方案。

0

MSDN是明確的關於這一主題:「必須卸載在其上執行的DLL,然後終止本身應該叫FreeLibraryAndExitThread,而不是調用FreeLibraryExitThread分開,否則可能會發生競爭狀態的線程詳情看到的FreeLibraryAndExitThread的備註部分

+0

我不想終止線程,只是釋放DLL並返回到EXE /以前的DLL – 2009-01-20 15:06:46

+0

這沒有任何意義 - 你只是卸載所有的返回語句! – MSalters 2009-01-23 08:29:01

1

對於情況下DLL在運行時切換,我會避免智能指針系統由DLL創建對象和使用這樣的系統:

    |-----------------------| |--------------------------| 
        | Abstraction Interface | | Implementation Interface | 
        |-----------------------| |--------------------------| 
          ^      ^
           |       | 
|-------------|1  *|-------------------|*  *|----------------| 
| Application |-------| Abstraction Layer |--------| Implementation | 
|-------------|  |-------------------|  |----------------| 

\------------- Main Program ------------------/ \-------- DLL --------/ 

該應用程序包含所有alloca的列表特德抽象層對象。抽象層對象是唯一允許擁有指向由實現層創建的對象的指針的對象。交換DLL時,首先迭代所有抽象層對象,並告訴它們釋放特定於實現的數據。然後卸載DLL並加載新的DLL。然後再次迭代抽象層對象,並告訴它們創建新的實現特定數據。

0

好吧我認爲最好的選擇是使用COM輪詢DLL的方法,看看它是否可以卸載。我怎麼能這樣做,雖然這樣我就可以繼續輪詢DLL的一切關閉後(即主線程已終止)?我是否需要創建一個完全獨立的過程來完成這個過程?在這種情況下,我該如何操作,以便這個獨立的過程知道所有加載的dll,並且對proformance的影響很小?

Mayby我可以創建輪詢系統,當所有的DllPtr超出範圍,並儘快終止它,只要它釋放DLL?這種方式只存在於任何剩餘的智能指針被銷燬時。