none
Loading and unloading assembly using appdomain with marshalbyrefobject

    Question

  • Hello,

    I'm trying to load a dll from a class that inherits MarshalByRefObject because I want to release the reference to this assembly so that I can delete it without restarting the program. What I want to happen is that I want to show the form that I created in the dll. I created a StartHere class that will call the actual form. So for me to be able to show the form, I need to call this class when the dll is loaded. 

        public class StartHere
        {
            public StartHere(string passkey)
            {
                if (passkey.Equals("pass"))
                {
                    SLAllOpeningBalancesForm report = new SLAllOpeningBalancesForm();
                    report.Show();
                }
            }
        }

    This is how I load the the dll

    	    FileInfo fileInfo = new FileInfo(dllName);
                SeperateAppDomainAssemblyLoader appDomainAssemblyLoader = new SeperateAppDomainAssemblyLoader();
                appDomainAssemblyLoader.LoadAssemblies( fileInfo, dllName, process, passkey );

    My problem here is that when the dll is loaded, the form is not showing. I believe the problem is that it's not callingl the StartHere class when the dll is loaded. Can somebody help me on how to call the StartHere class with my current setup? I've tried a lot changes already but I can't make it work.

    The code below is my previous working code. It can actually load the form but by using this, I can't delete the dll file even if it's unloaded already. For this reason, I changed my approach to the one above.

    AppDomainSetup appDomainSetup = new AppDomainSetup() { PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory };

    Evidence evidence = AppDomain.CurrentDomain.Evidence;

    AppDomain appDomain = AppDomain.CreateDomain("MigrationAppDomain", evidence, appDomainSetup);

    Assembly assembly = appDomain.Load(dllName.Replace(".dll", ""));

    Type type = assembly.GetType(dllName.Replace(".dll", ".") + process); // process = StartHere

    MethodInfo methodInfo = type.GetMethod(process);

    object[] consargs = new object[1];

    consargs[0] = passkey;

    object foo = Activator.CreateInstance(type, consargs);



    Here is the  SeperateAppDomainAssemblyLoader class:

     public class SeperateAppDomainAssemblyLoader
        {
            #region Public Methods
            /// 
            /// Loads an assembly into a new AppDomain and obtains all the
            /// namespaces in the loaded Assembly, which are returned as a 
            /// List. The new AppDomain is then Unloaded
            /// 
            /// The Assembly file 
            /// location
            /// A list of found namespaces
            public void LoadAssemblies(FileInfo assemblyLocations, string dllName, string process, string passkey)
            {
              
                crmCORE oCoreClasses = new crmCORE();
    
                AppDomain childDomain = BuildChildDomain(AppDomain.CurrentDomain);
                try
                {
                    Type loaderType = typeof(AssemblyLoader);
                    if (loaderType.Assembly != null)
                    {
                        
    
                        var loader = (AssemblyLoader)childDomain.CreateInstanceFrom(loaderType.Assembly.Location, loaderType.FullName ).Unwrap();
                        
                        loader.LoadAssemblies(assemblyLocations, dllName, process, passkey);
    
                    }             
                }
    
                finally
                {
    
                    AppDomain.Unload(childDomain);
                }
            }
            #endregion
    
    
            #region Private Methods
            /// 
            /// Creates a new AppDomain based on the parent AppDomains 
            /// Evidence and AppDomainSetup
            /// 
            /// The parent AppDomain
            /// A newly created AppDomain
            private AppDomain BuildChildDomain(AppDomain parentDomain)
            {
                Evidence evidence = new Evidence(parentDomain.Evidence);
                AppDomainSetup setup = parentDomain.SetupInformation;
                return AppDomain.CreateDomain("DiscoveryRegion", evidence, setup);
            }
            #endregion
    
    
    
    
    
            /// 
            /// Remotable AssemblyLoader, this class 
            /// inherits from MarshalByRefObject 
            /// to allow the CLR to marshall
            /// this object by reference across 
            /// AppDomain boundaries
            /// 
            class AssemblyLoader : MarshalByRefObject
            {
                #region Private/Internal Methods
                /// 
                /// ReflectionOnlyLoad of single Assembly based on 
                /// the assemblyPath parameter
                /// 
                /// The path to the Assembly
                [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance","CA1822:MarkMembersAsStatic")]
                internal void LoadAssemblies(FileInfo assemblyLocations, string dllName, string process, string passkey)
                {
                    crmCORE oCoreClasses = new crmCORE();
                    try
                    {
                       
                        Assembly.ReflectionOnlyLoadFrom(assemblyLocations.FullName);
    
                    }
                    catch (FileNotFoundException e)
                    {
                        string a = e.ToString();
                        
                    }
                }
                #endregion
            }
        }







    Tuesday, February 21, 2012 7:59 AM

Answers

  • 1. Create a ClassLibrary, add a WinForm item to the classlibrary project:

    namespace ClassLibrary2
    {
        public class Class1
        {
            public Class1()
            {
                    Form1 form = new Form1();
                    form.ShowDialog();          
            }
        }
    }

    2. Create a new Appdomain to show the form.

    static void Main(string[] args)
            {
                AppDomain newAppDomian = AppDomain.CreateDomain("new AppDomain");
    
                Assembly asm = newAppDomian.Load(@"ClassLibrary2");
    
                var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1");
    
                AppDomain.Unload(newAppDomian);​
    
            }​


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us


    • Edited by Paul Zhou Wednesday, February 29, 2012 6:26 AM
    • Marked as answer by rhinestone89 Friday, March 02, 2012 3:37 AM
    Wednesday, February 29, 2012 6:25 AM
  • You can modify the code line:

    var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1");

    instead of:

    var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1",false,BindingFlags.CreateInstance|BindingFlags.Default, null,new object[] {"pass"  },null,null);


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us

    • Marked as answer by rhinestone89 Friday, March 02, 2012 3:37 AM
    Wednesday, February 29, 2012 8:21 AM
  • You are right. I found the problem and it's working now. I'm not sure how it happened but after modifying Paul Zhou's code to

     AppDomain newAppDomian = AppDomain.CreateDomain("new AppDomain");
    
                var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1");
    
                AppDomain.Unload(newAppDomian);​

    I can now remove the dll without restarting the program. Seems like the "Assembly asm = newAppDomain.Load" line was causing the the dll to load in the default AppDomain. But still I would like to give the credits to Paul Zhou for showing me this code 

    var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1",false,BindingFlags.CreateInstance|BindingFlags.Default, null,new object[] {"pass"  },null,null);
    Thanks everyone.



    • Marked as answer by rhinestone89 Friday, March 02, 2012 3:38 AM
    Friday, March 02, 2012 3:37 AM

All replies

  • Hi rihinestone89,

    You can consider posting it at the following more appropriate forum for more efficient responses. Thanks!

    http://social.msdn.microsoft.com/Forums/en-US/clr/threads


    Bob Shen [MSFT]
    MSDN Community Support | Feedback to us

    Wednesday, February 22, 2012 9:56 AM
  • 1. Create a ClassLibrary, add a WinForm item to the classlibrary project:

    namespace ClassLibrary2
    {
        public class Class1
        {
            public Class1()
            {
                    Form1 form = new Form1();
                    form.ShowDialog();          
            }
        }
    }

    2. Create a new Appdomain to show the form.

    static void Main(string[] args)
            {
                AppDomain newAppDomian = AppDomain.CreateDomain("new AppDomain");
    
                Assembly asm = newAppDomian.Load(@"ClassLibrary2");
    
                var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1");
    
                AppDomain.Unload(newAppDomian);​
    
            }​


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us


    • Edited by Paul Zhou Wednesday, February 29, 2012 6:26 AM
    • Marked as answer by rhinestone89 Friday, March 02, 2012 3:37 AM
    Wednesday, February 29, 2012 6:25 AM
  • Thank you for your reply Paul Zhou but I have question. How am I going to pass the method parameters? Like for example
    namespace ClassLibrary2
    {
        public class Class1
        {
            public Class1(string param1)
            {
                    Form1 form = new Form1();
                    form.ShowDialog();          
            }
        }
    }
    How am I going to pass class1's parameter with 
     AppDomain newAppDomian = AppDomain.CreateDomain("new AppDomain");
    
                Assembly asm = newAppDomian.Load(@"ClassLibrary2");
    
                var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1");
    
                AppDomain.Unload(newAppDomian);​
    Thanks in advance.
    Wednesday, February 29, 2012 6:48 AM
  • You can modify the code line:

    var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1");

    instead of:

    var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1",false,BindingFlags.CreateInstance|BindingFlags.Default, null,new object[] {"pass"  },null,null);


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us

    • Marked as answer by rhinestone89 Friday, March 02, 2012 3:37 AM
    Wednesday, February 29, 2012 8:21 AM
  • Yes it worked but I have a problem. I am actually trying to create a module of a program. One feature of the program is to allow the user to add, update and remove the module. My problem is that when I ran the module and close it, I can't update the module not until I restart the program and update that module without running it first. Perhaps the reason is that when the module was started and the appdomain was unloaded, the program is still holding the the dll. Any idea? Actually this was my reason why I changed my method to the one I discussed above.


    • Edited by rhinestone89 Wednesday, February 29, 2012 9:59 AM
    Wednesday, February 29, 2012 9:59 AM
  • The only way I know how to reload an assembly is for that assembly to be in a different AppDomain. You can shutdown the existing AppDomain and when restarting it will pickup the new assembly.
    Wednesday, February 29, 2012 7:17 PM
  • What do you mean "allow the user to add, update and remove the module"? Do you mean that you are using Reflection Emit to modify IL to rebuild the module?

    I agree with Jared said. Actually, when an assembly is loaded, all types(methods etc.) in the assembly will be complied from IL to native CPU instructions by JITCompiler at runtime. If you want to modify the assembly or module, I think you have to load the module in a different appdomain from main AppDomain. You can not modify it when the module is loading because methods in the assembly has been compiled to native CPU instructions. So as Jared said, unload the existing AppDomain that loads the assembly, then modify it and reload it.


    Paul Zhou [MSFT]
    MSDN Community Support | Feedback to us

    Thursday, March 01, 2012 2:19 AM
  • uhmm.. I'm not sure if that's the right way to describe what I'm trying to do. What I'm trying to say is that on the update part, I will replace the dll with the new dll(with the same dll name). My problem is that when the dll is loaded using the new AppDomain, I'm unable to update it even if I already unloaded the new AppDomain. Perhaps it's not possible to delete or replace that dll? Please correct me if I'm wrong with my method. Thank you.

    By the way I did the method Paul Zhou told me and I'm sure the new AppDomain was unloaded because it passed through the break point I set up.




    Thursday, March 01, 2012 5:17 AM
  • I replace dll's all the time for a program that I use while the process is still running. I can't do it though when the program has an AppDomain that's loaded the dll.
    Thursday, March 01, 2012 5:45 AM
  • So that means I'm doing it wrong right? hmm.. If you don't mind, can you give me a sample code? I'm actually new with this so I don't really know what steps should be taken. Thank you.

    Thursday, March 01, 2012 5:52 AM
  • The only code to show is AppDomain.Unload(). The process creates a new AppDomain, has that AppDomain invoke a certain method, the execution of the method is what loads all of the assemblies through normal execution, and when the method is finished it unloads that AppDomain.

    I suspect that your primary AppDomain is also loading the module you're trying to replace. After you unload the AppDomain, break into the application with the debugger, open the Modules window, and see if the assembly is listed in the Modules window.

    Thursday, March 01, 2012 3:58 PM
  • You are right. I found the problem and it's working now. I'm not sure how it happened but after modifying Paul Zhou's code to

     AppDomain newAppDomian = AppDomain.CreateDomain("new AppDomain");
    
                var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1");
    
                AppDomain.Unload(newAppDomian);​

    I can now remove the dll without restarting the program. Seems like the "Assembly asm = newAppDomain.Load" line was causing the the dll to load in the default AppDomain. But still I would like to give the credits to Paul Zhou for showing me this code 

    var newInstance = newAppDomian.CreateInstance("ClassLibrary2", "ClassLibrary2.Class1",false,BindingFlags.CreateInstance|BindingFlags.Default, null,new object[] {"pass"  },null,null);
    Thanks everyone.



    • Marked as answer by rhinestone89 Friday, March 02, 2012 3:38 AM
    Friday, March 02, 2012 3:37 AM