-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AssemblyLoadContext cant initialize in WPF application method. #47704
Comments
Tagging subscribers to this area: @vitek-karas, @agocke, @CoffeeFlux Issue DetailsFramework: .Net 5
This is the loading and unloading method (I modified the code from microsoft doc)
This is part of a wpf program code
If the unloading code is not together with the loading code
But if the loading code is not called in the Wpf initialization function "MainWindow()" or wpf application event e.g. Loaded event, ContentRendered event, Activated event, it will work.
Can anyone tell me why? Could this be a bug? Note: I have not used the LoadIt function elsewhere. Use it only once.
|
In .NET Core unloadability is "cooperative", it means the program needs to release all references to objects and types from the assemblies to be unloaded. So it's not just about the code driving the load/unload, but also about the plugin itself. For example if the plugin registers a callback on the application object, there will be a reference from the application object to the plugin - which will keep the plugin around and it won't unload. So the while the code above looks OK, the plugin can do all kinds of things which will prevent unload from being successful. There are ways to debug this - see the guide here: https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability#debug-unloading-issues There could also be issues with the framework itself (both WPF as well as the core framework) which might prevent the plugin from unloading. A typical example are components which hold on to global caches - the cache would then hold the reference to the plugin. If you find that to be the case here, please create an issue so that we can track/fix it. |
I'm pretty sure there is no problem with my plugin, I tested it before. Plugin interface like this
The problem now is that if I load window-related functions, I cannot unload AssemblyLoadContext in other functions. I tested loading AssemblyLoadContext in window-related functions, and then unloading, there is no problem. This is what bothers me. Therefore, I think that if the AssemblyLoadContext is loaded into a window-related function, it will cause some unexpected results. |
The plugin interface itself is not very telling. It depends what it actually does when it executes. But you're right in general - WPF has issues when used from non-default load contexts. Some of it can be worked around by using contextual reflection overrides: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext.entercontextualreflection?view=net-5.0. Also see the design doc for this feature here: https://github.com/dotnet/runtime/blob/master/docs/design/features/AssemblyLoadContext.ContextualReflection.md. Unloading is basically yet another set of requirement on top of correct secondary load context support. It's very possible that WPF has some issues in this area as well. |
I found this thread after searching on possible solutions when not all expected assemblies are unloaded. |
I gradually found possible solutions with the help of vitek-karas, but failed.
to
Use the code to test in MainWindow()
So far, my software is running well, and there are no problems loading and unloading. |
Are you trying to just unload your plugin or your plugin along with the wpf assemblies that your plugin loaded? Because in the first case your plugin should unload just fine, but the wpf assemblies will stay loaded for the full lifetime of your application. |
I wish this were true. Unfortunately the frameworks are still full of issues which cause plugins to fail to unload (hold onto references of things in the plugin). There's a story tracking the work to make frameworks work well with secondary load contexts: #43544. |
It seems to be that the situation became worse over the time. Now Wpf tries to keep more assemblies as possible, included the plugin. But not only Wpf, in my case the following assemblies loaded from a third party assembly failed to unload: System.Runtime.InteropServices.RuntimeInformation.dll My plugin and most of the other assemblies unload fine. Do you know if the assemblies in this list are problematic? I suspect .netstandard can play a role. I also suspect that the third party app does not fully cooperate and may keep some reference (even if all of their main assemblies are successfully unloaded). Is there a way to force unload when cooperation is not 100% full? |
All these assemblies are framework assemblies - there's no way to unload them. I don't know how you load them, but the only really supported configuration is to load them to the default load context - which means they can't unload. As discussed in some other issues, there's no support for unloading frameworks (either as a whole or per-assembly). In fact there's no support to "load" frameworks dynamically either (if you start with non-WindowsDesktop app, there's no way to add WindowsDesktop framework to it). If you measure "unload" by listing all assemblies loaded into the process and before/after comparison - that is not very telling. Framework assemblies are lazy loaded (just like anything else), so the fact that the plugin caused the assembly to load doesn't mean anything but that it was the first to need it - it might be that the host would eventually load it anyway.
No. And this is by design. Force unloading assemblies is VERY tricky business. .NET Framework did implement it - it was very expensive to do, and even after multiple releases and endless bug fixing it still wasn't 100% - it could lead to data corruption or random crashes in the extreme cases. (Just as an example, what do you do if the assembly in question is running code on some thread and that thread is stuck in native code - how do you "force" that to end without potential data corruption). Cooperative unload is also problematic (as seen by this issue for example), but at least it doesn't have really bad consequences (data corruption/crashes). The current unload implementation in the runtime basically guarantees no corruption of anything - but the compromise is that it doesn't guarantee unload. |
I just uninstalled the plugin, not the wpf assembly, as @vitek-karas said, they are all loaded into AssemblyLoadContext.Default. I think my program should have successfully uninstalled them. I tested it like this (The code used is the modified code shown above):
The program did not reopen in the above test. Maybe I am not strict with this test because I don't know how to test it. But I think this should successfully uninstall the assembly. |
@JinkerLeong Well, in your specific case you need to constantly run multiple plugins at the same time, therefore it makes perfectly sense to leave WPF loaded. Regarding the locked files, I recently found a thread (not sure where, but I believe in this repo) where this problem could be related to visual studio debugger locking assemblies. I am not sure if they have already fixed this, but you may want to retry without debugger attached. @vitek-karas Sometimes I ask myself if there are some assemblies that are not framework assemblies except for the ones represented from an empty dll project. Eh eh
I am trying to do a complete switch to .Net 5 and eliminate any dependency from the old .Net Framework, but I am afraid that .net standard is the bridge that is causing the whole runtime loaded.
Yes, this is something that I should never forget. It's highly likely that a good number of unloaded assemblies are needed from the client app anyway. My mistake is the wrong assumption that when an assembly is fully unloaded, then "everything is good, clean and without any memory left here and there".
I totally agree. It's just like real humans. Forcing for a full cooperation can result in a total lack of cooperation. |
Ok, when I tried to run the release, the error locking the assembly file disappeared. This surprised me. I didn't expect the problem to be here. |
Maybe a better way to think about it in terms of load contexts (that's how it's implemented). Your statement above should be true when speaking about things loaded into the unloaded context. If there are any memory (or other) leaks from it, then it's a bug which should be fixed. But the code in the custom load context is not isolated from the rest of the app so it can cause stuff to happen outside of the load context, for example loading more code into some other context (typically Default), increased memory allocation by other components (frameworks sometimes have caches which don't have an eviction policy for example) and so on. Some of these problems are sort of new to .NET Core (loading to Default for example, since .NET Framework had that solved differently), some of them are basically the same as before, but maybe a bit more visible (.NET Framework could "cut" direct references to unloaded code/types, so the caches would still be larger, but the stuff they point to would not longer be there). |
Framework: .Net 5
This is code from microsoft docs. Please note the comments in the code
This is the loading and unloading method (I modified the code from microsoft doc)
This is part of a wpf program code
If the unloading code is not together with the loading code
But if the loading code is not called in the Wpf initialization function "MainWindow()" or wpf application event e.g. Loaded event, ContentRendered event, Activated event, it will work.
Can anyone tell me why? Could this be a bug?
Note: I have not used the LoadIt function elsewhere. Use it only once.
In order to confirm whether it is a problem with my project, I put the TestAssemblyLoadContext class, LoadIt function and UnloadIt function into the new WpfApp to test and get exactly the same result.
The text was updated successfully, but these errors were encountered: