2012-04-04 126 views
10

我有一個C#插件使用單獨的C++ DLL。該DLL的唯一引用來自插件本身。父應用程序將所有插件加載到其自己的AppDomain中,並在插件卸載時卸載此AppDomain。C++ DLL不能卸載AppDomain

我已經檢查過了,當我卸載插件時,我肯定會看到應用程序的內存丟失。我也能夠刪除所有加載的託管程序集。問題是,當我嘗試刪除本地DLL時,我只是不斷收到訪問拒絕,直到我關閉了整個應用程序。

我一直在看這一段時間,但我仍然無法弄清楚爲什麼只有這個DLL留在內存中。

回答

18

AppDomains是一個純粹的託管代碼構造。本地代碼中沒有這樣的東西,Windows也沒有任何關於它的想法。所以加載的本地DLL的作用域就是這個過程。從技術上講,pinvoke編組可以引用對DLL進行計數並跟蹤確切地哪個AppDomain觸發了DLL的加載。但它不能分辨是否有任何使用該DLL的本機代碼正在運行。原生代碼可以通過另一個 AppDomain中的代碼進行調用,可能通過編組委託間接進行。

如果AppDomain管理器卸載以這種方式使用的DLL,那麼顯然會發生災難性的打擊,這是一種討厭且無法診斷AccessViolation的DLL。特別討厭,因爲AppDomain卸載後可能會觸發很長時間。

所以編組器並沒有實現這種計數,DLL保持加載。只有你可以提供這種保證不會發生的情況,你可以控制在DLL中運行什麼代碼以及它是如何啓動的。你可以強制DLL卸載,但它需要一個黑客。自己調用LoadLibrary()來獲取DLL的句柄。並免費FreeLibrary()兩次以強制卸載它。 Windows和CLR都不能看到你在作弊。您必須必須確保在此之後不能使用該DLL。

+3

+1 nice hack :-) – Yahia 2012-04-04 16:29:40

+0

嘿,只是想說非常明確的答案,謝謝! – user472875 2012-04-04 17:50:41

5

AFAIK(引擎蓋下)本機DLL需要通過Win32 API的LoadLibrary ...直接加載它們到處理存儲器被加載 - 在.NET應用程序的情況下,是不是特定於AppDomain ... LoadLibrary知道絕對沒有關於AppDomain(這是純粹的.NET專用的)......從而卸載AppDomain不一定卸載機DLL ...

關於這種情況下有趣的討論:

,如果你可以改變各個插件的實現,那麼你將實現「遲到本地結合」,這將解決你看到的問題: