14

我有一个使用单独的 C++ DLL 的 C# 插件。对该 DLL 的唯一引用来自插件本身。父应用程序在自己的 AppDomain 中加载所有插件,并在卸载插件时卸载此 AppDomain。

我已经检查过了,当我卸载插件时,我肯定会看到应用程序的内存下降。我还能够删除所有已加载的托管程序集。问题是,当我尝试删除本机 DLL 时,我只会一直拒绝访问,直到我关闭整个应用程序。

我已经研究了一段时间,但我仍然无法弄清楚为什么只有这个 DLL 留在内存中。

4

2 回答 2

22

AppDomain 是纯托管代码构造。本机代码中不存在类似的东西,Windows 也不知道它。因此,加载的本机 DLL 的范围是进程。从技术上讲,pinvoke marshaller 可以引用 DLL 计数并准确跟踪哪个 AppDomain 触发了 DLL 的加载。但是,它无法判断是否有任何使用该 DLL 的本机代码正在运行。本机代码可以通过从另一个AppDomain 中的代码进行调用来启动,可能间接通过编组委托。

如果 AppDomain 管理器卸载以这种方式使用的 DLL,那么显然会发生灾难,这是一个令人讨厌且无法诊断 AccessViolation 的问题。特别讨厌,因为它可以在 AppDomain 卸载后很长时间触发。

所以编组器没有实现那种计数,DLL 保持加载状态。只有您可以保证这不会发生,您可以对在 DLL 中运行的代码及其启动方式进行一定程度的控制。您可以强制 DLL 卸载,但它需要 hack。自己调用 LoadLibrary() 以获取 DLL 的句柄。并 pinvoke FreeLibrary()两次以强制卸载它。Windows 和 CLR 都看不到您在作弊。您必须确保在此之后不能使用 DLL。

于 2012-04-04T16:27:54.957 回答
6

AFAIK (幕后)本机 DLL 需要通过 Win32 API 加载...直接将它们加载到进程内存中 - 如果 .NET 应用程序LoadLibrary不是特定于AppDomain...纯粹特定于.NET)......因此卸载不一定卸载本机DLL......LoadLibraryAppDomainAppDomain

关于这种情况的有趣讨论:

如果您可以更改相应插件的实现,那么您将实现“后期本机绑定”,这将解决您看到的问题:

于 2012-04-04T16:03:30.223 回答