18

i have a weird problem. i would like to delete an assembly(plugin.dll on harddisk) which is already loaded, but the assembly is locked by the operating system (vista), even if i have unloaded it.

f.e.

AppDomainSetup setup = new AppDomainSetup();
setup.ShadowCopyFiles = "true";
AppDomain appDomain = AppDomain.CreateDomain(assemblyName + "_AppDomain", AppDomain.CurrentDomain.Evidence, setup);
IPlugin plugin = (IPlugin)appDomain.CreateInstanceFromAndUnwrap(assemblyName,                        "Plugin.MyPlugins");

I also need the assemblyinfos, because I don't know which classes in the pluginassembly implements the IPlugin Interface. It should be possible to have more than one Plugin in one Pluginassembly.

Assembly assembly = appDomain.Load(assemblyName);
if (assembly != null) {
   Type[] assemblyTypes = assembly.GetTypes();
   foreach (Type assemblyTyp in assemblyTypes) {
      if (typeof(IPlugin).IsAssignableFrom(assemblyTyp)) {
         IPlugin plugin = (IPlugin)Activator.CreateInstance(assemblyTyp);
         plugin.AssemblyName = assemblyNameWithEx;
         plugin.Host = this;
      }
   }
}
AppDomain.Unload(appDomain);

How is it possible to get the assemblyinfos from the appDomain without locking the assembly?

best regards

starblue
  • 55,348
  • 14
  • 97
  • 151
Ase
  • 1,105
  • 1
  • 9
  • 10

5 Answers5

21

I think i've the answer! the answer from Øyvind Skaar will not work, if you would like to delete the loaded assembly.

instead of

using (FileStream dll = File.OpenRead(path))
{
   fileContent = new byte[dll.Length];
   dll.Read(fileContent, 0, (int)dll.Length);
}
Assembly assembly = appDomain.Load(fileContent);

you have to use

byte[] b = File.ReadAllBytes(assemblyName);
assembly = Assembly.Load(b);

best regards

Tch
  • 234
  • 1
  • 5
  • 1
    It worked fine when I tried it, but yours is shorter anyway;) – Øyvind Skaar Jan 12 '09 at 18:13
  • There are a lot of similar questions on unloading DLLs and using separate AppDomains, but they don't seem to work when you actually want to *delete* the DLL. This definitely does the trick regardless :) – Jedidja Jul 25 '13 at 18:59
  • Nice! Don't even need another app domain. – Thomas Eyde Aug 18 '15 at 10:03
  • this does not work if you use another app domain, but this is definitely a better approach than complicating things using app domain :) – am05mhz Jan 13 '16 at 02:18
  • 1
    It is not clear to me how the .dll is unloaded if only AppDomains can be unloaded. The solution that uses the byte array does not appear to use any AppDomain. Can someone please clarify? –  Nov 13 '16 at 00:42
9

I know this thread is quite dead, but I am currently working on this and I just got the answer (at 1h30 AM...)

AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
AppDomain app = AppDomain.CreateDomain("YaCsi", null, setup);
app.DoCallBack(LoaderCallback);
AppDomain.Unload(app);
File.Delete("__YaCsi_Test01.dll");

static void LoaderCallback()
{
    byte[] raw = File.ReadAllBytes("__YaCsi_Test01.dll");
    Assembly yacsi = Assembly.Load(raw);
    ((IScript)yacsi.CreateInstance("Script")).Go();
}

And it actually don't throw any exceptions!!! Hope someone will read this and that it will answer there question!

  • 1
    This is correct. This should work. The reason that the other way does not work is: when you call `app.Load()`, the assembly is loaded in the application domain `app` as well as in the application domain in which the code is running. After you unload `app`, the assembly is still loaded in the other application domain. Using `app.DoCallBack` makes the code run in `app` and thus results in the assembly only being loaded in `app`. (sources: http://msdn.microsoft.com/en-us/library/36az8x58.aspx and http://msdn.microsoft.com/en-us/library/system.appdomain.docallback.aspx) – Matthijs Wessels Dec 13 '11 at 22:37
5

See these pages:

Set a new AppDomain AppDomainSetup with LoaderOptimization.MultiDomainHost

E.g.

domainnew = AppDomain.CreateDomain(newdomain_name, null, new AppDomainSetup {
        ApplicationName = newdomain_name,
        ApplicationBase = assembly_directory,
        ConfigurationFile = ConfigurationManager.OpenExeConfiguration(assemblylocation).FilePath,
        LoaderOptimization = LoaderOptimization.MultiDomainHost,
        ShadowCopyFiles = shadowcopy ? "true" : "false",
    }
);
Mark Ursino
  • 31,209
  • 11
  • 51
  • 83
kiii
  • 441
  • 6
  • 4
3

What we do, is to have one folder that is watched for assemblies. When an assembly is added, the application copies it to a temp directory gives it a unique file name and loads it from there.

When the application is first loaded, it tries to clear out the temp directory.

I don't think that directly answers your question, but it probably solves your problem.

Stever B
  • 4,117
  • 2
  • 26
  • 18
3

If you load the assembly as a stream, it should work.

byte[] fileContent;
string path = "../../../test/bin/Debug/test.dll"; //Path to plugin assembly
using (FileStream dll = File.OpenRead(path))
{
   fileContent = new byte[dll.Length];
   dll.Read(fileContent, 0, (int)dll.Length);
}
Assembly assembly = appDomain.Load(fileContent);
File.Delete(path);
Øyvind Skaar
  • 1,842
  • 14
  • 22