How to Load an Assembly to AppDomain with all references recursively?
Solution 1
You need to invoke CreateInstanceAndUnwrap
before your proxy object will execute in the foreign application domain.
class Program
{
static void Main(string[] args)
{
AppDomainSetup domaininfo = new AppDomainSetup();
domaininfo.ApplicationBase = System.Environment.CurrentDirectory;
Evidence adevidence = AppDomain.CurrentDomain.Evidence;
AppDomain domain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);
Type type = typeof(Proxy);
var value = (Proxy)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
var assembly = value.GetAssembly(args[0]);
// AppDomain.Unload(domain);
}
}
public class Proxy : MarshalByRefObject
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
return Assembly.LoadFile(assemblyPath);
}
catch (Exception)
{
return null;
// throw new InvalidOperationException(ex);
}
}
}
Also, note that if you use LoadFrom
you'll likely get a FileNotFound
exception because the Assembly resolver will attempt to find the assembly you're loading in the GAC or the current application's bin folder. Use LoadFile
to load an arbitrary assembly file instead--but note that if you do this you'll need to load any dependencies yourself.
Solution 2
Once you pass the assembly instance back to the caller domain, the caller domain will try to load it! This is why you get the exception. This happens in your last line of code:
domain.Load(AssemblyName.GetAssemblyName(path));
Thus, whatever you want to do with the assembly, should be done in a proxy class - a class which inherit MarshalByRefObject.
Take in count that the caller domain and the new created domain should both have access to the proxy class assembly. If your issue is not too complicated, consider leaving the ApplicationBase folder unchanged, so it will be same as the caller domain folder (the new domain will only load Assemblies it needs).
In simple code:
public void DoStuffInOtherDomain()
{
const string assemblyPath = @"[AsmPath]";
var newDomain = AppDomain.CreateDomain("newDomain");
var asmLoaderProxy = (ProxyDomain)newDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(ProxyDomain).FullName);
asmLoaderProxy.GetAssembly(assemblyPath);
}
class ProxyDomain : MarshalByRefObject
{
public void GetAssembly(string AssemblyPath)
{
try
{
Assembly.LoadFrom(AssemblyPath);
//If you want to do anything further to that assembly, you need to do it here.
}
catch (Exception ex)
{
throw new InvalidOperationException(ex.Message, ex);
}
}
}
If you do need to load the assemblies from a folder which is different than you current app domain folder, create the new app domain with specific dlls search path folder.
For example, the app domain creation line from the above code should be replaced with:
var dllsSearchPath = @"[dlls search path for new app domain]";
AppDomain newDomain = AppDomain.CreateDomain("newDomain", new Evidence(), dllsSearchPath, "", true);
This way, all the dlls will automaically be resolved from dllsSearchPath.
Solution 3
http://support.microsoft.com/kb/837908/en-us
C# version:
Create a moderator class and inherit it from MarshalByRefObject
:
class ProxyDomain : MarshalByRefObject
{
public Assembly GetAssembly(string assemblyPath)
{
try
{
return Assembly.LoadFrom(assemblyPath);
}
catch (Exception ex)
{
throw new InvalidOperationException(ex.Message);
}
}
}
call from client site
ProxyDomain pd = new ProxyDomain();
Assembly assembly = pd.GetAssembly(assemblyFilePath);
Solution 4
On your new AppDomain, try setting an AssemblyResolve event handler. That event gets called when a dependency is missing.
Solution 5
You need to handle the AppDomain.AssemblyResolve or AppDomain.ReflectionOnlyAssemblyResolve events (depending on which load you're doing) in case the referenced assembly is not in the GAC or on the CLR's probing path.
Related videos on Youtube
abatishchev
This is my GUID. There are many like it but this one is mine. My GUID is my best friend. It is my life. I must master it as I must master my life. Without me, my GUID is useless. Without my GUID I am useless.
Updated on July 08, 2022Comments
-
abatishchev almost 2 years
I want to load to a new
AppDomain
some assembly which has a complex references tree (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll)As far as I understood, when an assembly is being loaded to
AppDomain
, its references would not be loaded automatically, and I have to load them manually. So when I do:string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory string path = System.IO.Path.Combine(dir, "MyDll.dll"); AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation; setup.ApplicationBase = dir; AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup); domain.Load(AssemblyName.GetAssemblyName(path));
and got
FileNotFoundException
:Could not load file or assembly 'MyDll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
I think the key part is one of its dependencies.
Ok, I do next before
domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies()) { domain.Load(refAsmName); }
But got
FileNotFoundException
again, on another (referenced) assembly.How to load all references recursively?
Do I have to create references tree before loading root assembly? How to get an assembly's references without loading it?
-
Mick over 9 yearsI've loaded assemblies like this many times before, I've never had to manually load all it's references. I'm not sure the premise of this question is correct.
-
-
abatishchev about 15 yearsSo I have to indicate requested assembly manually? Even it is in new AppDomain's AppBase ? Is there a way not to do that?
-
Tri Q Tran almost 13 yearsHow is this solution put into context of creating a new AppDomain, can someone explain?
-
Christoph Meißner almost 12 yearsA
MarshalByRefObject
can be passed around appdomains. So I would guess thatAssembly.LoadFrom
tries to load the assembly in a new appdomain, what is only possible, if the calling object could be passed between those appdomains. This is also called remoting as described here: msdn.microsoft.com/en-us/library/… -
NoWar over 11 yearsNice solution I am going to put link to it here. stackoverflow.com/questions/12427323/…
-
Jduv over 11 yearsThis doesn't work. If you execute the code and check the AppDomain.CurrentDomain.GetAssemblies() you'll see that the target assembly you're attempting to load is loaded into the current application domain and not the proxy one.
-
user1004959 over 11 yearsIt doesn't. Actually, you get an exception on the line you're registering this event on the new AppDomain. You've to register this event on the current AppDomain.
-
Jduv about 11 yearsCheck out the code I wrote to solve this problem: github.com/jduv/AppDomainToolkit. Specifically, look at the LoadAssemblyWithReferences method in this class: github.com/jduv/AppDomainToolkit/blob/master/AppDomainToolkit/…
-
Aaronaught over 10 yearsThis is complete nonsense. Inheriting from
MarshalByRefObject
doesn't magically make it load in every otherAppDomain
, it just tells the .NET framework to create a transparent remoting proxy instead of using serialization when you unwrap the reference from oneAppDomain
in anotherAppDomain
(the typical way being theCreateInstanceAndUnwrap
method). Can't believe this answer has over 30 upvotes; the code here just a pointlessly roundabout way of callingAssembly.LoadFrom
. -
Aaronaught over 10 yearsI've found that although this works most of the time, in some cases you actually still need to attach a handler to the
AppDomain.CurrentDomain.AssemblyResolve
event as described in this MSDN answer. In my case, I was trying to hook into the SpecRun deployment running under MSTest, but I think it applies to many situations in which your code might not run from the "primary" AppDomain - VS extensions, MSTest, etc. -
Jduv over 10 yearsAh interesting. I'll look into that and see if I can make that slightly easier to work with via ADT. Sorry that code's been slightly dead for a while now--we all have day jobs :).
-
Philip Daniels almost 10 years@Jduv Would upvote your comment about 100 times if I could. Your library helped me solve a seemingly unsolvable problem I was having with dynamic assembly loading under MSBuild. You should promote it to an answer!
-
Mick over 9 yearsYes it looks like complete nonsense, yet it has 28 up votes and is marked as the answer. The link supplied doesn't even mention MarshalByRefObject. Quite bizarre. If this actually does anything I'd love someone to explain how
-
Dennis Kassel about 9 yearsWhy do I have to load the assembly by using a proxy class? What is the difference compared to loading it using Assembly.LoadFrom(string). I am interested in the technical details, from the CLR's perspective. I would be very grateful if you could provide an answer.
-
Nir about 9 yearsYou use the proxy class in order to avoid the new assembly from being loaded in to your caller domain. If you'll use Assembly.LoadFrom(string), the caller domain will try to load the new assembly references and won't find them because it doesn't search for assemblies in the "[AsmPath]". (msdn.microsoft.com/en-us/library/yx7xezcf%28v=vs.110%29.aspx)
-
ArthurVard about 8 yearsIt works for me, I could load assembly from different folder and it works, before I use Load method it failed. if someone can explain how it exactly work and when we should not use this approach will be great.
-
Igor Bendrup over 7 years@Jduv are you sure that
assembly
variable will reference assembly from "MyDomain"? I think byvar assembly = value.GetAssembly(args[0]);
you will load yourargs[0]
into both domains andassembly
variable will reference copy from the main application domain -
Joon w K over 6 years@Jduv, I got error and the message I got is "Unable to cast transparent proxy to type "Proxy" when I applied the code in VSIX command handler. Could you let me know why I can not get same result as you have. Thanks in advance.
-
user2126375 about 6 yearsIt does if class is inherited from MarshalByRefObject. It does not if class is marked only with [Serializable] attribute.
-
abatishchev about 6 yearsHi, if I remember correctly, the core issue was to how to load all dependencies recursively, hence the question. Please test your code by changing HelloWorld to return a class of type
Foo, FooAssembly
which has a property of typeBar, BarAssembly
, i.e 3 assemblies total. Would it continue to work? -
SimperT about 6 yearsYes, need proper directory enumerated in the assembly probing stage. AppDomain has a ApplicationBase , however, I did not test it. Also config files you can specify assembly probing directories such as a app.config which a dll can use as well just set to copy in properties. Also, if you have control over the building of the assembly wishing to load in separate app domain, references can get a HintPath that specify were to look for it. If all that failed I would result to subscribing to the new AppDomains AssemblyResolve event and manually load the assemblies.Tons of example for that.
-
Ivandro Jao over 5 years@IgorBendrup you are right
assembly = value.GetAssembly(args[0])
will make suretype
where the return type assembly exits in both domain -
Owen Ivory about 5 yearsSome small typos in the code, and I have to admit I didn't believe it would work, but this was a life saver for me. Thanks a ton.