Custom AppDomain and PrivateBinPath
Asked Answered
D

3

11

I'm using c# 4.0 and a console application just for testing, the following code does gives an exception.

AppDomainSetup appSetup = new AppDomainSetup()
{
    ApplicationName = "PluginsDomain",
    ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
    PrivateBinPath = @"Plugins",
    ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
};

AppDomain appDomain = AppDomain.CreateDomain("PluginsDomain", null, appSetup);

AssemblyName assemblyName = AssemblyName.GetAssemblyName(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins", "sample.dll"));

Assembly assembly = appDomain.Load(assemblyName); //This gives an exception of File not found

AppDomain.Unload(appDomain);

I keep getting File not found exception when using Load on my created AppDomain.

Thanks.

Delmadelmar answered 8/7, 2011 at 15:21 Comment(2)
Try look here: https://mcmap.net/q/187012/-how-to-load-an-assembly-to-appdomain-with-all-references-recursively/735864Tam
@danyolgiax, the solution provided there doesnt use a new created app domain, he just used Assembly.LoadFrom which i guess it loades it into the current app domain which is not what i want, unless am wrong about something, thnx anyway.Delmadelmar
D
6

I think i'v figured out why this happens, thats because the current domain needs to load the assembly too even if your loading the assembly in a diffrent app domain, the current domain needs to know about it and load it, thats because how the .NET was designed.

check here for details.

http://msdn.microsoft.com/en-us/library/36az8x58.aspx

and when i checked the fusion logs, i found that the newly created app domain was successfully able to load the assembly from the private bin path, and the reason why you still get the exception of "File not found", because this exception belongs originally to the current app domain.

that means if you copied the assembly in current application path or to the path where the current domain is probing, you will find that you can load the assembly into your custom domain.

Hope that helps.

Delmadelmar answered 12/7, 2011 at 0:20 Comment(5)
"This method should be used only to load an assembly into the current application domain. This method is provided as a convenience for interoperability callers who cannot call the static Assembly.Load method." You're doing it wrong. You should be injecting a class across the AD barrier and having that class load the assembly using Assembly.Load or Assembly.LoadFrom. You might as well ditch the other appdomain completely, as you are not isolating the assemblies from your executing AppDomain.Quiche
Will is right. You're supposed to call something like appDomain.CreateInstanceAndUnwrap to create an object that inherits from MarshalByRefObject on your new domain and return a proxy. Then you can use that proxy as if it were a normal object created on your main domain. If that object has a LoadTheNecessaryAssemblies method, if you call that method from the proxy on the main domain, as the method will be really run on your new domain, you'll be loading the necessary assemblies in your new domain.Predicate
Ahmed's answer is spot-on, reading the docs will do wonders. I disagree with @Will's opinion "you should be injecting a class", because sometimes an assembly resolve handler is still required even when you've successfully 'injected' (aka. "create instance of type in target appdomain") because some assemblies which reside in private bin folders are not resolved (like the Plugins folder.) that said, the real fix for the original code is to specify a full path, the assembly resolve handler should not be necessary. fusion logs are gospel in these scenarios.Verona
@ShaunWilson the need to load assemblies doesn't negate anything I said. My point was that, if you're using AppDomains to isolate code, you shouldn't (and absolutely do not have to) load that code into the current AppDomain. "[B]ecause the current domain needs to load the assembly too even if your loading the assembly in a diffrent app domain..." This statement is completely false.Quiche
ah, I see, so "you could instead use Activator.CreateInstance to create an instance of target type(s) in the other appdomain, and as long as you do not attempt to unwrap the returned object handles you will not need to copy the assembly over remoting boundaries"Verona
S
11

I came across this thread when attempting to dynamically load a dll file from a directory outside of the bin directory. Long story short, I was able to accomplish this by using the AppDomain.CurrentDomain.AssemblyResolve event. Here is the code:

//--begin example:

public MyClass(){
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    foreach (var moduleDir in _moduleDirectories)
    {
        var di = new DirectoryInfo(moduleDir);
        var module = di.GetFiles().FirstOrDefault(i => i.Name == args.Name+".dll");
        if (module != null)
        {
            return Assembly.LoadFrom(module.FullName);
        }
    }
    return null;
}

//---end example

The method CurrentDomain_AssemblyResolve is called each time the AppDomain.CurrentDomain.Load("...") method is called. This custom event handler does the job of locating the assembly using your own custom logic (which means you can tell it to look anywhere, even outside of the bin path, etc). I hope this saves someone else a few hours...

Stearic answered 22/5, 2013 at 19:43 Comment(1)
The code above doesn't work as is since args.Name is the fully qualified name of the assembly required and i.Name will give you the file name. Please keep this in mind when you copy paste from above (and patch it in your code). Simplest way could be to match against args.Name.Split(',')[0] + ".dll"Quartered
A
10

First make sure Plugins is a subdirectory of your AppDomain base path. PrivateBinPath will only work on subdirectories as described here

If that isn't the problem then take a look at your fusion binding logs. Use the fusion log viewer There is also a good Blog Post on that. The fusion logs will tell you where it searched for the assembly. That should tell you if your path is included in the search.

One of the other possibilities is that it is finding your assembly but not one of its dependencies. Again the fusion log viewer will tell you.

Albertinealbertite answered 8/7, 2011 at 20:0 Comment(3)
Thanks for your time, the Plugins folder is a sub-directory of the app domain base directory, but i figured out something, if i copied the dll to the application base directory it works, it doesnt work when its in the private bin path which is the "Plugins" folder. when i used the fusion log it doesnt seem that it searches in the private bin path, do u have any idea why ?Delmadelmar
"PrivateBinPath will only work on subdirectories" - That's why the darn thing doesn't work. Thank you thank you thank you.Geodetic
the link from: PrivateBinPath will only work on subdirectories as described does not work anymorePainty
D
6

I think i'v figured out why this happens, thats because the current domain needs to load the assembly too even if your loading the assembly in a diffrent app domain, the current domain needs to know about it and load it, thats because how the .NET was designed.

check here for details.

http://msdn.microsoft.com/en-us/library/36az8x58.aspx

and when i checked the fusion logs, i found that the newly created app domain was successfully able to load the assembly from the private bin path, and the reason why you still get the exception of "File not found", because this exception belongs originally to the current app domain.

that means if you copied the assembly in current application path or to the path where the current domain is probing, you will find that you can load the assembly into your custom domain.

Hope that helps.

Delmadelmar answered 12/7, 2011 at 0:20 Comment(5)
"This method should be used only to load an assembly into the current application domain. This method is provided as a convenience for interoperability callers who cannot call the static Assembly.Load method." You're doing it wrong. You should be injecting a class across the AD barrier and having that class load the assembly using Assembly.Load or Assembly.LoadFrom. You might as well ditch the other appdomain completely, as you are not isolating the assemblies from your executing AppDomain.Quiche
Will is right. You're supposed to call something like appDomain.CreateInstanceAndUnwrap to create an object that inherits from MarshalByRefObject on your new domain and return a proxy. Then you can use that proxy as if it were a normal object created on your main domain. If that object has a LoadTheNecessaryAssemblies method, if you call that method from the proxy on the main domain, as the method will be really run on your new domain, you'll be loading the necessary assemblies in your new domain.Predicate
Ahmed's answer is spot-on, reading the docs will do wonders. I disagree with @Will's opinion "you should be injecting a class", because sometimes an assembly resolve handler is still required even when you've successfully 'injected' (aka. "create instance of type in target appdomain") because some assemblies which reside in private bin folders are not resolved (like the Plugins folder.) that said, the real fix for the original code is to specify a full path, the assembly resolve handler should not be necessary. fusion logs are gospel in these scenarios.Verona
@ShaunWilson the need to load assemblies doesn't negate anything I said. My point was that, if you're using AppDomains to isolate code, you shouldn't (and absolutely do not have to) load that code into the current AppDomain. "[B]ecause the current domain needs to load the assembly too even if your loading the assembly in a diffrent app domain..." This statement is completely false.Quiche
ah, I see, so "you could instead use Activator.CreateInstance to create an instance of target type(s) in the other appdomain, and as long as you do not attempt to unwrap the returned object handles you will not need to copy the assembly over remoting boundaries"Verona

© 2022 - 2024 — McMap. All rights reserved.