3

there are many similar questions like this one. But they are all years old and dont work (anymore?) today.

So here is my question: I want to load a c# assembly into memory and find out if it has types included that match an interface i want to use. If the assembly has this class type included, i reference and use it in my main application. If not, i want to unload the dll and then be able to delete the file (from my main application or by hand while the application is running).

My first implementation simply ommitted the appdomain approach, but it worked (without deleting or replacing assembly files). Then I tried to load the assemblies as described in various examples. But with none i was able to delete the not needed assembly files from disk.

Either it is simply not possible to unload and delete assemblies when they were referenced in an application or i am doing something wrong. This is what i tried at last:

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using ESTLogViewer_V2.Formulare;
using Parent;
namespace ESTLogViewer_V2
{
    static class Program
    {
        //<summary>
        //Der Haupteinstiegspunkt für die Anwendung.
        //</summary>
        [STAThread]
        static void Main()
        {
            LoadTest.Haupt(null);
        }
    }
}
namespace Parent
{
    public class Constants
    {
        // adjust
        public const string LIB_PATH = @"e:\PRJDOTNET4\ESTLogViewer V2\Plugins\LogFiles_Plugin.dll";
    }

    public interface ILoader
    {
        string Execute();
    }

    public class Loader : MarshalByRefObject, ILoader
    {
        public string Execute()
        {
            var assembly = Assembly.LoadFile(Constants.LIB_PATH);
            foreach (var t in assembly.GetTypes())
            {
                Debug.WriteLine(t.FullName);

            }
            return assembly.FullName;
        }
    }

    class LoadTest
    {
        public static void Haupt(string[] args)
        {
            var domain = AppDomain.CreateDomain("child");
            var loader = (ILoader)domain.CreateInstanceAndUnwrap(typeof(Loader).Assembly.FullName, typeof(Loader).FullName);
            Console.Out.WriteLine(loader.Execute());
            AppDomain.Unload(domain);
            domain = null;
            File.Delete(Constants.LIB_PATH);
        }
    }
}

So, this code works until the File.Delete statement. But then it throws an exception:

LogFiles_Plugin.clsSchritLogDateiSplitter
LogFiles_Plugin.clsLogFileMeldDateiSplitter
LogFiles_Plugin.clsLogFileMeldDateiSplitterNG
LogFiles_Plugin.clsAuftragsAnalyseSplitter
LogFiles_Plugin.clsAuftrag
LogFiles_Plugin.clsS_R_SignalSplitter
LogFiles_Plugin.clsLogFileGVMeldDateiSplitter
LogFiles_Plugin.clsLogFileGVTeleSplitter
LogFiles_Plugin.clsLogFileGVTeleSplitter+spalte
<PrivateImplementationDetails>{37CEBF0C-E73A-4FE7-B152-9A858E6C176D}
<PrivateImplementationDetails>{37CEBF0C-E73A-4FE7-B152-9A858E6C176D}+__StaticArrayInitTypeSize=6256
"ESTLogViewer V2.exe" (Verwaltet (v4.0.30319)): "C:\Windows\Microsoft.Net\assembly\GAC_MSIL\mscorlib.resources\v4.0_4.0.0.0_de_b77a5c561934e089\mscorlib.resources.dll" geladen
Eine Ausnahme (erste Chance) des Typs "System.UnauthorizedAccessException" ist in mscorlib.dll aufgetreten.

Right now, while writing this question, i did some more tests - i tried to find out why i could not delete the assembly file. I found out, that it was locked by the visual studio remote debugger.

So the second question is: How to avoid locking the file with the remote debugger?

fuz
  • 88,405
  • 25
  • 200
  • 352
Wolfgang Roth
  • 451
  • 4
  • 18
  • Related post - [How to unload an assembly from the primary AppDomain?](https://stackoverflow.com/q/123391/465053) – RBT Oct 15 '21 at 08:45

1 Answers1

6

I found that Assembly.LoadFile(...) will lock the file, even beyond the point of unloading the AppDomain.

My solution is to first load the assembly bytes into memory, and then load it into the AppDomain:

var assemblyBytes = System.IO.File.ReadAllBytes("myassembly.dll");
var assembly = System.Reflection.Assembly.Load(assemblyBytes);

That way the file is never locked and you can delete it, etc.

There's a better answer to a similar question here.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • Hello @John, I replaced the code in my example with your code and it worked. This is the solution for the "appdomain"-approach. But it should work without using appdomain either. – Wolfgang Roth Oct 10 '17 at 08:22
  • @WolfgangRoth While it should work for both, please note that (as the [linked answer](https://stackoverflow.com/a/18935973/3181933) says) you can't unload the assembly once it has been loaded unless it's in an AppDomain. [MSDN concurs](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/assemblies-gac/how-to-load-and-unload-assemblies) – ProgrammingLlama Oct 10 '17 at 08:25
  • I did not yet investigate the effects of multiple loadings of the assembly. But my primary idea of the assembly loading was to be able to implement plugins to my code which can be exchanged by the user. And every time the user updates the list of plugins, new ones should be added and older ones should be deleted from the list shown to the user - it may be, that the application wont be able to replace assemblies that differ only in the implemention but otherwhise have the same namespace and names... Will try this perhaps tomorrow. – Wolfgang Roth Oct 10 '17 at 19:58