Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type.IsSubclassOf() doesn't work across AppDomains?

I'm having some problems with the following code:

private class ClientPluginLoader : MarshalByRefObject
{
    public bool IsPluginAssembly(string filename)
    {
        AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += new ResolveEventHandler(CurrentDomainReflectionOnlyAssemblyResolve);

        Assembly asm = Assembly.ReflectionOnlyLoadFrom(filename);

        Type[] types = asm.GetTypes();
        foreach (Type type in types)
        {
            if (type.IsSubclassOf(typeof(ClientPlugin)))
            {
                return true;
            }
        }

        return false;
    }
}

The code is called via a proxy that I've created through my custom app domain's CreateInstanceFromAndUnwrap(). This means that IsPluginAssembly() is executed in the context of my custom app domain.

The problem is that the call to IsSubclassOf() always returns false, even though it should IMHO return true. The "type" in question really does inherit from ClientPlugin - there's no doubt about that.

ClientPlugin is defined in a different private assembly, which I'm resolving manually, as evident in the code fragment above.

I've put a breakpoint on the if (type.IsSubclassOf(...)) line and confirmed this expression to be false:

type.BaseType == typeof(ClientPlugin)

On the other hand, this expression is true:

type.BaseType.FullName == typeof(ClientPlugin).FullName

How is this possible? What's going on?

UPDATE: Kent Boogaart pointed me to the right direction. I searched the web a bit more and run into this blog post. It seems I'll have to resolve my Load/LoadFrom/ReflectionOnlyLoadFrom conflicts in order to make this work.

like image 971
aoven Avatar asked Jan 21 '09 16:01

aoven


2 Answers

This is due to loading into a different context. How you load an assembly (Load / LoadFrom / ReflectionOnlyLoad) determines which context it is loaded into. This simple example also demonstrates the problem:

using System;
using System.Reflection;

class Foo
{
    public static void Main()
    {
        var type = typeof(Foo);
        var reflectionLoadType = Assembly.ReflectionOnlyLoad("ConsoleApplication1").GetType("Foo");
        Console.WriteLine(type == reflectionLoadType);  //false
        Console.WriteLine(type.Equals(reflectionLoadType));  //false

        Console.WriteLine("DONE");
        Console.ReadKey();
    }
}

See here for more info.

like image 59
Kent Boogaart Avatar answered Nov 16 '22 08:11

Kent Boogaart


I've had a similar problem. I also had this architecture - a .DLL that contains ClientPlugin base class; several plugins which reference this .DLL; and a main application, which also references this .DLL. The problem was that the .DLL with the ClientPlugin base class was copied in two folders - both the Plugins folder, and the main application folder. Thus it got loaded twice in my AppDomain (plugins also loaded it indirectly). And when the main application tried to do reflection type magic, it failed, because there were two instances of the ClientPlugin type.

Although I don't think this is exactly your case, there still is a lesson to learn here - if a .DLL gets loaded twice, the types will also be duplicated. In your case I would suspect either separate AppDomains, or the "ReflectionOnlyLoad", because the .DLL is then loaded somehow differently.

like image 28
Vilx- Avatar answered Nov 16 '22 10:11

Vilx-