0

I want to make Plugins Application For Plugins I use Interface IPlugin that is located in CorePlugIn dll and has such code :

using System;
namespace CorePlugIn
{
    public interface IPlugIn
    {
        Int64? Id { get; }
        Int64? PId { get; }
        Int64 Type { get; }
        string Name { get; }
        string Author { get; }
        string ChildFolderPath { get; }
        string Version { get; }
        bool IsForMainMenu { get; }
        EMainMenuGroup GroupName { get; }
        IPluginHost Host { get; set; }
        Image CustomImage { get; }
        XtraUserControl ShowFrame(CoreFormParams param, CoreDbCommon dbWorker, TUsers user);
        bool SaveChanges(bool isClosing = false);
        bool HasChanges();
    }
    public interface IPluginHost
    {
        bool Register(IPlugIn plug);
    }
}  

Plugins that I have in my application All has AisCorePlugIn that is inherit interface IPlugin

using System;

using CorePlugIn;

namespace UcAccount 
{   
    public class AisCorePlugIn : IPlugIn
    {
        private IPluginHost _host;
        private UcMain _frame;
        public Int64? Id { get; } = (Int64) EPlugins.UcAccountMember;
        public Int64? PId { get; } = null;
        public Int64 Type { get; } = (Int64)EPluginType.MainFormPlugin;
        public string Name { get; } = "Account";
        public string Author { get; } = SystemConst.AisProAdminLogin;
        public string ChildFolderPath { get; } = null;
        public string Version { get; } = "1.0";
        public bool IsForMainMenu { get; } = true;
        public EMainMenuGroup GroupName { get; } = EMainMenuGroup.Main;
        public Image CustomImage { get; } = Properties.Resources.Logo;
        public XtraUserControl ShowFrame(CoreFormParams iFormParams, CoreDbCommon dbWorker, TUsers user)
        {
            _frame = new UcMain(iFormParams, dbWorker, user);
            return _frame;
        }

        public bool SaveChanges(bool isClosing)
        {
            return _frame.SaveChanges(isClosing);
        }

        public bool HasChanges()
        {
            return _frame.HasChanges();
        }

        public IPluginHost Host
        {
            get => _host;
            set
            {
                _host = value;
                _host.Register(this);
            }
        }
    }
}

All buids correctly. Now I have Main Application where I want to get List Plugins in plugin folder. For this I use class CorePluginWorker in CorePlugIn dll with code

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using CoreEnums;

namespace CorePlugIn
{
   public static class CorePluginWorker
   {
       public static List<IPlugIn> GetPlugins(string path, IPluginHost form)
       {
           List<IPlugIn> list = new List<IPlugIn>();
           string[] pluginFiles = Directory.GetFiles(path, "*.dll");
           foreach (string t in pluginFiles)
           {
               string args = t.Substring(
                   t.LastIndexOf("\\", StringComparison.Ordinal) + 1,
                   t.IndexOf(".dll", StringComparison.Ordinal) -
                   t.LastIndexOf("\\", StringComparison.Ordinal) - 1);

               Type objType;
               try
               {
                   var ass = Assembly.LoadFrom(t);
                   objType = ass.GetType(args + ".AisCorePlugIn");
               }
               catch
               {
                   return null;
               }

               try
               {
                   if (objType != null)
                   {
                       var item = Activator.CreateInstance(objType);
                       list.Add((IPlugIn)item);
                       list[list.Count - 1].Host = form;
                   }
               }
               catch (InvalidOperationException ex)
               {
                   return null;
               }
           }

           return list;
       }
   }
}

and on list.Add((IPlugIn)item) I got invalid cast exception. why?

1 Answers1

1

Pay attention to the documentation of Assembly.LoadFrom. It mentions a number of disadvantages this method has. Among the disadvantages:

An assembly can be loaded in the load-from context even though an assembly with the same identity exists in the load context. Interoperability between the two assemblies will not work, leading to errors such as InvalidCastException, MissingMethodException, or other unexpected behavior.

While the plugin assembly itself has not been loaded prior to executing Assembly.LoadFrom, the CorePlugIn assembly has (CorePlugIn being the assembly providing the IPlugIn type you have troubles with). So, probably the CorePlugIn assembly is present in the "load context" (because your app has a dependency on it) as well as being present in the "load-from context" after Assembly.LoadFrom has been executed (due to the plugin itself having a dependency on CorePlugIn).

If this is the cause of your problem, you should be able to make Assembly.LoadFrom load the plugin assembly (and its dependencies) into the "load context" (and thus avoid this problem) by adding the plugin directory to your applications config using the <probing> config element. This, however, requires the plugin directory to be a sub directory of your app directory.

(Also, this answer has some info relating to how to add <probing> to your app's config. While i don't know if this still applies to the latest .NET version, it's probably useful to know in case the <probing> element does not seem to have any effect.)