In the old days of Win32, process boundaries were used to isolate applications running on the same computer. Applications needed to be isolated because the address space was process-relative. A meaningful pointer in one process cannot be used in another process since the memory layout would be completely different.
The story for managed code is quite different. Before any managed code can be run, it must pass a verification process (unless the administrator has granted permission to skip this essential step.) Code that passes verification is called type-safe. Type safe code allows the CLR to provide an isolation boundary similar to Win32 processes, except at a much lower cost.
An application domain is a boundary that the CLR establishes around objects created within the same application scope. In other words, application domain provide a secure and versatile processing unit that the CLR can use to provide isolation between applications. An application process can have one or more application domains with each application domain running a separate application while still keeping the same level of isolation as Win32 processes. This ability to run multiple applications within a single process boundary increases server scalability.
The isolation provided by application domains has the following benefits:
Therefore, application domains can be defined as: logical entities that provide security, unloading, and isolation boundaries. 'Security boundary' because each domain can have its own security settings. 'Unloading boundary' because assemblies in each domain can be loaded/unloaded independent of other assemblies. 'Isolation boundary', because objects between application domains cannot be shared or called (except through .NET remoting).
An assembly must be loaded into an application domain before you can run the application. By default, the CLR loads an assembly into the application domain that contains code references it. In this way, an assembly code and data are isolated to the application using it. If an assembly's code is shared by many application domains within the same application, the assembly's (not its data) can be shared by all domains referencing the assembly. This reduces the amount of memory used at runtime. An assembly is said to be domain-neutral when its code can be shared by domains in the process
There are three options for loading domain-neutral assemblies:
Note that the performance of domain-neutral assemblies is slower if they contain static and global data that are accessed frequently. This is because each domain that accesses an assembly with static or global data must have its own copy of this data to prevent references to objects in static variable from crossing the domain boundary. As a result, the runtime contains additional logic to direct the caller to the appropriate copy of the static data or method.
As assembly is not shared between domain and hence is not domain-neutral when it is granted a different set of permissions for each domain. This can occur if the runtime host set an application domain-level security policy.
Application domains form an isolation, security, and unloading boundary for managed code. Note the following points about threads and application domains:
In general, application domains are created by a the host - a system application responsible for loading the CLR into a user process and executing code within an application domain.. Sometimes however, you may have to explicitly interact with application domains, especially if you create your own runtime host application, or if your application needs to work with additional application domains that are not automatically generated by the runtime. You can programmatically interact with application domains using the System.AppDomain class. The following examples illustrate:
You can create your own application domains and then load into them assemblies that you want to personally manage. When finished you can unload them. During the unloading process no new threads can access the application domain and all application domain-specific data structures are freed. During the unloading process, all assemblies loaded into the application domain are removed and are no longer available. However, if an assembly in the application domain is domain-neutral, the assembly's data remain in memory until the process shuts down (there is no mechanism to unload a domain-neutral assembly other than shutting down the process):
// Create a domain
AppDomain domain1 = System.AppDomain.CreateDomain( "MyTestDomain" );
// Output some info about the new domain
Trace.WriteLine( "Domain friendly name: " + domain1.FriendlyName );
Trace.WriteLine( "Current domain name: " + AppDomain.CurrentDomain.FriendlyName );
// Then unload the application domain
AppDomain.Unload( domain1 );
You can provide the CLR with configuration information for a new application domain using the AppDomainSetup class. When creating your own application domains, the most important property if ApplicationBase. ApplicationBase defines the root directory for the application, so that when the CLR needs to satisfy a type request, it will use ApplicationBase value to probe for the assembly containing the requested type:
private void button1_Click(object sender, System.EventArgs e)
{
// Setup new application domain with configuration information
System.AppDomainSetup domaininfo = new System.AppDomainSetup();
domaininfo.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
// Create a new application domain
AppDomain domain1 = AppDomain.CreateDomain( "MyTestAppDoman", null, domaininfo );
// Some traces ...
Trace.WriteLine( "New domain base directory: " + domain1.BaseDirectory );
// Unload the app domain
AppDomain.Unload( domain1 );
}
// Output
// Program startup
'DefaultDomain': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'AppDomains': Loaded 'C:\Projects\NET_Framework\Projects\AppDomains\bin\Debug\AppDomains.exe', Symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system.windows.forms\1.0.5000.0__b77a5c561934e089\system.windows.forms.dll', No symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system\1.0.5000.0__b77a5c561934e089\system.dll', No symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system.drawing\1.0.5000.0__b03f5f7f11d50a3a\system.drawing.dll', No symbols loaded.
// New domain loading and unloading
'Domain2': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system.xml\1.0.5000.0__b77a5c561934e089\system.xml.dll', No symbols loaded.
New domain base directory: C:\Projects\NET_Framework\Projects\AppDomains\bin\Debug\
The program '[3716] AppDomains.exe: MyTestAppDoman' has exited with code 0 (0x0).
// Process exit
The program '[3716] AppDomains.exe' has exited with code 0 (0x0).
There are two main approaches to load an assembly into an application domain using System.AppDomain and using System.Refelction.Assembly classes:
The follwoing code illustrates:
private void btn_LoadAssemblies_Click(object sender, System.EventArgs e)
{
// Setup new application domain with configuration information
System.AppDomainSetup domaininfo = new System.AppDomainSetup();
domaininfo.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
// Create a new application domain
AppDomain domain1 = AppDomain.CreateDomain( "MyTestAppDoman", null, domaininfo );
// Now load an assembly into the new app domain
System.Reflection.Assembly assembly = domain1.Load( @"C:\Projects\NET_Framework\Projects\AppDomains\bin\Debug\MyAssembly.dll");
// Get the MyAssemblyClass and instantiate it
System.Type typClass = assembly.GetType( "MyAssemblyClass" );
Object obClass = System.Activator.CreateInstance( typClass );
typClass.GetMethod("foo").Invoke( obClass, null );
// Unload the app domain
AppDomain.Unload( domain1 );
}