I am building a WPF tool which will analyze via reflection the assemblies of a target application.
I have so far been using Assembly.Load
etc. to load the target assemblies. This is OK except that it has a couple of limitations: I want to be able to rebuild the target application and "refresh" the tool to re-analyze the newly-built assemblies. This won't currently work because the assemblies are locked upon load and not released until the tool exits. This also precludes re-loading newly built assemblies.
I believe I can create a temporary AppDomain, load the assemblies into it, do the reflection I want to do, and then unload the domain.
The problem I am having is that I can't get it to work. I've tried numerous variations and get results such as:
- Loading into the current application domain, not the one I explicitly created
- Error loading the requested assembly
- Error loading the tool's assembly(?)
For example, following the suggestion here: Create custom AppDomain and add assemblies to it
I created a SimpleAssemblyLoader
thus:
public class SimpleAssemblyLoader : MarshalByRefObject
{
public Assembly Load(string path)
{
ValidatePath(path);
return Assembly.Load(path);
}
public Assembly LoadFrom(string path)
{
ValidatePath(path);
return Assembly.LoadFrom(path);
}
public Assembly UnsafeLoadFrom(string path)
{
ValidatePath(path);
return Assembly.UnsafeLoadFrom(path);
}
private void ValidatePath(string path)
{
if (path == null) throw new ArgumentNullException(nameof(path));
if (!System.IO.File.Exists(path))
throw new ArgumentException($"path \"{path}\" does not exist");
}
}
... and use it in a calling method thus:
private static AppDomain MakeDomain(string name, string targetPath, string toolPath)
{
var appDomain =
AppDomain.CreateDomain(name, AppDomain.CurrentDomain.Evidence, new AppDomainSetup
{
ApplicationBase = targetPath,
PrivateBinPath = toolPath,
LoaderOptimization = LoaderOptimization.MultiDomainHost
},
new PermissionSet(PermissionState.Unrestricted));
return appDomain;
}
/// <summary>
///
/// </summary>
/// <param name="targetPath">Location of assemblies to analyze</param>
/// <param name="toolPath">Location of this tool</param>
/// <param name="file">Filename of assembly to analyze</param>
/// <returns></returns>
public string[] Test(string targetPath, string toolPath, string file)
{
var dom = MakeDomain("TestDomain", targetPath, toolPath);
var assemblyLoader = (SimpleAssemblyLoader)dom.CreateInstanceAndUnwrap(typeof(SimpleAssemblyLoader).Assembly.FullName, typeof(SimpleAssemblyLoader).FullName);
var path = Path.Combine(targetPath, file);
var assembly = assemblyLoader.LoadFrom(path);
var types = assembly.GetTypes();
List<string> methods = new List<string>();
foreach (var type in types)
{
foreach (var method in type.GetMethods(BindingFlags.Instance|BindingFlags.Public))
{
methods.Add(method.Name);
}
}
AppDomain.Unload(dom);
return methods.ToArray();
}
... the tool application launches, but it fails to instantiate the SimpleAssemblyLoader, reporting a "File not found" exception associated with the tool's assembly - apparently trying to load the tool's assembly into the new domain(?).
What am I doing wrong, and how do I fix it?