C ++ DLL不会使用AppDomain卸载

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

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

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

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

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

所以marshaller没有实现那种计数,DLL保持加载状态。 只有您可以保证不会发生这种情况,您可以控制DLL中运行的代码以及它如何启动。 你可以强制DLL卸载但它需要一个hack。 Pinvoke LoadLibrary()自己来获​​取DLL的句柄。 并且两次使用 FreeLibrary()来强制卸载它。 Windows和CLR都看不到你在作弊。 您必须确保在此之后无法使用DLL。

AFAIK(引擎盖下)本机DLL需要通过Win32 API LoadLibrary加载…它将它们直接加载到进程内存中 – 如果.NET应用程序不是特定于AppDomainLoadLibrary绝对不知道AppDomain (纯粹是特定于.NET)…因此卸载AppDomain不一定卸载本机DLL …

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

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