6

I am building a plugin-type system with each plugin represented as a DLL. I would like to be able to reload them without stopping the main application. This means that they must be loaded at runtime, without pre-built links between them (do a filesearch for dlls and load them). I have this set up using Assembly.LoadFile(filename), however, when I try to use File.Copy to replace the DLL it throws an exception, saying something akin to 'file in use'. I have tried using AppDomain, loading all plugins through this secondary domain, and unloading it before reloading, but this throws the same exception.

My current code:

        if (pluginAppDomain != null)
            AppDomain.Unload(pluginAppDomain);
        foreach (string s in Directory.GetFiles(path_to_new_DLLs))
        {
            string name = s.Substring(s.LastIndexOf('\\') + 1);
            Console.WriteLine("Copying " + name);
            File.Copy(s, Path.Combine(current_directory, name), true); // Throws exception here
        }
        AppDomainSetup setup = new AppDomainSetup();
        setup.ApplicationBase = Environment.CurrentDirectory;
        setup.ShadowCopyFiles = "true"; 
        // I think this is where the problem is, maybe I'm forgetting to set something
        pluginAppDomain = AppDomain.CreateDomain("KhybotPlugin", null, setup);
        foreach (String s in Directory.GetFiles(Environment.CurrentDirectory, "*.dll"))
        {
            int pos = s.LastIndexOf('\\') + 1;
            Assembly dll = pluginAppDomain.Load(s.Substring(pos, s.Length - pos - 4));
            // Elided... Load types from DLL, etc, etc
        }
  • 1
    Why not use the Managed Extensibility Framework http://msdn.microsoft.com/en-us/library/dd460648.aspx already built in to .NET which does this? – Dour High Arch Apr 02 '12 at 16:37

3 Answers3

5

Generally you need to unload the AppDomain for the communication. If you want to prevent the mentioned error you simply can load your dll by using Assembly.Load(Byte[]).

You can also use the Managed Extensibility Framework which will save you a lot of work.

Assembly.Load issue solution

Assembly loaded using Assembly.LoadFrom() on remote machine causes SecurityException

Community
  • 1
  • 1
Felix K.
  • 6,201
  • 2
  • 38
  • 71
  • +1. Also using Assembly.Load(byte[]) should be very last resort as it causes interesting issues with type identity. – Alexei Levenkov Apr 02 '12 at 16:47
  • Alexei: Care elaborating? Doing a simple `Assembly.Load(File.ReadAllBytes(filename))` solved everything for me, although I assume that the old dlls may be staying in memory, but that isn't a big issue. –  Apr 02 '12 at 16:57
  • Check out [Best Practices for Assembly Loading](http://msdn.microsoft.com/en-us/library/dd153782.aspx) - "No Context" section. – Alexei Levenkov Apr 02 '12 at 17:19
2

Loading plugin DLLs into another AppDomain is the only solution - so you are on the right path. Watch out for leaking object from second app domain to main one. You need to have all communication with plugins to be happening in plugin's AppDomain.

I.e. returning plugin's object to main code will likely leak plugin's Assembly usage to main AppDomain.

Start with very simple code completely in plugin's AppDomain like "load assembly and create class, but don't return anything to main Domain". Than expand usage when you get more understanding on communication between AppDomains.

Note: unless you doing it for educational purposes using existing system (i.e. MEF) is better.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
0

You could do something like this ...

if (pluginAppDomain != null)
{
    AppDomain.Unload(pluginAppDomain);
}

//for each plugin
pluginAppDomain = AppDomain.CreateDomain("Plugins Domain");
x = pluginAppDomain.CreateInstanceFromAndUnwrap("Plugin1.dll", "Namespace.Type");

You should not reference the plugins in your main app directly. Put them in separate project/s and reference them through an interface.

danze
  • 745
  • 2
  • 10
  • 24
  • Note that it OP said "tried using AppDomain, loading all plugins through this secondary domain". Your sample most likely causes the type to be loaded in both AppDomains. – Alexei Levenkov Apr 02 '12 at 16:45
  • @AlexeiLevenkov, I have edited my response. I use the same design for my test automation tool, to unload old test dlls and load with new ones. – danze Apr 02 '12 at 17:09