Customizing the AppDomain Creation Process

Last week, I posted about AppDomainManagers.  Today, I'm going to look a little more closely at how the AppDomainManager allows you to customize the domain creation process.  Specifically there are two methods that, when overridden, allow you to modify how an AppDomain is created:

  • CreateDomain - called whenever the CLR needs to create a new AppDomain (with the exception of the default domain).
  • InitializeNewDomain - first code executed in every new AppDomain (with the exception of the AppDomainManager constructor).

Lets look at these one at a time:

CreateDomain takes the friendly name of the AppDomain to create, the Evidence to supply to that AppDomain, and an AppDomainSetup instance describing the new domain.  All your CreateDomain method needs to do is take this information and provide the CLR with an AppDomain based on them.  Notice, that you don't have to provide the CLR with a new AppDomain, if you decide that you'd rather provide it with one that already exists.

If you do decide that you'd like to create a new AppDomain, the AppDomainManager class provides a helper method, CreateDomainHelper which will do all the work of creating a new AppDomain for you.

OK, so what good is CreateDomain?  Lets imagine a scenario where I write an application that hosts plugins from various places.  In order to protect my application from having a poorly-written plugin bring it down, I'd like to run the plugins in separate AppDomains.  However, if I'm expecting a lot of plugins, I might not want to create an AppDomain for each of them, since AppDomains can be expensive.  Instead, I've designed the application to use three AppDomains, one for plugins from the MyComputer zone, one for plugins from the LocalIntranet zone, and a third for plugins from the Internet zone.

In addition to this, I'll also adjust the application base to point to the plugins subdirectory by modifying the AppDomainSetup for the AppDomain.  Finally, the only AppDomain that I allow to create new domains is the default domain, so if any of the other AppDomains decide that they want to create a new domain, they're only going to get back the domain they're currently in.  I've placed this code in a ZoneSandbox AppDomainManager, which would look something like the code I've posted here.

As you can see, all of the above requirements were implemented in around 50 lines of source.  Basically, CreateDomain first checks to see if this is the default domain, if not, it just returns the current domain.  Then it sets the application base to be that of the currently executing application's plugins subdirectory.  Finally it loops over the evidence, and determines which zone the new domain should belong to defaulting to Internet.

The real work occurs in the switch statement at line 59.  At that point, we know which AppDomain we want to return, so we check our local variables to see if we've already created this domain.  If we have, just return it.  However, if we haven't we need to create it.  To create it, we pass off our own friendly name (disregarding the name that was requested), our modified evidence (if it contained no zone), and our modified AppDomainSetup to CreateDomainHelper.

Now that we've seen what CreateDomain can do, what about InitializeNewDomain?  Unlike CreateDomain, which has to, well, create an AppDomain, there are no restrictions as to what can be done with InitailizeNewDomain.

Like I mentioned above, InitializeNewDomain is the first code other than the AppDomainManager's constructor that will be run in every AppDomain.  Some things that you might want to do inside your InitializeNewDomain method?

  • Hook various AppDomain events
  • Initialize static state
  • Setup logging
  • etc

For instance, if I have a CrashReport class that logs all crash reports in an internal database, and sends an email to the developer with details, I might write an InitializeNewDomain method that looked like this:

using System;
using System.Reflection;

namespace AppDomainManagers
{
    public sealed class ReportFailureAppDomainManager : AppDomainManager
    {
        public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
        {
            AppDomain.CurrentDomain.UnhandledException += delegate(object sender,
                    UnhandledExceptionEventArgs e)
            {
                CrashReport.SendReport(e.ExceptionObject);
                return;
            });

            return;
        }
    }
}

This AppDomainManager will ensure that no matter how many AppDomains get created in the process, or who creates the AppDomains, any unhandled exception will always be sent in a report back to the developers.

Next up, looking at some of the other managers.