none
Delete loaded assembly

    Question

  • Hi there,

    I am trying to load an assembly into my AppDomain, after analyzing the assembly, I want to be able to delete the assembly from the disk. Here is my code:

    class MainClass
    {
        public static void Main()
        {
            string file = @"D:\Temp\Test\Setup.dll";
            AppDomainSetup domaininfo = new AppDomainSetup();
            domaininfo.ShadowCopyFiles = "true";
            domaininfo.ShadowCopyDirectories = null;
            AppDomain d = AppDomain.CreateDomain("NewDomain", null, domaininfo);
            Assembly asse = Assembly.LoadFrom(file);
            // Do something with "asse"
            AppDomain.Unload(d);
            File.Delete(file);
        }
    }


    An exception "System.UnauthorizedAccessException" was seen on the last line of the code (deleting the file). Apprently, the file was still locked before my application exits.

    I am using VS2008 on Vista x64 if this matters.

    I think I am following what is said to be the ONLY way to do this, but what am I doing wrong?

    Best regards,

    Robin

    Monday, February 18, 2008 9:35 PM

Answers

  • Alois is right, and I write some code to show you a easy approach for this issue:

        1 // Path of the assembly, make sure this assembly is not used by other applications now

        2 String asmPath = @"D:\Working\MyDll.dll";

        3 

        4 Assembly asm = Assembly.Load(File.ReadAllBytes(asmPath));

        5 

        6 File.Delete(asmPath);

        7 

        8 foreach (Type t in asm.GetTypes())

        9     Console.WriteLine(t.FullName);


    Thanks!
    Thursday, February 21, 2008 3:20 AM
  • Joe Duffy has already answered this question here:

    Yours,
     Alois Kraus

    Tuesday, February 19, 2008 10:38 PM

All replies

  • This is because when you call AppDomain.Load(ASSEMBLYNAME), the function will return an instance of Assembly class in the current domain. But Assembly class is not derived from MarshalByRefObject, so when you pass this instance of type Assembly cross domain boundary, CLR will also load the assembly in the current domain since it cannot be accessed using proxy.

    To work around this issue, just read all the binary data from the assembly file and load the byte array, since most of the assemblies are not very large(less than about 500KB, maybe 100KB). Or just set the new AppDomain’s codebase directory in AppDomain.CreateDomain method’s AppDomainSetup argument, and call AppDomain.CreateInstance, this needs you to know name of one of the types in the assembly of course.

    Tuesday, February 19, 2008 3:23 AM
  • Thanks for the reply.

    I tried (I think) the first workaround as you suggested.

        public static void Main()
        {
            string file = @"D:\Temp\Test\Setup.dll";
            AppDomainSetup domaininfo = new AppDomainSetup();
            domaininfo.ShadowCopyFiles = "true";
            domaininfo.ShadowCopyDirectories = null;
            AppDomain d = AppDomain.CreateDomain("NewDomain", null, domaininfo);

            FileStream fs = new FileStream(file, FileMode.Open);
            byte[] buffer = new byte[(int)fs.Length];
            fs.Read(buffer, 0, buffer.Length);
            fs.Close();
            d.Load(buffer);
            ...............

        }


    The last line above requires that the file "Setup.dll" to be in the same folder of my application binary, for instance, "D:\MyProject\Test\Debug". Otherwise, I'd receive a "FileNotFoundException". Anyways, even after I copied the file over to the application folder, I could not delete this file (newly copied in the application folder) after the secind AppDomain is unloaded. I could indeed delete the original file (in "D:\Temp\Test" folder), but that is not my intention in this situation as this file is irrelevant any more.

    I am not sure if I did the right thing to load the assembly as a byte array. But, what should I do then?

    Thank you very much!
    Tuesday, February 19, 2008 2:43 PM
  • Joe Duffy has already answered this question here:

    Yours,
     Alois Kraus

    Tuesday, February 19, 2008 10:38 PM
  • Your code to read the content of the assembly is not correct. It will not read all the content of the file.

    Using File.ReadAllBytes() instead.
    Wednesday, February 20, 2008 1:46 AM
  • Well, I tried to use File.ReadAllBytes(), although I do not know the difference. All I can see through debugger is that the resulted "buffer" had the same array. Anyways, it did not work either.
    Wednesday, February 20, 2008 1:52 PM
  • Me posting again.

    I tried the solution in the link that "Alois" has provided. I was able to remove the file after loading it through a memory stream. It seems to me that a "memory stream" is the key here because no matter what I tried, a simple byte array just does not cut it. This got me thinking. Because of the usage of a memory stream, the disk file is simply irrelavent. I do not have to use AppDomain any more.

    I want to restate my issue here. I want to be able to remove the assembly file on the disk after I am done analyzing it, without restarting my application. So, can anyone explain to me why so many people and topics insist that using AppDomain is key to solve my problem?

    Another interesting point is: while I was using Assembly.LoadFrom() or Assembly.LoadFile(), I was able to rename the disk file after the "Load" call, but I could not remove (delete) it. I could not figure out why.

    Thanks!
    Wednesday, February 20, 2008 2:54 PM
  • When a file is open it can be renamed. This is by design

     

    You do not need an AppDomain for reflection but if you use one you are able to unload the AppDomain and the loaded dlls which consume memory even if they are no longer needed. There is no way to unload an assembly from your own AppDomain.

     

    Yours,

       Alois Kraus

     

    Wednesday, February 20, 2008 5:02 PM
  • Alois is right, and I write some code to show you a easy approach for this issue:

        1 // Path of the assembly, make sure this assembly is not used by other applications now

        2 String asmPath = @"D:\Working\MyDll.dll";

        3 

        4 Assembly asm = Assembly.Load(File.ReadAllBytes(asmPath));

        5 

        6 File.Delete(asmPath);

        7 

        8 foreach (Type t in asm.GetTypes())

        9     Console.WriteLine(t.FullName);


    Thanks!
    Thursday, February 21, 2008 3:20 AM
  • Thank you, guys. I think I have enough information to move on. I just wish CLR provided an easy mechanism to totally unload binaries like in the Win32 world.

     

    Best regards!

     

     

     

    Thursday, February 21, 2008 1:42 PM
  • Friday, February 22, 2008 1:57 AM
  • Hi,

    I'm trying the do the following:
    1. Load an assembly that may be changing constantly.
    2. Use the assembly wrapped by some handler class.

    I need to be able to use the assembly and delete it or replace it while running my main application. Is there a way to achieve this?

    Thanks in advance.

    Best Regards,

    César Campos
    Wednesday, December 03, 2008 5:57 PM