0
    /// <summary>
    /// This function loads a list of assemblies based on their file paths.
    /// This function is intended to load the assemblies into another application domain than the main
    /// one so that the assemblies can be unloaded when the application domain is unloaded.
    /// 
    /// See below to understand how this function achieves loading assemblies into a domain other than
    /// the current one.
    /// 
    /// FIRST a new domain is created ---
    ///     - load the assembly into a child domain of the domain which will execute any code from a plugin.
    /// NEXT the current assembly is loaded into the child domain ---
    ///     - this is MUCH easier than dealing another assembly into a child domain
    ///     - this assembly owns the definition of this PluginLoader class which will take full advantage of
    /// NEXT get a PluginLoader instance from the child domain you just created ---
    ///     - since this instance is from the child domain anything you do with it will affect the child
    ///       domain and not the current one
    ///         - we want to load assemblies into the child domain so this is exactly what we want.
    /// NEXT load the plugin assemblies into the child domain
    ///     - we leverage Assembly.LoadFrom which is MUCH easier to make use of than AppDomain.Load
    ///         - Assembly.Load* is the preferred method of loading assemblies and this is made abundantly
    ///           clear in any attempt to use AppDomain.Load in a generic fashion.
    ///     
    /// CRITICAL KNOWLEDGE
    ///     - PluginLoader MUST derive from MarshalByRefObject or this will WILL NOT WORK.
    /// </summary>
    /// <param name="pathsToAssemblies"></param>
    /// <param name="plugInfo"></param>
    /// <returns></returns>
    static public bool LoadPluginAssemblies(string pluginName, List<string> pathsToAssemblies, out PluginInformation plugInfo)
    {
        try
        {
            string parentAssemblyName = Assembly.GetExecutingAssembly().FullName;

            #region Create child domain and setup tracking for it

            AppDomainSetup ads = new AppDomainSetup();

            ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            ads.PrivateBinPath = "Assemblies";
            //ads.PrivateBinPathProbe = "pluto_is_not_a_planet";

            plugInfo = new PluginInformation(pluginName, AppDomain.CreateDomain(pluginName,null,ads));

            #endregion //Create child domain and setup tracking for it

            #region Load executing assembly into child domain

            plugInfo.ChildDomain.Load(parentAssemblyName);

            #endregion //Load executing assembly into child domain

            #region Get PluginLoader from child domain

            PluginLoader plugLoader =
                (PluginLoader)plugInfo.ChildDomain.CreateInstanceAndUnwrap(parentAssemblyName, "DynamicAssemblyLoadTester.PluginLoader");

            #endregion //Get PluginLoader from child domain

            #region Load assemblies into child domain

            foreach (string assemblyPath in pathsToAssemblies)
            { plugInfo.m_assembliesInDomain.Add(plugLoader._loadAssemblyIntoChildDomain(assemblyPath)); }//end for

            #endregion //Load assemblies into child domain
        }
        catch (System.Exception ex)
        {
            Console.WriteLine("Plugin Load Failure: " + ex.Message);
            plugInfo = null;
            return false;
        }//end try-catch

        return true;
    }//end function

    private Assembly _loadAssemblyIntoChildDomain(string assemblyPath)
    {
        try
        {
            return Assembly.LoadFrom(assemblyPath);
        }
        catch (System.Exception ex)
        {
            Console.WriteLine("Failed to load assembly into child domain:  " + ex.Message);
            return null;
        }//end try-catch
    }//end function

I am trying to load some assemblies into a separate AppDomain so that I would be able to unload them later.

I posted comments with the first function in the hope it explains what I'm attempting to do. The second function is called from the child AppDomain with the hope that the assemblies will be loaded there.

This doesn't work.

Does casting the return of CreateInstanceAndUnWrap to the Type I need implicitly mean I need to load the assembly into both the parent and child domain? If so, I won't do that.

I have attempted to set the PrivateBinPath but when I attempt to load an assembly along that path I am suspecting it is never probed. I base this on what an exceptions tells me. An exception is thrown indicating the assembly cannot be found. The path mentioned in the exception is always the base directory with the name of my assembly appended to it.

I would enjoy learning both how to fix this and how what I did was wrong. I beg for enlightenment...seriously...I'm on my knees.

edit

I was incorrect about the exception message. It lists the path is say the assembly is in. That path resolves in a child directory of the base directory. My best theory is still that that I have not setup the probing for child directories of the base directory correctly.

  • 1
    What do you mean by *This doesn't work*. Do you get errors, Exceptions or are the assemblies simply not loaded into your new domain? You could try setting up a proxy object that - itself - does nothing else than loading assemblies in his object context. Notice, that the object that is crossing AppDomain boundaries (the one created by `CreateInstanceAndUnWrap`) needs to derive from `MarshalByRefObject` – ElGauchooo Mar 06 '14 at 15:31
  • PluginLoader does derive from MarshalByRefObject. This is the error I see. ... Could not load file or assembly 'file:///O:\HP_WORK2\I...\Assemblies\MyAssembly.dll' or one of its dependencies. The system cannot find the file specified. – S. Pritchett Mar 06 '14 at 15:48
  • The assembly, MyAssembly, will not load into the child domain. The exception is thrown in the second function. This is when I attempt to load the assembly into the child domain via the object that crossed the AppDomain boundary. All of the code belongs to PluginLoader. TPluginLoader is a reference to an object within the child domain. I cannot use PluginLoader to load assemblies into the child domain like I desire. – S. Pritchett Mar 06 '14 at 15:54
  • If no has a clue as to what is wrong. What would be the next best path to pursue? – S. Pritchett Mar 10 '14 at 15:27
  • Take a look here: http://stackoverflow.com/questions/1373100/how-to-add-folder-to-assembly-search-path-at-runtime-in-net – ElGauchooo Mar 13 '14 at 16:15
  • Thank you ElGauchooo. I've fixed my problems. Basically my plugin loader class needed to be in another assembly than the main application. This way I can place my plugin loader into a child domain, have it load an plugin assemblies and then unload the child domain with the plugin is no longer needed. Fyi - I use AppDomain.SetData/GetData to exchange information between domains. I use AppDomain.DoCallback to execute plugin code within the child domain exclusively. In short - I just needed to learn more. – S. Pritchett Mar 14 '14 at 22:31

1 Answers1

0

Despite my best efforts I cannot load an assembly from anywhere but the base directory. I will copy assemblies to the base directory, load them into a cache using shadow copy, and immediately delete them from my base directory. This is messy and has other issues but it's the best I think I can manage at the moment.