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";
        }
    }
}
Tim
  • 1,621
  • 4
  • 19
  • 35
  • A larger code sample would help understand the actual problem. I am pretty sure IsAssignableFrom is working for me, as I write this, in my app - exactly as expected. – Charles Prakash Dasari Aug 13 '09 at 02:06
  • Please check whether the currFile is the DLL containing plugins & also see what currType is for each of the types in the plugin assembly. – shahkalpesh Aug 13 '09 at 02:07
  • The currFile *is* the *.dll containing the plugin, and the currType is (the only type in the assembly), which is a Plugin which implements IPlugin. In other words, those are correct. Any other ideas? – Tim Aug 13 '09 at 02:13
  • Should it not be: if(currType.IsAssignableFrom(typeof(IPlugin)) You are testing if the type of IPlugin is assignable from the type of your plugin. You should be testing if the type of your plugin is assignable from IPlugin? – Kristoffer L Apr 13 '10 at 10:49
  • Kristoffer, The code in 280Z28's reply is correct. I believe the way that you should think of it is: can I assign an istnace of the type (that is the argument to the method) to an instance of the type before the .IsAssignableFrom. The MSDN article states this several different ways (probably more technically correct than I did): http://msdn.microsoft.com/en-us/library/system.type.isassignablefrom.aspx – Tim Apr 13 '10 at 17:37

2 Answers2

5

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();
}
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • I have tried using IsAssignableFrom, but it isn't working either. See my edit in the original question. – Tim Aug 13 '09 at 01:57
  • I don't think assemblies are signed (GAC). OP says, he is trying to load it from plugins folder. – shahkalpesh Aug 13 '09 at 02:14
  • They are not signed (not in the GAC). – Tim Aug 13 '09 at 02:17
  • What? I didn't say GAC at all. I sign my assemblies but no way do I put them in the GAC. – Sam Harwell Aug 13 '09 at 02:20
  • Please interpret as "(and not in the GAC)." Would signing them help? – Tim Aug 13 '09 at 02:26
  • 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. – Sam Harwell Aug 13 '09 at 02:33
  • 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 Aug 14 '09 at 18:06
  • @Tim: Try printing the `otherInterfaceType.Assembly.Location` as well. – Sam Harwell Aug 14 '09 at 18:56
  • 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 Aug 14 '09 at 20:50
  • 1
    Nevermind, just figured it out. For others: right-click on the reference and change Copy-Local to false. – Tim Aug 14 '09 at 20:56
  • @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. :) – Sam Harwell Aug 14 '09 at 20:57
2

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.
  • 7,018
  • 2
  • 32
  • 54
  • I have tried using IsAssignableFrom, but it isn't working either. See my edit in the original question. – Tim Aug 13 '09 at 01:58