AssemblyLoadContext loading and unloading get different results in different functions

537 Views Asked by At

This is code from microsoft docs. Please note the comments in the code

class TestAssemblyLoadContext : AssemblyLoadContext
{
    private AssemblyDependencyResolver _resolver;

    public TestAssemblyLoadContext(string mainAssemblyToLoadPath) : base(isCollectible: true)
    {
        _resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath);
    }

    protected override Assembly Load(AssemblyName name)
    {
        string assemblyPath = _resolver.ResolveAssemblyToPath(name);
        if (assemblyPath != null)
        {
            return LoadFromAssemblyPath(assemblyPath);
        }
        return null;
    }
}

This is plugin interface code

public interface IPlugin
{
    string result { get; }
}

This is the loading and unloading method (I modified the code from microsoft doc)

private static string pluginPath = @"C:\PluginFolder\MyPlugin.dll"; //interface is IPlugin
private static TestAssemblyLoadContext talc; //the assemblyloadcontext
[MethodImpl(MethodImplOptions.NoInlining)]
static void LoadIt(string assemblyPath)
{
    talc = new PluginFactory.TestAssemblyLoadContext(pluginPath); //create instance
    Assembly a = talc.LoadFromAssemblyPath(assemblyPath); 
    //assemblyPath cant delete now because is loaded
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void UnloadIt()
{
    talc.Unload();
    for (int i = 0; i < 10; i++)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
    //if success unload, file on assemblyPath can success delete.
    File.Delete(assemblyPath);
}

This is part of a wpf program code

public MainWindow()
{
    InitializeComponent();

    LoadIt(pluginPath); //loaded assembly
    UnloadIt(pluginPath); //success unload assembly, file assembly can delete.
}

private static TestAssemblyLoadContext talc;
LoadIt(){...}
UnloadIt(){...}

If the unloading code is not together with the loading code

public MainWindow()
{
    InitializeComponent();

    LoadIt(pluginPath); //loaded assembly
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    UnloadIt(pluginPath); //unsuccessful. File assembly cant delete.
}

private static TestAssemblyLoadContext talc;
LoadIt(){...}
UnloadIt(){...}

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.

public MainWindow()
{
    InitializeComponent();
}

private void LoadButton_Click(object sender, RoutedEventArgs e)
{
    LoadIt(pluginPath); //load plugin with button click event.
}

private void UnloadButton_Click(object sender, RoutedEventArgs e)
{
    UnloadIt(pluginPath) //It successful unload, assembly file can be deleted.
}

private static TestAssemblyLoadContext talc;
LoadIt(){...}
UnloadIt(){...}

Can anyone tell me why? Could this be a bug?

You can copy the code to your new wpf program for testing. My runtime framework is .Net 5.

1

There are 1 best solutions below

0
On

Well, I know the reason, the debugger will lock the assembly file when the debugger is running, and it will be fine if it is changed to release.

https://github.com/dotnet/runtime/issues/47704