In-Depth

Unload Assemblies From an Application Domain

You don't want to lock needless assemblies into the VS.NET process. Avoid this problem by loading the assembly into a separate application domain that you can unload later.

Technology Toolbox: VB.NET

Level: Intermediate

The .NET Framework has introduced the concept of application domains, which act as lightweight processes. More than one application domain can coexist in the same OS process. A default application domain is created when the Common Language Runtime (CLR) is first loaded into a process. Your code generally runs within this default application domain (with the exception of ASP.NET-based applications, which recycle application domains).

The CLR loads assemblies into application domains transparently the first time a type contained in the assembly is referenced within a JIT-compiled piece of code. You also can force the CLR to load a specific assembly manually using calls such as Assembly.LoadFrom or Assembly.Load.

Note, however, that there is no corresponding call to unload, such as Assembly.Unload. Once an assembly has been loaded into an application domain, the assembly remains loaded (meaning the assembly file is locked on the disk as well) until the entire application domain is unloaded (calling the Unload static method on the AppDomain class):

AppDomain.Unload(myAppDomain)

Two objects cannot communicate directly if they run in two different application domains. Their communication must pass through .NET Remoting instead. As a consequence, classes that must be passed outside the application domain in which they were created must inherit from MarshalByRefObject.

You need to add a bit of code in a separate application domain when you need to unload the assembly once you've executed a specific task on it. You would need to do this, for instance, when developing VS.NET add-ins, in order to avoid locking your own assemblies into the VS.NET process (so you cannot rebuild them).

First, you create an application domain:

myAppDomain = _
   AppDomain.CreateDomain("mydomain")

Now you create an instance of the class you need using the AppDomain CreateInstanceAndUnwrap method. This method returns a proxy to the real object, which resides in the new application domain. The assembly where the class resides is loaded into the new application domain as well:

Dim obj As Assembly1.Class1 = DirectCast( _   
   myAppDomain.CreateInstanceAndUnwrap( _
   "Assembly1", _
   "Assembly1.Class1"), Assembly1.Class1)
' Call a method on the object
obj2.Method1()

Remember that Assembly1.Class1 must inherit from MarshalByRefObject or you'll get a runtime error.

Finally, unload the application domain:

AppDomain.Unload(myAppDomain)

Unfortunately, this code has one flaw that prevents Assembly1 from being unloaded. In fact, you won't be able to delete the assembly from disk after the call to AppDomain.Unload; you'll have to exit the application to do that.

Method1 has been executed correctly within the new application domain. However, if you take a look at the VS.NET output/debug window, you'll notice that Assembly1 has been loaded twice: once in the new domain and once in the default domain. How did that happen?

If you take a deeper look at the code, you'll notice that the Assembly1.Class1 type is referenced within the default application domain. Even if the Assembly1.Class1 is not created within the default domain, the CLR loads Assembly1 because one of its types is present in the calling code.

You have two distinct design options to avoid this problem. You can define an interface in a separate assembly and have the class implement it. The calling code will reference only the assembly containing the interface, so that Assembly1 won't be loaded into the default domain:

Dim obj As SharedAssembly1.IMyInterface = _
   DirectCast( _
   myAppDomain.CreateInstanceAndUnwrap( _
   "Assembly1", "Assembly1.Class1"), _
   SharedAssembly1.IMyInterface)

You also can define a gateway assembly that takes the responsibility of loading and executing assemblies that must be unloaded at a certain point. This second option requires a little more coding, but is more flexible and preferable to the first option, which is more straightforward but a bit intrusive, because it introduces some constraints in the application design.

Featured

comments powered by Disqus

Subscribe on YouTube