views:

373

answers:

2

I am implementing a simple plugin architecture in an application. The plugin requirements are defined using an interface (IPlugin) that is in a *.dll that is referenced by the application and the plugin. The application has a plugin manager (also in the same *.dll) which loads the plugins by looking for all the *.dll's in a plugins folder, loads them, then checks that the plugin implements the interface. I have done this checking two different ways [previously by a simple if (plugin is IPlugin)], but neither one will recognize when a plugin implements the interface. Here's the code:

Assembly pluginAssembly = Assembly.LoadFrom(currFile.FullName);
if (pluginAssembly != null)
{
    foreach (Type currType in pluginAssembly.GetTypes())
    {
        if (currType.GetInterfaces().Contains(typeof(IPlugin)))
        {
            // Code here is never executing
            // even when the currType derives from IPlugin
        }
    }                    
}

I used to test a particular class name ("Plugin"), but then I allowed it to cycle through all the classes in the assembly to no avail. (This follows an example that I found elsewhere.) To make this a little more complicated, there are two interfaces each of which implements the original interface (IPluginA, IPluginB). The plugin actually implements one of the more specific interfaces (IPluginB). However, I have tried it with the plugin just implementing the more general interface (IPlugin), and this still does not work.

[Edit: in response to the two responses that I first received] Yes, I have tried using IsAssignableFrom. See the following:

Assembly pluginAssembly = Assembly.LoadFrom(currFile.FullName);
if (pluginAssembly != null)
{
    foreach (Type currType in pluginAssembly.GetTypes())
    {
        if (typeof(IPlugin).IsAssignableFrom(currType))
        {
            string test = "test";
        }
    }
}
+4  A: 

Have you tried:

typeof(IPlugin).IsAssignableFrom(currType)

Also, types implement interfaces, but they do not derive from them. The BaseType property and IsSubclassOf method show derivation, where IsAssignableFrom shows derivation or implementation.

Edit: are your assemblies signed? They could be loading side-by-side versions of your assembly, and since Type objects are compared with ReferenceEquals, the same type in two side-by-side assemblies would be completely independent.

Edit 2: Try this:

public Type[] LoadPluginsInAssembly(Assembly otherAssembly)
{
    List<Type> pluginTypes = new List<Type>();
    foreach (Type type in otherAssembly.GetTypes())
    {
        // This is just a diagnostic. IsAssignableFrom is what you'll use once
        // you find the problem.
        Type otherInterfaceType =
            type.GetInterfaces()
            .Where(interfaceType => interfaceType.Name.Equals(typeof(IPlugin).Name, StringComparison.Ordinal)).FirstOrDefault();

        if (otherInterfaceType != null)
        {
            if (otherInterfaceType == typeof(IPlugin))
            {
                pluginTypes.Add(type);
            }
            else
            {
                Console.WriteLine("Duplicate IPlugin types found:");
                Console.WriteLine("  " + typeof(IPlugin).AssemblyQualifiedName);
                Console.WriteLine("  " + otherInterfaceType.AssemblyQualifiedName);
            }
        }
    }

    if (pluginTypes.Count == 0)
        return Type.EmptyTypes;

    return pluginTypes.ToArray();
}
280Z28
I have tried using IsAssignableFrom, but it isn't working either. See my edit in the original question.
Tim
I don't think assemblies are signed (GAC). OP says, he is trying to load it from plugins folder.
shahkalpesh
They are not signed (not in the GAC).
Tim
What? I didn't say GAC at all. I sign my assemblies but no way do I put them in the GAC.
280Z28
Please interpret as "(and not in the GAC)." Would signing them help?
Tim
The only other possibility is you have an `IPlugin` interface or type located in another namespace and/or assembly. See my Edit #2 (coming in a sec) for some help.
280Z28
I added your code in and replaced the check (for implementation of the interface) with a call to your method. When I run the app, it outputs the same thing twice (i.e. the same namespace). I will post a longer code sample with all three files (it SO will let me) tonight.
Tim
@Tim: Try printing the `otherInterfaceType.Assembly.Location` as well.
280Z28
The plugin outputs a copy of the *.dll containing the interface into the plugins folder. I can delete the copy in the plugins folder, and run the app, and it seems to work fine. How do I prevent it from putting a copy in the plugins folder? I can delete using a post build event, but is there another way?Thanks for all the help!!
Tim
Nevermind, just figured it out. For others: right-click on the reference and change Copy-Local to false.
Tim
@Tim: Under the References folder for the *Plugin* project, locate the reference to your primary project. Then press F4 or right click > Properties and find the Copy Local option. Set that to false. :)
280Z28
+2  A: 

The method IsAssignableFrom is what you are looking for:

Type intType = typeof(IInterface);
foreach (Type t in pluginAssembly.GetTypes())
{
    if (intType.IsAssignableFrom(t))
    {
    }
}
Francis B.
I have tried using IsAssignableFrom, but it isn't working either. See my edit in the original question.
Tim