I have a C# plugin that uses a separate C++ DLL. The only reference to that DLL is from within the plugin itself. The parent application loads all plugins in their own AppDomain and unloads this AppDomain when the plugin is unloaded.
I have checked, and I definitely see the application's memory drop when I unload the plugin. I am also able to delete all of the managed assemblies that were loaded. The problem is that when I try to delete the native DLL I just keep getting Access Denied until I close the whole application.
I've been looking at this for a while, but I still can't figure out why just this DLL stays in memory.
AppDomains are a pure managed code construct. Nothing like that exists in native code, nor does Windows have any idea about it. So the scope for a loaded native DLL is the process. Technically, the pinvoke marshaller could reference count the DLL and keep track exactly which AppDomain triggered the load of the DLL. It however cannot tell whether any native code is running that uses that DLL. Native code that could be started by a call made from code in another AppDomain, possibly indirectly through a marshaled delegate.
Clearly disaster strikes if the AppDomain manager unloads a DLL that's used that way, that's a nasty and impossible to diagnose AccessViolation. Particularly nasty since it can trigger a long time after the AppDomain got unloaded.
So the marshaller doesn't implement that kind of counting, the DLL stays loaded. Only you can provide the guarantee that this can't happen, you have some measure of control over exactly what code runs in the DLL and how it gets started. You can force the DLL to unload but it requires a hack. Pinvoke LoadLibrary() yourself to get a handle to the DLL. And pinvoke FreeLibrary() twice to get it unloaded forcibly. Neither Windows nor the CLR can't see that you are cheating. You must ensure that the DLL can't be used after this.
AFAIK (under the hood) native DLLs need to be loaded via Win32 API LoadLibrary
... which loads them directly into the process memory - in case of a .NET application that is not specific to an AppDomain
... LoadLibrary
knows absolutely nothing about AppDomain
(which is purely .NET-specific)... thus unloading the AppDomain
doesn't necessarily unload native DLLs...
Interesting discussions regarding this situation:
IF you can change the implementation of the respective plugin then you would implement "late native binding" which would solve the problem you see:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With