I'm sure the root cause of my problem is this similar question:
How do I pass references as method parameters across AppDomains?
and in particular this answer, but I'm not quite sure how to fix it.
The problem I have is this, I have a Visual Studio extension that I'm writing and one of the things it needs to be able to do is load an assembly into a separate domain (so I can dump it when I'm done with it) and do some stuff with it. So I have my domain set up like this:
// The actual ApplicationBase of the current domain will be the one of VS and not of my plugin
// We need our new AppDomain to be able to find our assemblies
var p = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var setup = new AppDomainSetup()
{
ApplicationBase = p
};
domain = AppDomain.CreateDomain("MyTest_AppDomain", AppDomain.CurrentDomain.Evidence, setup);
var t = typeof(Proxy);
proxy = domain.CreateInstanceAndUnwrap(t.Assembly.FullName,
t.FullName,
false,
BindingFlags.Default,
null,
new object[] { assemblyPath },
null,
null) as Proxy;
I needed to set the ApplicationBase for it to be able to load my class Proxy even though it's defined in the same assembly as the above code. Without that, I was getting FileNotFoundExceptions
Now my Proxy class looks something like this:
public class Proxy : MarshalByRefObject
{
private Assembly _assembly;
private IEnumerable<Type> _types;
public IEnumerable<Type> Types
{
get
{
if (_types == null && _assembly != null)
{
_spiders = _assembly.GetTypes().Where(t => typeof(IFoo).IsAssignableFrom(t)).ToList();
}
return _types;
}
}
public Proxy(string assemblyPath)
{
_assembly = Assembly.LoadFile(assemblyPath);
}
public IFoo GetInstanceOf(string fooType)
{
var type = Types.FirstOrDefault(t => t.FullName == fooType);
if (type == null)
{
throw new InvalidOperationException($"Unknown type {fooType} in assembly {_assembly.FullName}");
}
return GetInstanceOf(type);
}
private IFoo GetInstanceOf(Type fooType)
{
var obj = Activator.CreateInstance(fooType);
return obj as IFoo;
}
public override object InitializeLifetimeService()
{
return null;
}
}
Back in the same class that created the new AppDomain and the instance of Proxy in the new AppDomain I have something like this:
public IFoo GetInstanceOf(Type type)
{
var name = type.FullName;
var foo = proxy.GetInstanceOf(name);
return foo;
}
Initially I tried to call proxy.GetInstanceOf passing the type directly, but that wasn't working probably for the same reason as the code I have doesn't work. When I get to the line var foo = proxy.GetInstanceOf... line I get an ArgumentException with the message:
Object type cannot be converted to target type.
I believe the problem is because the IFoo in MyTest_AppDomain isn't considered to be the same IFoo as in DefaultDomain, but I'm not sure what the correct way to fix this is.
It may be relevant that IFoo itself is defined in a separate third assembly that both my VSIX project and the assembly I'm trying to load in a separate domain are referencing. I need to convince, I think, MyTest_AppDomain to load the same IFoo assembly from the same location as DefaultDomain, or else convince it that they are one and the same.
Note: All my classes that implement IFoo inherit from an abstract base FooBase which itself inherits from MarshalByRefObject.
Edit Thinking more about this my problem might be coming from here:
_assembly = Assembly.LoadFile(assemblyPath);
The assemblyPath there is a different path to the ApplicationBase and that path has it's own version of foo.dll (where IFoo is defined). So perhaps it is loading from there before loading from ApplicationBase? In Proxy.GetInstanceOf I can look at typeof(IFoo).Assembly.Location and it gives me the expected path that was set in ApplicationBase, but I don't think when my assembly loads it's getting foo.dll from there.