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.