Loading DLLs into a separate AppDomain with known

2019-04-12 12:34发布

问题:

I need to load .dll(plugins) in another domain. In main app I don't know anything about plugins types, only that they implement common interface ICommonInterface with some methods. So this code wouldn't help, because I can't create an instance with interface type.

AppDomain domain = AppDomain.CreateDomain("New domain name");
//Do other things to the domain like set the security policy

string pathToDll = @"C:\myDll.dll"; //Full path to dll you want to load
Type t = typeof(TypeIWantToLoad);
TypeIWantToLoad myObject = (TypeIWantToLoad)domain.CreateInstanceFromAndUnwrap(pathToDll, t.FullName);

My question is how I can load assembly in new domain and get the instance, if I know only interface name which implements type I want to create.

UPDATE: Here is my code: MainLib.dll

namespace MainLib
{
public interface ICommonInterface
{
    void ShowDllName();
}
}

PluginWithOutException.dll

namespace PluginWithOutException
{
public class WithOutException : MarshalByRefObject, ICommonInterface
{
    public void ShowDllName()
    {
        Console.WriteLine("PluginWithOutException");
    }
}
}

PluginWithException.dll

namespace PluginWithException
{
public class WithException : MarshalByRefObject, ICommonInterface
{
    public void ShowDllName()
    {
        Console.WriteLine("WithException");
        throw new NotImplementedException();
    }
}
}

And main application:

        static void Main(string[] args)
    {
        string path = @"E:\Plugins\";
        string[] assemblies = Directory.GetFiles(path);

        List<string> plugins = SearchPlugins(assemblies);

        foreach (string item in plugins)
        {
            CreateDomainAndLoadAssebly(item);
        }

        Console.ReadKey();
    }

    public static List<string> SearchPlugins(string[] names)
    {
        AppDomain domain = AppDomain.CreateDomain("tmpDomain");
        domain.Load(Assembly.LoadFrom(@"E:\Plugins\MainLib.dll").FullName);
        List<string> plugins = new List<string>();

        foreach (string asm in names)
        {
            Assembly loadedAssembly = domain.Load(Assembly.LoadFrom(asm).FullName);

            var theClassTypes = from t in loadedAssembly.GetTypes()
                                where t.IsClass &&
                                      (t.GetInterface("ICommonInterface") != null)
                                select t;
            if (theClassTypes.Count() > 0)
            {
                plugins.Add(asm);
            }
        }
        AppDomain.Unload(domain);
        return plugins;
    }

Plugins and main app have reference to MainLib.dll. The main aim is to not to load assemblies in default domain, but load them to another domains, so when I don't need them, I just Unload() domain and unload all plugins from application.

For now the exception is FileNotFoundException, Could not load file or assembly 'PluginWithException, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.) on string Assembly loadedAssembly = domain.Load(Assembly.LoadFrom(asm).FullName);(I trying to load plugin with name PluginWithException), I've delete all the dependencies in plugins, exept System, I loaded System.dll in this domain(it loaded correct and it is in domain), but still cant load plugins into domain. Also I checked, that PluginWithException has 2 dependencies - mscorlib and MainLib, and all of them loaded to this domain.

UPDATE: Here I asked this question with more details.

回答1:

This question seems relevant to what you want to do.

How to Load an Assembly to AppDomain with all references recursively?

After you've loaded the assembly, you can use Assembly.GetTypes() and iterate to find the types that implement your interface.



回答2:

I'm not sure if it's what you need, i'd try to help you with this. This is how I do to load plugin assemblies. I use a helper class to manage new AppDomain and the instance of the class on that assembly. This is the helper class:

[Serializable, ClassInterface(ClassInterfaceType.AutoDual)]
class helperDomain<T>: MarshalByRefObject where T: class
{
    #region private
    private AppDomain _app_domain;
    private AppDomainSetup _app_domain_info;

    private string _assembly_class_name;
    private string _assembly_file;
    private string _assembly_file_name;
    private T _inner_class;
    private bool _load_ok;
    private string _loading_errors;
    private string _path;
    #endregion

    #region .ctor
    public helperDomain(string AssemblyFile, 
       string configFile = null, string domainName)
    {
        this._load_ok = false;
        try
        {
            this._assembly_file = AssemblyFile; //full path to assembly
            this._assembly_file_name = System.IO.Path.GetFileName(this._assembly_file); //assmbly file name
            this._path = System.IO.Path.GetDirectoryName(this._assembly_file); //get root directory from assembly path 
            this._assembly_class_name = typeof(T).ToString(); //the class name to instantiate in the domain from the assembly
            //start to configure domain
            this._app_domain_info = new AppDomainSetup();
            this._app_domain_info.ApplicationBase = this._path;
            this._app_domain_info.PrivateBinPath = this._path;
            this._app_domain_info.PrivateBinPathProbe = this._path;
            if (!string.IsNullOrEmpty(configFile))
            {
                this._app_domain_info.ConfigurationFile = configFile;
            }
            //lets create the domain
            this._app_domain = AppDomain.CreateDomain(domainName, null, this._app_domain_info);
            //instantiate the class
            this._inner_class = (T) this._app_domain.CreateInstanceFromAndUnwrap(this._assembly_file, this._assembly_class_name);
            this._load_ok = true;
        }
        catch (Exception exception)
        {
            //There was a problema setting up the new appDomain
            this._load_ok = false;
            this._loading_errors = exception.ToString();
        }
    }
    #endregion


    #region public properties
    public string AssemblyFile
    {
        get
        {
            return _assembly_file;
        }
    }

    public string AssemblyFileName
    {
        get
        {
            return _assembly_file_name;
        }
    }

    public AppDomain AtomicAppDomain
    {
        get
        {
            return _app_domain;
        }
    }

    public T InstancedObject
    {
        get
        {
            return _inner_class;
        }
    }

    public string LoadingErrors
    {
        get
        {
            return _loading_errors;
        }
    }

    public bool LoadOK
    {
        get
        {
            return _load_ok;
        }
    }

    public string Path
    {
        get
        {
            return _path;
        }
    }
    #endregion
}

and then load plugins (each in a diferent folder).

foreach(string pluginassemblypath in pluginspaths)
{ 
    //Each pluginassemblypath (as it says..) is the full path to the assembly
    helperDomain<IPluginClass> isoDomain = 
        helperDomain<IPluginClass>(pluginassemblypath, 
             pluginassemblypath + ".config", 
             System.IO.Path.GetFileName(pluginassemblypath) + ".domain");
    if (isoDomain.LoadOK)
    {
       //We can access instance of the class (.InstancedObject)
       Console.WriteLine("Plugin loaded..." + isoDomain.InstancedObject.GetType().Name);
    }
    else
    {
       //Something happened...
       Console.WriteLine("There was en error loading plugin " + 
            pluginassemblypath + " - " + helperDomain.LoadingErrors);
    }
}

Hope it will helps you...