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.

comments powered by Disqus

Featured

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

  • What's New for Python, Java in Visual Studio Code

    Microsoft announced March 2024 updates to its Python and Java extensions for Visual Studio Code, the open source-based, cross-platform code editor that has repeatedly been named the No. 1 tool in major development surveys.

Subscribe on YouTube