4
votes

I am writing a plugin architecture. My plugin dlls are located in a sub directory from where the plugin manager is running. I am loading the plugins into a separate AppDomain as the following:

string subDir;//initialized to the path of the module's directory.
AppDomainSetup setup = new AppDomainSetup();
setup.PrivateBinPath = subDir;
setup.ApplicationBase = subDir;

AppDomain newDomain= AppDomain.CreateDomain(subDir, null, setup);

byte[] file = File.ReadAllBytes(dllPath);//dll path is a dll inside subDir
newDomain.Load(file);

However. newDomain.Load returns an assembly which the currently domain attempts to load. Because the plugin dlls are in a sub directory, the current domain cannot and should not see these dlls and the current domain throws a FileLoadException "ex = {"Could not load file or assembly ... or one of its dependencies."

The question is, can we load an assembly into a separate AppDomain without it returning the loaded assembly?

I know I can add a handler for the AssemblyResolve event in the current domain and return a null, but I would prefer to not to go this route.

Thanks in advance.

2
If you're building a plugin architecture, maybe this article would be useful: msdn.microsoft.com/en-us/magazine/cc163476.aspxSimon Mourier

2 Answers

5
votes

You can also use DoCallBack - here's something I put together after reading lots about it on SO. This creates an appdomain, checks that the assemblies have the same signature public key, loads the assembly, executes a static method, unloads the appdomain, then deletes the dll.

static void Main(string[] args)
    {
        string unknownAppPath = @"path-to-your-dll";

        Console.WriteLine("Testing");
        try
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.AppDomainInitializer = new AppDomainInitializer(TestAppDomain);
            setup.AppDomainInitializerArguments = new string[] { unknownAppPath };
            AppDomain testDomain = AppDomain.CreateDomain("test", AppDomain.CurrentDomain.Evidence, setup);
            AppDomain.Unload(testDomain);
            File.Delete(unknownAppPath);
        }
        catch (Exception x)
        {
            Console.WriteLine(x.Message);
        }
        Console.ReadKey(); 
    }

    public static void TestAppDomain(string[] args)
    {
        string unknownAppPath = args[0];
        AppDomain.CurrentDomain.DoCallBack(delegate()
        {
            //check that the new assembly is signed with the same public key
            Assembly unknownAsm = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(unknownAppPath));
            //get the new assembly public key
            byte[] unknownKeyBytes = unknownAsm.GetName().GetPublicKey();
            string unknownKeyStr = BitConverter.ToString(unknownKeyBytes);
            //get the current public key
            Assembly asm = Assembly.GetExecutingAssembly();
            AssemblyName aname = asm.GetName();
            byte[] pubKey = aname.GetPublicKey();
            string hexKeyStr = BitConverter.ToString(pubKey);
            if (hexKeyStr == unknownKeyStr)
            {
                //keys match so execute a method
                Type classType = unknownAsm.GetType("namespace.classname");
                classType.InvokeMember("method-you-want-to-invoke", BindingFlags.InvokeMethod, null, null, null);
            }
        });
    }
2
votes

As pointed out in links given below:

Loading/Unloading assembly in different AppDomain

Load Assembly in New AppDomain without loading it in Parent AppDomain

It seems that calling Load() method on another AppDomain object causes that assembly to be loaded in current AppDomain as well.

The solution is to use CreateInstanceFromAndUnwrap() method of AppDomain class instead. You pass a path to assembly and a type to be converted to.