Skip to content
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

Unable to delete File (dll) after unloading context. #11408

Closed
FrankDoersam opened this issue Nov 5, 2018 · 16 comments
Closed

Unable to delete File (dll) after unloading context. #11408

FrankDoersam opened this issue Nov 5, 2018 · 16 comments
Labels
area-VM-coreclr documentation Documentation bug or enhancement, does not impact product or test code

Comments

@FrankDoersam
Copy link

Hello,

i could not delete a dll after unloading it.

Could you please fix this bug.

Code:

class SimpleUnloadableAssemblyLoadContext : AssemblyLoadContext
{
public SimpleUnloadableAssemblyLoadContext()
: base(isCollectible: true)
{
}

    protected override Assembly Load(AssemblyName assemblyName) => null;
}

class Program
{
    private static void ExecuteAssembly(Assembly assembly)
    {
        MethodInfo entry = assembly.EntryPoint;
        foreach (Type type in assembly.GetTypes())
        {
            Console.WriteLine(type.FullName);
        }
        Console.WriteLine("ok");

    }

    static void Main(string[] args)
    {
        AssemblyLoadContext tt = new SimpleUnloadableAssemblyLoadContext();
        tt.LoadFromAssemblyPath(@"C:\Lokale Daten\AppDomainUnload\ConsoleApp1\bin\Debug\netcoreapp3.0\Plugin\Plugin1.dll");

        tt.Unload();
        Console.ReadKey();
    }

    private static void Context_Unloading(AssemblyLoadContext obj)
    {
        Console.WriteLine("Unloading");
    }
}

Thanks in advance.
Yours sincerely
Frank Dörsam

@jkotas
Copy link
Member

jkotas commented Nov 6, 2018

Answered in https://github.com/dotnet/corefx/issues/19773#issuecomment-435888506

We should make sure to include this in the documentation for assembly unloading at https://docs.microsoft.com/en-us/

@janvorli
Copy link
Member

Closing this issue as we have already published a doc on unloading at https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability-howto

@masums
Copy link

masums commented Jul 10, 2019

Unfortunately Microsoft's example project also unable to delete dll after unloading.
https://github.com/dotnet/samples/tree/master/core/tutorials/Unloading
I have tested this example project. Deleted plugins after Console.WriteLine().
Throws exception. I have added following lines

            Console.WriteLine($"Unload success: {!hostAlcWeakRef.IsAlive}");
            File.Delete(pluginFullPath);

@NMSAzulX
Copy link

I had the same problem and I'm going to do more tests tonight.

@NMSAzulX
Copy link

@masums @FrankDoersam I solved this problem using stream loading.Maybe it can solve your problem.

if (!DynamicDlls.ContainsKey(path))
{
        using (FileStream stream = new FileStream(path, FileMode.Open))
        {
              var assembly = LoadFromStream(stream);
              var types = assembly.GetTypes();

              for (int i = 0; i < types.Length; i++)
              {
                   ClassMapping[types[i].Name] = assembly;
              }
        }
}

It's ok.
But before you unload it, you have to clear the dictionary.
DynamicDlls.Clear(); (Note: Don't do it in the 'Main' method.)

@masums
Copy link

masums commented Aug 22, 2019 via email

@NMSAzulX
Copy link

@masums Maybe using AssemblyDependencyResolver.
Although I don't know how to use it yet, I'm just referring to the official example.

I have a project based on roslyn. I use DependencyContext.Default.CompileLibraries to get all dependencies when I compile dynamically.

@NMSAzulX
Copy link

@masums Unwittingly, I found a solution to this.

In the constructor

if (arg == "Default")
{
     Default.Resolving += Default_Resolving;
}

In the class

//Default will 
private Assembly Default_Resolving(AssemblyLoadContext arg1, AssemblyName arg2)
{
   return Load(arg2);
}
protected override Assembly Load(AssemblyName assemblyName)
{
    string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
    if (assemblyPath != null)
    {
         return Handler(assemblyPath);
    }

    return null;
}

@janvorli I'm a little confused. If I use the Resolving event instead of implementing the Load method, it will also work. Load and Resolving have the same functionality?

@janvorli
Copy link
Member

@vitek-karas can you please answer the question about Resolving event vs Load method?

@janvorli
Copy link
Member

janvorli commented Sep 27, 2019

Unfortunately Microsoft's example project also unable to delete dll after unloading.

@masums I am sorry, I've missed your comment on this here. It should work, so let me try to figure out what's wrong.

@NMSAzulX
Copy link

@janvorli I have an issue. I learned SOS debugging and printed out some information, but I don't know if it's useful? https://github.com/dotnet/coreclr/issues/26386

@vitek-karas
Copy link
Member

  • AssemblyLoadContext.Load method is the first thing called during the resolution. So this gives you the ability to resolve all assembly requests in the load context. Only once this returns null, the control goes to the default load context as a fallback. So this allows you to load a different version of a given assembly than the one in the default context.
  • AssemblyLoadContext.Resolving event is fired when all other attempts for resolution failed - it's basically the last chance (almost). So in this case if the assembly exists in the default load context it would not get to the event handler at all.

The AssemblyDependencyResolver is typically used in the Load method (to allow overrides of the default context). But obviously it depends on your scenario.

Another difference is that the Resolving event can be handled even on the default load context (you can add your event handler to AssemblyLoadContext.Default.Resolving. It is not possible to override the Load method on the default context though (this is intentional - it would be really easy to completely break everything if that method had a bug in it).

@NMSAzulX loading from memory should work, but there is a downside - it will not work with Ready2Run images (assemblies precompiled via crossgen). That might be OK in your scenario though. It's also obviously slower as you're making a copy (loading from file just memory maps the file which is typically faster and consumes less memory).

@NMSAzulX
Copy link

@janvorli @vitek-karas Thank you very much.Will there be any documents coming out?

@vitek-karas
Copy link
Member

Documentation about the AssemblyLoadContext and the actual order in which things happen is here: https://docs.microsoft.com/en-us/dotnet/core/dependency-loading/loading-managed

@NMSAzulX
Copy link

@vitek-karas OK .

@masums
Copy link

masums commented Sep 28, 2019

Today I have tested https://github.com/dotnet/samples/tree/master/core/tutorials/Unloading this code again. Now it is working with the new release of .Net core 3.0.100. Delete is working for unloaded dll. May be It will also work for me if I update the framework version.

@msftgits msftgits transferred this issue from dotnet/coreclr Jan 31, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 15, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-VM-coreclr documentation Documentation bug or enhancement, does not impact product or test code
Projects
None yet
Development

No branches or pull requests

6 participants