How to Unload an Assembly Loaded Dynamically Using Reflection

A few days ago while developing an application I faced a very interesting issue, where I was unable to unload an assembly loaded dynamically using reflection. I am using Visual Studio 2008 and .Net 3.5. I searched the internet and somewhere I got the hint that I should load the assembly in another appdomain and create an instance there, use it and then unload the appdomain. I have done that but surprisingly even after that, the issue was not resolved and more surprisingly after the app domain was unloaded, when I tried to invoke the function from a loaded assembly, the function was called successfully. Now it's more annoying for me. I was hitting my head. After moving around a lot on the internet I found a way out of this.

Now here I want to give the solution for such issues and will tell you how to unload the assembly that has been loaded dynamically using reflection using appdomains, with the simple code snippets.

Major obstacles

If the developer simply creates a new app domain, as in,

AppDomain newDomain = AppDomain.CreateDomain("NewDomain");

Then create an instance and get an assembly object as in,

System.Runtime.Remoting.ObjectHandle obj = newDomain.CreateInstanceFrom({Path}, {Type});
object instance = obj.Unwrap();

Now when the developers use the new domain. The createIntance method returns a wrapped instance of the assembly from the new app domain. This not only loads the assembly into a new app domain but also loads the assembly into the current app domain. After that when the user calls the app domain. unload method unloads the app domain but the assembly that is loaded into the current app domain remains as it is.

Now there is a solution to this problem. The solution is creating the object in a new app domain only so that the scope of the object should be in the new app domain only and not let the object touch the current app domain.

To implement this we will be following the following steps: (please read carefully, it might be confusing for new developers)

  • Create a class say class "B" derived by MarshalByRefObject
  • Write a method LoadAssembly({AssemblyPath}) in class "B" that will take the assembly path as a parameter. This function will only load that assembly and return void.
  • Write another method ExecuteMethod({MethodName, Params}) in class "B". This method will take MethodName and Params as Parameters.
  • This will execute the method (name passed in the parameter) of the loaded assembly.
  • Create a new class that says class "A". This will be our main class from where we will start and execute the code.
  • In class A write a main method that will load class B in the new app domain, and then call class B methods to load the assembly by passing the loadassembly method by passing assembly path and then by calling ExecuteMethod by passing the method name and parameters.

Ok, friends enough theory, now let's get our hands on the code.

// Creating a new AppDomain
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;  
AppDomain newDomain = AppDomain.CreateDomain("newDomain", AppDomain.CurrentDomain.Evidence, setup);

// Create an instance of the loader class in the new AppDomain
System.Runtime.Remoting.ObjectHandle obj = newDomain.CreateInstance(
    typeof(LoadMyAssembly).Assembly.FullName, 
    typeof(LoadMyAssembly).FullName
);

LoadMyAssembly loader = (LoadMyAssembly)obj.Unwrap();
// As the object we are creating is from another AppDomain, we get that object in wrapped format, hence we unwrap it

// Call LoadAssembly method so that the assembly will be loaded into the new AppDomain
// and the object will also remain in the new AppDomain only.
loader.LoadAssembly(StrPath);

// Call ExecuteMethod and pass the name of the method from the assembly and the parameters.
loader.ExecuteStaticMethod(strModule, "MyMethod", new object[] { "girish", "girish" });

AppDomain.Unload(newDomain); 
// After the method has been executed, call the Unload method of the AppDomain.
// You have unloaded the new AppDomain and also unloaded the loaded assembly from memory.

Here we complete the code in our main class and nowhere is the LoadeMyAssembly class.

class LoadMyAssembly : MarshalByRefObject 
{
    private Assembly _assembly;
    System.Type MyType = null;
    object inst = null;

    public override object InitializeLifetimeService() 
    {
        return null;
    }

    public void LoadAssembly(string path) 
    {
        _assembly = Assembly.Load(AssemblyName.GetAssemblyName(path));
    }

    public object ExecuteStaticMethod(string strModule, string methodName, params object[] parameters) 
    {
        foreach (System.Type type in _assembly.GetTypes()) 
        {
            if (String.Compare(type.Name, "MyClass", true) == 0) 
            {
                MyType = type;
                inst = _assembly.CreateInstance(type.FullName);
                break;
            }
        }

        MethodInfo MyMethod = MyType.GetMethod(
            methodName, 
            new Type[] { typeof(int), typeof(string), typeof(string), typeof(string) }
        );
        MyMethod.Invoke(inst, BindingFlags.InvokeMethod, null, parameters, null);
        return null;
    }
}


Similar Articles