AppDomain dependencies across directories
- by strager
I am creating a plugin system and I am creating one AppDomain per plugin. Each plugin has its own directory with its main assemblies and references. The main assemblies will be loaded by my plugin loader, in addition to my interface assemblies (so the plugin can interact with the application).
Creating the AppDomain:
this.appDomain = AppDomain.CreateDomain("AppDomain", null, new AppDomainSetup
{
ApplicationBase = pluginPath,
PrivateBinPath = pluginPath,
});
Loading the assemblies:
this.appDomain.Load(myInterfaceAssembly.GetName(true));
var assemblies = new List<Assembly>();
foreach (var assemblyName in this.assemblyNames)
{
assemblies.Add(this.appDomain.Load(assemblyName));
}
The format of assemblyName is the assembly's filename without ".dll".
The problem is that AppDomain.Load(assemblyName) throws an exception:
Could not load file or assembly '[[assemblyName]], Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
All of the dependencies of [[assemblyName]] are:
Inside the directory pluginPath,
The myInterfaceAssembly which is already loaded, or
In the GAC (e.g. mscorelib).
Clearly I'm not doing something right. I have tried:
Creating an object using this.appDomain.CreateInstanceAndUnwrap inheriting from MarshalByRefObject with a LoadAssembly method to load the assembly. I get an exception saying that the current assembly (containing the proxy class) could not be loaded (file not found, as above), even if I manually call this.appDomain.Load(Assembly.GetExecutingAssembly().GetName(true)).
Attaching an AssemblyResolve handler to this.appDomain. I'm met with the same exception as in (1), and manually loading doesn't help.
Recursively loading assemblies by loading their dependencies into this.appDomain first. This doesn't work, but I doubt my code is correct:
private static void LoadAssemblyInto(AssemblyName assemblyName, AppDomain appDomain)
{
var assembly = Assembly.Load(assemblyName);
foreach (var referenceName in assembly.GetReferencedAssemblies())
{
if (!referenceName.FullName.StartsWith("MyProject"))
{
continue;
}
var loadedAssemblies = appDomain.GetAssemblies();
if (loadedAssemblies.Any((asm) => asm.FullName == referenceName.FullName))
{
continue;
}
LoadAssemblyInto(referenceName, appDomain);
}
appDomain.Load(assembly.GetName(true));
}
How can I load my plugin assembly with its dependencies in that plugin's directory while also loading some assemblies in the current directory?
Note: The assemblies a plugin may (probably will) reference are already loaded in the current domain. This can be shared across domains (performance benefit? simplicity?) if required.
Fusion log:
*** Assembly Binder Log Entry (12/24/2010 @ 10:46:40 AM) ***
The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.
Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable C:\MyProject\bin\Debug\MyProject.vshost.exe
--- A detailed error log follows.
LOG: Start binding of native image vshost32, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=x86.
WRN: No matching native image found.
LOG: Bind to native image assembly did not succeed. Use IL image.
LOG: IL assembly loaded from C:\MyProject\bin\Debug\MyProject.vshost.exe