Loading an assembly into an AppDomain outsite of applicationBase C#
Asked Answered
F

2

6

So lately I've been working on a project where the application (or executable,whatever you wish to call it) needs to be able to load and unload assemblies not found within the executable's folder at all. (might even be another drive)

For the sake of an example , I want to be able to have my application at D:\AAA\theAppFolder , and the assemblies of the DLL files at C:\BBB\Assemblies

Looking thoroughly , I found out AppDomain allow the ability to unload themselves and any attached assemblies , so I figured I'd give it a shot, however there seems to be an issue after a few hours worth of attempts : AppDomains cannot look anywhere outside the application base.

According to AppDomain's documentary (and my own experience) you cannot set the PrivateBinPath outside of ApplicationBase , and if I set the ApplicationBase outside of the drive the application is at (via AppDomainSetup) , I get System.IO.FileNotFoundException complaining it can't find the application itself.

Because of that I can't even reach the phase where I can use the AssemblyResolve ResolveEventHandler to attempt to get the assembly using MarhsalByRefObject inheriting classes...

Here's a few snippets of code related to what I am currently attempting

    internal class RemoteDomain : MarshalByRefObject
    {
        public override object InitializeLifetimeService() //there's apparently an error for marshalbyref objects where they get removed after a while without this
        {
            return null;
        }
        public Assembly GetAssembly(byte[] assembly)
        {
            try
            {
                return Assembly.Load(assembly);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return null;
        }
        public Assembly GetAssembly(string filepath)
        {
            try
            {
                return Assembly.LoadFrom(filepath);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            return null;
        }
    }

    public static Assembly LoadAssembly(string modName, BinBuffer bb)
    {
        string assembly = pathDirTemp+"/"+modName+".dll";
        File.WriteAllBytes(assembly, bb.ReadBytes(bb.BytesLeft()));
        RemoteDomain loader = (RemoteDomain)modsDomain.CreateInstanceAndUnwrap(typeof(RemoteDomain).Assembly.FullName, typeof(RemoteDomain).FullName);
        return loader.GetAssembly(assembly);
    }

To be as specific as I can : Is there any way to get an unloadable AppDomain to load an assembly that is not within the application's base folder?

Fairminded answered 10/10, 2013 at 10:56 Comment(3)
How you create the AppDomain?Alkanet
I've had multiple approaches on that , the current one is modsDomain = AppDomain.CreateDomain("randomname",null,new AppDomainSetup() { PrivateBinPath = AssembliesFolderPath }); I know the path is valid since Directory.CreateDirectory(called in another part of the code) creates the folder just fine.Fairminded
Using Assembly.Load(byte[]) is asking for trouble. Compounded by storing assemblies in a temp directory and also getting the type unwrapped into the primary appdomain.Ostrogoth
N
7

Each AppDomain has it's own base directory and is not constrained at all by the main application base dir (unless it is the main AppDomain of the application). So you can achieve what you want using AppDomains.

The reason your approach doesn't work is that you are passing Assembly objects between AppDomains. When you call any of the GetAssembly methods, the assembly will be loaded in the child AppDomain but when the method returns, the main AppDomain will try to load the Assembly as well. Of course, the assembly will not be resolved because it is not in the main AppDomains's base dir, private paths or the GAC.

So in general you should never pass Type or Assembly objects between AppDomains.

A simple way to load assemblies without leaking them to the main AppDomain can be found in this answer.

Of course to make your application work with assemblies loaded in child AppDomains you will have to make MarshalByRefObject derived classes that will be your access point between AppDomains.

Neman answered 11/10, 2013 at 10:47 Comment(1)
Your answer explained what I observed. I passed Type(located in parent domain not child) from parent domain to child domain, then it tried to load the Type from child, then it threw file not found exception. Your answer saves my time. Thanks Panos!Integer
P
-6

Maybe you need to use global variable so if you use global variable to fix the problem you can declare readonly global variable for example:

public static string a = "Moosaie";

Convert it to

public static readonly a = "Moosaie";

Anyway you can not use global dynamic value variable for CLR assembly.

Panic answered 15/7, 2015 at 9:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.