"Close" DLL opened

Asked

Viewed 204 times

4

I created a dll to run in Runtime a class:

var classe = 
    "using System; " +
    "public static class CSharp" +
    "{" +
        "public static double Executar()" +
        "{" +
            "return 0;" +
        "}" +
    "}";
var compilation = CSharpCompilation.Create("CSharp")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
    MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(classe));

var fileName = "CSharp.dll";

compilation.Emit(fileName);

var carregarDll = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));

When I run the first time, it works, from the second time on error saying that the file is already in use...

System.IO.Ioexception: 'The process cannot access the file 'C:... Csharp.dll' because it is being used by Another process.'

I tried some kind forms with the stream.Close(), but it didn’t work, I never used a dll and such, so I don’t even know what to try or search to find...


UPDATE 1

I’m taking a look at Csharpcompilation, Emit() has a cancellationToken, but I don’t know how to use it, I’ll keep searching...

  • Do you really have to play this dynamic code within the context of the main application? Can’t you create a domain within the application that can host that dynamic(s) class(s)? Reference AppDomain

  • I never used Appdomain... so I didn’t see it as a possibility... I did a search here, it would be more or less that?

  • I will not only copy and paste, but understand what you are doing, so I want to know if it would be right to follow the link I sent

  • I did not analyze in depth the link you sent, because I am working, but on top is very close to what I suggested (probably exactly my suggestion). If the use of AppDomain go in the direction you are looking for, the night I can give an example of how to compile, compile and discard compiled code next to the domain.

  • Puts @Augustovasques, would help me a lot, thank you if you can show me yes...

  • I went to write the example and I have two bad news. 1: NET Core 3 does not support multiple domains. 2: The method Assemblyloadcontext.Unload only exists in the documentation. For now the only way this code progress is in . NET 4

  • I’m using the . net 2.2

  • But 'Unload' is only . net 3 ne...

  • I expressed myself badly. Core . NET is one thing and . NET Framework is another. This code you wrote is for Core . NET this code will only progress in . NET Framework.

Show 5 more comments

1 answer

3


I created a . NET Core 3.0 project and solved my problem using Assemblyloadcontext.Unload which was suggested by Augusto Vasques:

I did it in two ways:

1: Inmemory:

[MethodImpl(MethodImplOptions.NoInlining)]
private static void ExecuteInMemoryAssembly(Compilation compilation, int i)
{
    var context = new CollectibleAssemblyLoadContext();

    using (var ms = new MemoryStream())
    {
        var cr = compilation.Emit(ms);
        ms.Seek(0, SeekOrigin.Begin);
        var assembly = context.LoadFromStream(ms);

        var type = assembly.GetType("CSharp");
        var greetMethod = type.GetMethod("Executar");

        var instance = Activator.CreateInstance(type);
        var result = greetMethod.Invoke(instance, null);
    }
    context.Unload();
}

2: With the Dll

[MethodImpl(MethodImplOptions.NoInlining)]
private static void ExecuteAssembly(int i, string path)
{
    var context = new CollectibleAssemblyLoadContext();
    var assemblyPath = Path.Combine(Directory.GetCurrentDirectory(), path);

    using (var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
    {
        var assembly = context.LoadFromStream(fs);

        var type = assembly.GetType("CSharp");
        var method = type.GetMethod("Executar");

        var instance = Activator.CreateInstance(type);
        var result = method.Invoke(instance,null);
    }

    context.Unload();
}

Collectibleassemblyloadcontext:

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

    protected override Assembly Load(AssemblyName assemblyName)
    {
        return null;
    }
}

Controller:

private static Assembly SystemRuntime = Assembly.Load(new AssemblyName("System.Runtime"));

[HttpGet]
public void Get()
{
    var fileName = "CSharp.dll";
    var classe = "public class CSharp \r\n" +
       "\t{\r\n" +
       "\t\t public double Executar()\r\n" +
       "\t\t{\r\n" +
       "\t\t\t return 26; \r\n" +
       "\t\t}\r\n" +
       "}";
    var compilation = CSharpCompilation.Create("DynamicAssembly", new[] { CSharpSyntaxTree.ParseText(classe) }, new[] {
    MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
    MetadataReference.CreateFromFile(typeof(Console).GetTypeInfo().Assembly.Location),
    MetadataReference.CreateFromFile(SystemRuntime.Location),
   }, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

    for (var i = 0; i < 3000; i++)
    {
        ExecuteInMemoryAssembly(compilation, i); // (1)
        ExecuteAssembly(i, Path.GetFullPath(fileName));  //  (2)
    }

    GC.Collect();
    GC.WaitForPendingFinalizers();
}

REFERENCES:

https://www.strathweb.com/2019/01/collectible-assemblies-in-net-core-3-0/

https://docs.microsoft.com/pt-br/dotnet/standard/assembly/unloadability

Browser other questions tagged

You are not signed in. Login or sign up in order to post.