Use AppDomain to load/unload external assemblies

后端 未结 8 2024
遇见更好的自我
遇见更好的自我 2020-12-24 08:39

My scenario is as follows:

  • Create new AppDomain
  • Load some assemblies into it
  • Do some magic with loaded dlls
  • Unload AppDomain to rele
相关标签:
8条回答
  • 2020-12-24 09:13

    This is a late answer but would be worthwhile to have it here for any future views to this question. I needed to implement something similar to this but in a dynamic code compilation/execution fashion. The best would be executing all methods in a separate domain, i.e.: remote domain, other than your main AppDomain, otherwise the app memory will always increase and increase. You can solve this problem via remote interfaces and proxies. So you would expose your methods through an interface which you will get an instance of in your main AppDomain and then remotely execute those methods in the remote domain, unload the newly created domain (remote domain), nullify it and then force the GC to collect unused objects. I spent quite a long time debugging my code until I reliazed that I have to force the GC to do so and it works just fine. Bulk of my implementation is taken from: http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm.

          //pseudo code
          object ExecuteCodeDynamically(string code)
           {
            Create AppDomain my_app
             src_code = "using System; using System.Reflection; using RemoteLoader;
            namespace MyNameSpace{
           public class MyClass:MarshalByRefObject, IRemoteIterface
          {
          public object Invoke(string local_method, object[] parameters)
            {
           return this.GetType().InvokeMember(local_method, BindingFlags.InvokeMethod, null, this,    parameters);
         }
         public object ExecuteDynamicCode(params object[] parameters)
         {
        " + code + } } } ";// this whole big string is the remote application
    
         //compile this code which is src_code
         //output it as a DLL on the disk rather than in memory with the name e.g.: DynamicHelper.dll. This can be done by playing with the CompileParameters
         // create the factory class in the secondary app-domain
                   RemoteLoader.RemoteLoaderFactory factory =
                      (RemoteLoader.RemoteLoaderFactory)loAppDomain.CreateInstance("RemoteLoader",
                      "RemoteLoader.RemoteLoaderFactory").Unwrap();
    
                // with the help of this factory, we can now create a real instance
                object loObject = factory.CreateInstance("DynamicHelper.dll", "MyNamespace.MyClass", null);
    
                // *** Cast the object to the remote interface to avoid loading type info
                RemoteLoader.IRemoteInterface loRemote = (RemoteLoader.IRemoteInterface)loObject;
    
                if (loObject == null)
                {
                    System.Windows.Forms.MessageBox.Show("Couldn't load class.");
                    return null;
                }
    
                object[] loCodeParms = new object[1];
                loCodeParms[0] = "bla bla bla";
    
                try
                {
                    // *** Indirectly call the remote interface
                    object result = loRemote.Invoke("ExecuteDynamicCode", loCodeParms);// this is the object to return                
    
                }
                catch (Exception loError)
                {
                    System.Windows.Forms.MessageBox.Show(loError.Message, "Compiler Demo",
                        System.Windows.Forms.MessageBoxButtons.OK,
                        System.Windows.Forms.MessageBoxIcon.Information);
                    return null;
                }
    
                loRemote = null;
                try { AppDomain.Unload(my_app); }
                catch (CannotUnloadAppDomainException ex)
                { String str = ex.Message; }
                loAppDomain = null;
                GC.Collect();//this will do the trick and free the memory
                GC.WaitForPendingFinalizers();
                System.IO.File.Delete("ConductorDynamicHelper.dll");
                return result;
    
    }
    

    Note that RemoteLoader is another DLL that should be already created and added to both you main App and your remote App. It's basically an interface and a factory loader. The following code is taken from the above website:

          /// <summary>
        /// Interface that can be run over the remote AppDomain boundary.
       /// </summary>
         public interface IRemoteInterface
         {
        object Invoke(string lcMethod,object[] Parameters);
         }
    
    
         naemspace RemoteLoader{
       /// <summary>
       /// Factory class to create objects exposing IRemoteInterface
      /// </summary>
      public class RemoteLoaderFactory : MarshalByRefObject
     {
       private const BindingFlags bfi = BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance;
    
     public RemoteLoaderFactory() {}
    
     /// <summary> Factory method to create an instance of the type whose name is specified,
      /// using the named assembly file and the constructor that best matches the specified parameters.  </summary>
     /// <param name="assemblyFile"> The name of a file that contains an assembly where the type named typeName is sought. </param>
     /// <param name="typeName"> The name of the preferred type. </param>
     /// <param name="constructArgs"> An array of arguments that match in number, order, and type the parameters of the constructor to invoke, or null for default constructor. </param>
     /// <returns> The return value is the created object represented as ILiveInterface. </returns>
     public IRemoteInterface Create( string assemblyFile, string typeName, object[] constructArgs )
     {
      return (IRemoteInterface) Activator.CreateInstanceFrom(
      assemblyFile, typeName, false, bfi, null, constructArgs,
      null, null, null ).Unwrap();
       }
      }
      }
    

    Hope this makes sense and helps...

    0 讨论(0)
  • 2020-12-24 09:14

    Actually, combination of above answers pointed me to (I hope) correct answer: My code is now as follows:

    AppDomain newDomain = AppDomain.CreateDomain("newDomain", e, setup);
    string fullName = Assembly.GetExecutingAssembly().FullName;
    Type loaderType = typeof(AssemblyLoader);
    FileStream fs = new FileStream(@"library.dll", FileMode.Open);
    byte[] buffer = new byte[(int)fs.Length];
    fs.Read(buffer, 0, buffer.Length);
    fs.Close();
    
    Assembly domainLoaded = newDomain.Load(buffer);
    object loaded = Activator.CreateInstance(domainLoaded.GetTypes()[1]);
    AppDomain.Unload(newDomain);
    GC.Collect();
    GC.WaitForPendingFinalizers();
    

    I can't use AppDomain.CreateInstance, since it requires Assembly.FullName which I don't know - library is loaded dynamically.

    Thanks for the help, Bolek.

    0 讨论(0)
  • 2020-12-24 09:17

    you can try this code: http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm

    0 讨论(0)
  • 2020-12-24 09:24

    .Net uses non-deterministic finalization. If you want to see if the memory drops you should do ...

    GC.Collect(); 
    GC.WaitForPendingFinalizers();
    

    ... after the unload. Also unless you have a need to force collection (rather un-likely) you should allow the system to collect on its own. Normally if you feel the need to force collection in production code there is a resource leak typically caused by not calling Dispose on IDisposable objects or for not Releasing unmanaged objects

    using (var imdisposable = new IDisposable())
    {
    }
    //
    var imdisposable = new IDisposable();
    imdisposable.Dispose();
    //
    Marshal.Release(intPtr); 
    //
    Marshal.ReleaseComObject(comObject);
    
    0 讨论(0)
  • 2020-12-24 09:28

    This is not a complete answer: I just noticed that you use a string as payload. Strings are not useful for this, as literal strings are interned. The interned strings are shared among AppDomains, so that part is not unloaded when you unload your AppDomain. Try using a byte[] instead.

    0 讨论(0)
  • 2020-12-24 09:30

    I have posted an example where 3 different assemblies are loaded in different app domains and unloaded successfully. Here is the link http://www.softwareinteractions.com/blog/2010/2/7/loading-and-unloading-net-assemblies.html

    0 讨论(0)
提交回复
热议问题