AppDomain.Load() fails with FileNotFoundException

when you load an assembly into the AppDomain in that way, it is the current AppDomain's PrivateBinPath that is used to find the assembly.

For your example, when I added the following to my App.config it ran fine:

  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <probing privatePath="[PATH_TO_PLUGIN]"/>

This is not very useful to you though.

What I did instead was to create a new assembly that contained the IPostPlugin and IPluginsHost interfaces, and also a class called Loader that looked like this:

public class Loader : MarshalByRefObject
    public IPostPlugin[] LoadPlugins(string assemblyName)
        var assemb = Assembly.Load(assemblyName);

        var types = from type in assemb.GetTypes()
                where typeof(IPostPlugin).IsAssignableFrom(type)
                select type;

        var instances = types.Select(
            v => (IPostPlugin)Activator.CreateInstance(v)).ToArray();

        return instances;

I keep that new assembly in the application root, and it doesn't need to exist in the plugin directories (it can but won't be used as the application root will be searched first).

Then in the main AppDomain I did this instead:


Loader loader = (Loader)Activator.CreateInstance(
    BindingFlags.Public | BindingFlags.Instance,

var plugins = loader.LoadPlugins(AssemblyName.GetAssemblyName(f.FullName).FullName);

foreach (var p in plugins)


So I create an instance of the known Loader type, and then get that to create the plugin instances from within the plug-in AppDomain. That way the PrivateBinPaths are used as you want them to be.

One other thing, the private bin paths can be relative so rather than adding d.FullName you could add pluginsDir + Path.DirectorySeparatorChar + d.Name to keep the final path list short. That's just my personal preference though! Hope this helps.