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;
}
}

João Alberto DuartePosted May 23, 2018, 9:28 AM
Tks for this article...it's work's perfect for me...
Luis Alberto Nores VillaverdePosted Oct 23, 2017, 1:22 AM
Thank you very much for the post , really help us a lot with this topic
Dan BenedekPosted Aug 29, 2011, 7:55 AM
if the assembly is a c++ one then it is also loaded in the CurrentDomain. Is there a solution to this?
Pradeep BelePosted Jun 9, 2011, 4:41 AM
hey man, you did such a nice and mind blowing work with this problem, you saved a lot of time for all of us, looking forward to see your new articles to help people like me, big big thanks for this great article.
MOhamed GhazalPosted Jun 9, 2011, 3:00 AM
really helped and saved a lot of time. Thanks for the help.
Dinesh BeniwalPosted Jun 1, 2011, 8:30 AM
Good work, welcome to the Mindcracker Community.